D:\Com_CHM\CHM Prgm API\_API Win32 HELP us\Info\INFO_General.RTF

+$#A Legal Information
Information in this online help system is subject to change without notice and does not represent a commitment on the part of Microsoft Corporation. The software and/or files described in this online help system are furnished under a license agreement or nondisclosure agreement. The software and/or files may be used or copied only in accordance with the terms of the agreement. The purchaser may make one copy of the software for backup purposes. No part of this online help system may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or information and retrieval systems, for any purpose other than the purchaser's personal use, without the written permission of Microsoft Corporation.
Microsoft may have patents or pending patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property rights except as expressly provided in any written license agreement from Microsoft.
Copyright © 1992-1995 Microsoft Corporation. All rights reserved.
Microsoft, MS, MS-DOS, Win32, Win32s, and Windows are registered trademarks, and Windows NT is a trademark of Microsoft Corporation.
Portions of this documentation are provided under license from Digital Equipment Corporation. Copyright © 1990, 1992 Digital Equipment Corporation. All rights reserved.
DEC, DECnet and PATHWORKS are trademarks of Digital Equipment Corporation.

+$#AK Windows
A window in an application written for the Microsoft® Windows® operating system is a rectangular area of the screen where the application displays output and receives input from the user. A window shares the screen with other windows, including those from other applications. Only one window at a time can receive input from the user. The user can use the mouse, keyboard, or other input device to interact with this window and the application that owns it.
+$#A About Windows
Windows are the only means a Windows-based application has to interact with the user and accomplish tasks, so one of the first tasks of a Windows-based application is to create a window. This overview describes the elements of the Microsoft® Win32® application programming interface (API) that applications use to create and use windows; manage relationships between windows; and size, move, and display windows.
+$#A Desktop Window
When Windows starts, it automatically creates the desktop window. The desktop window is a system-defined window that paints the background of the screen and serves as the base for all windows displayed by all Win32-based applications.
The desktop window uses a bitmap stored in a bitmap file (with the .BMP filename extension) to paint the background of the screen. The pattern created by the bitmap is called the desktop wallpaper. By default, the desktop window uses the bitmap from a file specified in the registry as the desktop wallpaper.
A system configuration application, such as a Control Panel application, can change the desktop wallpaper by using the SystemParametersInfo_win32_SystemParametersInfo function with the wAction parameter set to SPI_SETDESKWALLPAPER and the lpvParam parameter specifying a bitmap filename. SystemParametersInfo then loads the bitmap from the specified file, uses the bitmap to paint the background of the screen, and enters the new filename in the registry. For more information about bitmaps, see Bitmaps_win32_Bitmaps. For more information about the registry, see Registry_win32_Registry.
+$#A Application Windows
Every Win32-based application creates at least one window, called the main window, that serves as the main window for the application. This window serves as the primary interface between the user and the application. Most applications also create many other windows, either directly or indirectly, to perform tasks related to the main window. Each window plays a part in displaying output and receiving input from the user.
+$#A Components of an Application Window
An application window can include a title bar, a menu bar, a System menu (also called the Control menu), a minimize box, a maximize box, a sizing border, a client area, a horizontal scroll bar, and a vertical scroll bar. An application's main window typically includes all of these components. The following illustration shows these components in a typical main window.
{ewc msdncd, EWGraphic, group10814 0 /a "SDK_02.bmp"}
The title bar displays an application-defined icon and line of text; typically, the text specifies the name of the application or indicates the purpose of the window. An application specifies the icon and text when creating the window. The title bar also makes it possible for the user to move the window by using a mouse or other pointing device.
Most applications include a menu that lists the commands supported by the application. Items in the menu bar represent the main categories of commands. Choosing an item from the menu bar typically opens a pop-up menu whose items correspond to the tasks within a given category. By selecting a command, the user directs the application to carry out a task.
The System-menu box is a bitmap that, when clicked, displays the System menu. The System menu is a menu created and managed by Windows. It contains a standard set of menu items that, when chosen by the user, set a window's size or position, close the application, or open the Task List. For more information about menus and the System menu, see Menus (Windows NT)_win32_Menus_Windows_NT_.
The maximize and minimize boxes are bitmaps that, when clicked, affect a window's size and position. When the user clicks the maximize box, Windows enlarges the window to the size of the screen and positions the window, so it covers the entire screen. At the same time, Windows replaces the maximize box with the restore box. The restore box is a bitmap that, when clicked, restores the window to its previous size and position.
When the user clicks the minimize box, Windows reduces the window to the size of an icon, positions the window at the bottom of the screen, and displays the window's icon at the position of the window. An icon is a 32- by 32-pixel bitmap that represents a window. For more information about icons, see Icons_win32_Icons.
The sizing border is an area around the perimeter of the window that enables the user to size the window by using a mouse or other pointing device.
The client area is the part of a window where the application displays output, such as text or graphics. For example, a desktop publishing application displays the current page of a document in the client area. The application must provide a function, called a window procedure, to process input to the window and display output in the client area. For more information about window procedures, see Window Procedures_win32_Window_Procedures.
The horizontal and vertical scroll bars convert mouse or keyboard input into values that an application uses to shift the contents of the client area either horizontally or vertically. For example, a word-processing application that displays a lengthy document typically provides a vertical scroll bar to enable the user to page up and down through the document.
The title bar, menu bar, System menu, minimize and maximize boxes, sizing border, and scroll bars are referred to collectively as the window's nonclient area. Windows manages most aspects of the nonclient area; the application manages everything else about the window. In particular, the application manages the appearance and behavior of the client area.
+$#A Controls, Dialog Boxes, and Message Boxes
An application uses several types of windows in addition to its main window, including controls, dialog boxes, and message boxes.
A control is a window that an application uses to obtain a specific piece of information from the user, such as the name of a file to open or the desired point size of a text selection. Applications also use controls to obtain information needed to control a particular feature of an application. For example, a word-processing application typically provides a control to let the user turn wordwrapping on and off.
Controls are always used in conjunction with another window ¾ typically, a dialog box. A dialog box is a window that contains one or more controls. An application uses a dialog box to prompt the user for input needed to complete a command. For example, an application that includes a command to open a file would display a dialog box that includes controls in which the user specifies a path and filename.
A message box is a window that displays a note, caution, or warning to the user. For example, a message box can inform the user of a problem the application has encountered while performing a task.
Dialog boxes and message boxes do not typically use the same set of window components as does a main window. Most have a title bar, a System menu, a border (nonsizing), and a client area, but they typically do not have a menu bar, minimize and maximize buttons, or scroll bars. For more information about dialog boxes and message boxes, see Dialog Boxes_win32_Dialog_Boxes.
+$#A Window Creation
An application creates its main window by using the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function and providing information Windows requires to define the attributes of the window. CreateWindowEx has a parameter, dwExStyle, that CreateWindow does not have; otherwise, the functions are identical. In fact, CreateWindow simply calls CreateWindowEx, setting the dwExStyle parameter to zero. For this reason, the remainder of this topic refers only to CreateWindowEx.
The Win32 API provides additional functions ¾ including DialogBox_win32_DialogBox, CreateDialog_win32_CreateDialog, and MessageBox_win32_MessageBox ¾ for creating special-purpose windows such as dialog boxes and message boxes. For more information about these functions, see Dialog Boxes_win32_Dialog_Boxes.
+$#A Window Attributes
An application must provide the following information when creating a window:
· Window class
· Window name
· Window style
· Parent or owner window
· Size
· Location
· Position
· Child-window identifier or menu handle
· Instance handle
· Creation data

These attributes are described in the following sections.
Window Class
Every window belongs to a window class. An application must register a window class before creating any windows of that class. The window class defines most aspects of a window's appearance and behavior. The chief component of a window class is the window procedure, a function that receives and processes all input and requests sent to the window. Windows provides the input and requests in the form of messages. For more information about window classes, window procedures, or messages, see Window Classes_win32_Window_Classes, Window Procedures_win32_Window_Procedures, or Messages and Message Queues_win32_Messages_and_Message_Queues.
Window Name
A window can have a name. A window name (also called window text) is a text string that identifies a window for the user. A main window, dialog box, or message box typically displays its window name in its title bar, if present. For a control, the appearance of the window name depends on the control's class. A button, edit control, or static control displays its window name within the rectangle occupied by the control. A list box, combo box, or static control does not display its window name.
An application uses the SetWindowText_win32_SetWindowText function to change the window name after creating the window. It uses the GetWindowTextLength_win32_GetWindowTextLength and GetWindowText_win32_GetWindowText functions to retrieve the current window-name text from a window.
Window Style
Every window has one or more window styles. A window style is a named constant that defines an aspect of the window's appearance and behavior that is not specified by the window's class. For example, the SCROLLBAR class creates a scroll bar control, but the SBS_HORZ and SBS_VERT styles determine whether a horizontal or vertical scroll bar control is created. A few window styles apply to all windows, but most apply to windows of specific window classes. Windows and, to some extent, the window procedure for the class, interpret the styles.
Parent or Owner Window
A window can have a parent window. A window that has a parent is called a child window. The parent window provides the coordinate system used for positioning a child window. Having a parent window affects aspects of a window's appearance; for example, a child window is clipped so that no part of the child window can appear outside the borders of its parent window. A window that has no parent, or whose parent is the desktop window, is called a top-level window. An application uses the EnumWindows_win32_EnumWindows function to obtain the handle of each of its top-level windows. EnumWindows passes the handle of each top-level window, in turn, to an application-defined callback function (a function called by Windows).
A window can own, or be owned by, another window. An owned window always appears in front of its owner window, is hidden when its owner window is minimized, and is destroyed when its owner window is destroyed.
Location, Size, and Position in the Z Order
Every window has a location, size, and position in the Z order. The location is the coordinates of the window's upper left corner, relative to the upper left corner of the screen or, in the case of a child window, the upper left corner of the parent's client area. A window's size is its width and height measured in pixels. A window's position in the Z order is the position of the window in a stack of overlapping windows.
Child-Window Identifier or Menu Handle
A child window can have a child-window identifier, a unique, application-defined value associated with the child window. Child-window identifiers are especially useful in applications that create multiple child windows. When creating a child window, an application specifies the identifier of the child window. After creating the window, the application can change the window's identifier by using the SetWindowLong_win32_SetWindowLong function, or it can retrieve the identifier by using the GetWindowLong_win32_GetWindowLong function.
Every window, except a child window, can have a menu. An application can include a menu by providing a menu handle either when registering the window's class or when creating the window.
Instance Handle
Every Win32-based application has an instance handle associated with it. Windows provides the instance handle to an application when the application starts. Because it can run multiple copies of the same application, Windows uses instance handles internally to distinguish one instance of an application from another. The application must specify the instance handle in many different windows, including those that create windows.
Creation Data
Every window can have application-defined creation data associated with it. When the window is first created, Windows passes a pointer to the data on to the window procedure of the window being created. The window procedure uses the data to initialize application-defined variables.
+$#A Main Window Creation
Every Win32-based application must have a WinMain_win32_WinMain function as its entry point. WinMain performs a number of tasks, including registering the window class for the main window and creating the main window. WinMain registers the main window class by calling the RegisterClass_win32_RegisterClass function, and it creates the main window by calling the CreateWindowEx_win32_CreateWindowEx function.

Portability Issue The entry point need not be named WinMain.

Windows does not automatically display the main window after creating it; instead, an application must use the ShowWindow_win32_ShowWindow function to display the main window. After creating the main window, the application's WinMain_win32_WinMain function calls ShowWindow, passing it two parameters: the handle of the main window and a flag specifying whether the main window should be minimized or maximized when it is first displayed. Normally, the flag can be set to any of the constants beginning with the SW_ prefix that are defined in the Win32 header files. However, when ShowWindow is called to display the application's main window, the flag must be set to SW_SHOWDEFAULT. This flag tells Windows to display the window as directed by the program that launched the application.
+$#A Window-Creation Messages
When creating any window, Windows sends messages to the window procedure for the window. Windows sends the WM_NCCREATE_win32_WM_NCCREATE message after creating the window's nonclient area and the WM_CREATE_win32_WM_CREATE message after creating the client area. The window procedure receives both messages before Windows displays the window. Both messages include a pointer to a CREATESTRUCT_win32_CREATESTRUCT_str structure that contains all the information specified in the CreateWindowEx_win32_CreateWindowEx function. Typically, the window procedure performs initialization tasks upon receiving these messages.
When creating a child window, Windows sends the WM_PARENTNOTIFY_win32_WM_PARENTNOTIFY message to the parent window after sending the WM_NCCREATE and WM_CREATE messages. It also sends other messages while creating a window. The number and order of these messages depend on the window class and style and on the function used to create the window. These messages are described in other topics in this help file.
+$#A Multithread Applications
A Win32-based application can have multiple threads of execution, and each thread can create windows. An application can use the EnumThreadWindows_win32_EnumThreadWindows function to enumerate the windows created by a particular thread. This function passes the handle of each thread window, in turn, to an application-defined callback function. The GetWindowThreadProcessId_win32_GetWindowThreadProcessId function returns the identifier of the thread that created a particular window.
+$#A Window Handles
After creating a window, the creation function returns a window handle that uniquely identifies the window. An application uses this handle in other functions to direct their actions to the window. A window handle has the HWND data type; an application must use this type when declaring a variable that holds a window handle.
The Win32 API includes several special constants that can replace a window handle in certain functions. These constants begin with the HWND_ prefix. For example, an application can use HWND_TOP or HWND_BOTTOM in the SetWindowPos_win32_SetWindowPos function to move a window to the top or bottom of the Z order.
Although the NULL constant is not a window handle, an application can use it in some functions to specify that no window is affected. For example, specifying NULL for the CreateWindowEx_win32_CreateWindowEx function's hwndParent parameter creates a window that has no parent or owner. Some functions may return NULL instead of a handle, indicating that the given action applies to no window.
An application can use the FindWindow_win32_FindWindow function to discover whether a window with the specified class name or window name exists in the system. If such a window exists, FindWindow returns the handle of the window. The IsWindow_win32_IsWindow function determines whether a window handle identifies a valid, existing window.
+$#A Window Styles
The Win32 API provides general window styles and class-specific window styles. The general windows styles are constants that begin with the WS_ prefix; they can be combined to form different types of windows, including main windows, dialog boxes, and child windows. The class-specific window styles define the appearance and behavior of windows belonging to the predefined control classes such as edit controls and list boxes. This section describes the general window styles.
An application usually sets a window's styles when creating the window. It can also set the styles after creating the window by using the SetWindowLong_win32_SetWindowLong function.
+$#A Overlapped Window
An overlapped window is a top-level window that has a title bar, border, and client area; it is meant to serve as an application's main window. It can also have a System menu, minimize and maximize boxes, and scroll bars. An overlapped window used as a main window typically includes all of these components.
By specifying the WS_OVERLAPPED or WS_OVERLAPPEDWINDOW style in the CreateWindowEx_win32_CreateWindowEx function, an application can create an overlapped window. If created with the WS_OVERLAPPED style, the window has a title bar and border. If created with the WS_OVERLAPPEDWINDOW style, the window has a title bar, sizing border, System menu, and minimize and maximize boxes.
+$#A Pop-up Window
A pop-up window is a special type of overlapped window used for dialog boxes, message boxes, and other temporary windows that appear outside an application's main window. Title bars are optional for pop-up windows; otherwise, pop-up windows are the same as overlapped windows of the WS_OVERLAPPED style.
An application creates a pop-up window by specifying the WS_POPUP style in CreateWindowEx_win32_CreateWindowEx. To include a title bar, the WS_CAPTION style must also be specified. An application can use the WS_POPUPWINDOW style to create a pop-up window that has a border and a System menu. The WS_CAPTION style must be combined with the WS_POPUPWINDOW style to make the System menu visible.
+$#A Child Window
A child window has the WS_CHILD style and is confined to the client area of its parent window. An application typically uses child windows to divide the client area of a parent window into functional areas. An application creates a child window by specifying the WS_CHILD style in the CreateWindowEx_win32_CreateWindowEx function.
A child window must have a parent window. The parent window can be an overlapped window, a pop-up window, or even another child window. An application specifies the parent window when calling CreateWindowEx. If the application specifies the WS_CHILD style in CreateWindowEx but does not specify a parent window, Windows does not create the window.
A child window has a client area but no other features, unless they are explicitly requested. An application can request a title bar, a System menu, minimize and maximize boxes, a border, and scroll bars for a child window, but a child window cannot have a menu. If the application specifies a menu handle, either when it registers the child's window class or creates the child window, the menu handle is ignored.
Positioning
Windows always positions the child window relative to the upper left corner of the parent window's client area. No part of a child window ever appears outside the borders of its parent window. If an application creates a child window that is larger than the parent window or positions a child window so that some or all of the child window extends beyond the borders of the parent, Windows clips the child window; that is, the portion outside the parent window's client area is not displayed. Actions that affect the parent window can also affect the child window, as follows.
Parent window Child window
Destroyed Destroyed before the parent window is destroyed.
Hidden Hidden before the parent window is hidden. A child window is visible only when the parent window is visible.
Moved Moved with the parent window's client area. The child window is responsible for painting its client area after the move.
Shown Shown after the parent window is shown.

Clipping
Windows does not automatically clip a child window from the parent window's client area. This means the parent window draws over the child window if it carries out any drawing in the same location as the child window. Windows does, however, clip the child window from the parent window's client area if the parent window has the WS_CLIPCHILDREN style. If the child window is clipped, the parent window cannot draw over it.
A child window can overlap other child windows in the same client area. A child window that shares the same parent window as one or more other child windows is called a sibling window. Sibling windows can draw in each other's client area, unless one of the child windows has the WS_CLIPSIBLINGS style. If a child window does have this style, any portion of its sibling window that lies within the child window is clipped.
If a window has either the WS_CLIPCHILDREN or WS_CLIPSIBLINGS style, a slight loss in performance occurs. Each window takes up system resources, so an application should not use child windows indiscriminately. For best performance, an application that needs to logically divide its main window should do so in the window procedure of the main window rather than by using child windows.
Relationship to Parent Window
An application can change the parent window of an existing child window by calling the SetParent_win32_SetParent function. In this case, Windows removes the child window from the client area of the old parent window and moves it to the client area of the new parent window. If SetParent specifies a NULL handle, the desktop window becomes the new parent window. In this case, the child window is drawn on the desktop, outside the borders of any other window. The GetParent_win32_GetParent function retrieves the handle of a child window's parent window.
The parent window relinquishes a portion of its client area to a child window, and the child window receives all input from this area. The window class need not be the same for each of the child windows of the parent window. This means that an application can fill a parent window with child windows that look different and carry out different tasks. For example, a dialog box can contain many types of controls, each one a child window that accepts different types of data from the user.
A child window has only one parent window, but a parent can have any number of child windows. Each child window, in turn, can have child windows. In this chain of windows, each child window is called a descendant window of the original parent window. An application uses the IsChild_win32_IsChild function to discover whether a given window is a child window or a descendant window of a given parent window.
The EnumChildWindows_win32_EnumChildWindows function enumerates the child windows of a parent window. Then, EnumChildWindows passes the handle of each child window to an application-defined callback function. Descendant windows of the given parent window are also enumerated.
Messages
Windows passes a child window's input messages directly to the child window; the messages are not passed through the parent window. The only exception is if the child window has been disabled by the EnableWindow_win32_EnableWindow function. In this case, Windows passes any input messages that would have gone to the child window to the parent window instead. This permits the parent window to examine the input messages and enable the child window, if necessary.
A child window can have a unique integer identifier. Child window identifiers are important when working with control windows. An application directs a control's activity by sending it messages. The application uses the control's child window identifier to direct the messages to the control. In addition, a control sends notification messages to its parent window. A notification message includes the control's child window identifier, which the parent uses to identify which control sent the message. An application specifies the child-window identifier for other types of child windows by setting the hmenu parameter of the CreateWindowEx_win32_CreateWindowEx function to a value rather than a menu handle.
+$#A Window Border
The Win32 API provides the following border styles.
Style Description
WS_BORDER Creates a window with a thin-line border.
WS_DLGFRAME Creates a window with a double border, a style typically used with dialog boxes. A window with this style cannot have a title bar.
WS_EX_DLGMODALFRAME Creates a window with a double border. Unlike the WS_DLGFRAME style, an application can also specify the WS_CAPTION style to create a title bar for the window.
WS_EX_STATICEDGE Creates a window with a three-dimensional border style intended to be used for items that do not accept user input.
WS_THICKFRAME Creates a window with a sizing border.

A window with the WS_OVERLAPPED or WS_POPUPWINDOW style has the WS_BORDER style by default. One of the other border styles must be combined with the WS_OVERLAPPED or WS_POPUPWINDOW style to give an overlapped window a different border style.
If no border style is specified for a window with the WS_POPUP or WS_CHILD style, the system creates a borderless window. An application can use borderless child windows to divide the parent window's client area while keeping the divisions invisible to the user.
+$#A Nonclient-Area Components
The nonclient area of a window can include a title bar, System menu, minimize and maximize boxes, sizing border, and horizontal and vertical scroll bars. An application can create a window with one or more of these components by specifying the following styles in the CreateWindowEx_win32_CreateWindowEx function.
Style Description
WS_CAPTION Creates a window that has a title bar (includes the WS_BORDER style).
WS_HSCROLL Creates a window that has a horizontal scroll bar.
WS_MAXIMIZEBOX Creates a window that has a maximize box.
WS_MINIMIZEBOX Creates a window that has a minimize box.
WS_SYSMENU Creates a window that has a System-menu box in its title bar. The WS_CAPTION style must also be specified.
WS_VSCROLL Creates a window that has a vertical scroll bar.

+$#A Initial State
The following styles determine whether a window is enabled or disabled, visible or invisible, and minimized or maximized.
Style Description
WS_DISABLED Creates a window that is initially disabled. A disabled window cannot receive input from the user.
WS_MAXIMIZE Creates a window that is initially maximized.
WS_MINIMIZE Creates a window that is initially minimized.
WS_VISIBLE Creates a window that is initially visible.

+$#A Parent and Child Styles
The following styles affect the clipping relationship between a parent window and its child windows, and between a child window and its sibling windows.
Style Description
WS_CLIPCHILDREN Excludes the area occupied by child windows when drawing within the parent window. Use this style when creating the parent window.
WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT_win32_WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of one child window, to draw within the client area of another neighboring child window.

+$#A Dialog Box Styles
A dialog box includes a built-in keyboard interface that processes the direction and TAB keys. The user can use these keys instead of the mouse to navigate among the controls in the dialog box. The following styles affect how the built-in keyboard interface processes the direction and TAB keys.
Style Description
WS_GROUP Specifies the first control of a group of controls. The user can change the keyboard focus from one control in the group to the next by using the direction keys. The next control with the WS_GROUP style ends the group and starts the next group.
WS_TABSTOP Specifies a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control of the WS_TABSTOP style.

+$#A Extended Styles
The following styles can be specified in the dwExStyle parameter of the CreateWindowEx_win32_CreateWindowEx function.
Style Description
WS_EX_ACCEPTFILES Specifies that a window created with this style accepts drag-drop files.
WS_EX_CONTEXTHELP Includes a question mark in the title bar of the window. When the user clicks the question mark, the cursor changes to a question mark with a pointer. If the user then clicks a child window, the child receives a WM_HELP message. The child window should pass the message to the parent window procedure, which should call the WinHelp_win32_WinHelp function using the HELP_WM_HELP command. The Help application displays a pop-up window that typically contains help for the child window.
WS_EX_CONTROLPARENT Allows the user to navigate among the child windows of the window by using the TAB key.
WS_EX_DLGMODALFRAME Creates a window with a double border. Unlike the WS_DLGFRAME style, an application can also specify the WS_CAPTION style to create a title bar for the window.
WS_EX_NOPARENTNOTIFY Specifies that a child window created with this style will not send the WM_PARENTNOTIFY_win32_WM_PARENTNOTIFY message to its parent window when either created or destroyed.
WS_EX_TOPMOST Specifies that a window created with this style should be placed above all non-topmost windows and stay above them even when the window is deactivated.
WS_EX_TOOLWINDOW Creates a tool window; that is, a window intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the task bar or in the window that appears when the user presses ALT+TAB.

+$#A Owned Windows
An overlapped or pop-up window can be owned by another overlapped or pop-up window. Being owned places several constraints on a window.
· An owned window is always above its owner in the Z order.
· Windows automatically destroys an owned window when its owner is destroyed.
· An owned window is hidden when its owner is minimized.

Only an overlapped or pop-up window can be an owner window; a child window cannot be one. An application creates an owned window by specifying the owner's window handle as the hwndParent parameter of CreateWindowEx_win32_CreateWindowEx when it creates a window with the WS_OVERLAPPED or WS_POPUP style. The hwndParent parameter must identify an overlapped or pop-up window. If hwndParent identifies a child window, Windows assigns ownership to the top-level parent window of the child window. After creating an owned window, an application cannot transfer ownership of the window to another window.
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
An application can use the GetWindow_win32_GetWindow function with the GW_OWNER flag to retrieve the handle of a window's owner.
+$#A Disabled Windows
A window can be disabled. A disabled window receives no keyboard or mouse input, but it can receive messages from other windows, from other applications, and from Windows. An application typically disables a window to prevent the user from using the window. For example, an application may disable a push button in a dialog box to prevent the user from choosing it. An application can enable a disabled window at any time; enabling a window restores normal input.
By default, a window is enabled when created. An application can specify the WS_DISABLED style, however, to disable a new window. An application enables or disables an existing window by using the EnableWindow_win32_EnableWindow function. Windows sends a WM_ENABLE_win32_WM_ENABLE message to a window when its enabled state is about to change. An application can determine whether a window is enabled by using the IsWindowEnabled_win32_IsWindowEnabled function.
When a child window is disabled, Windows passes the child's mouse input messages to the parent window. The parent uses the messages to determine whether to enable the child window. For more information about mouse input, see Mouse Input_win32_Mouse_Input.
Only one window at a time can receive keyboard input; that window is said to have the keyboard focus. If an application uses the EnableWindow function to disable a keyboard-focus window, the window loses the keyboard focus in addition to being disabled. EnableWindow then sets the keyboard focus to NULL, meaning no window has the focus. If a child window, or other descendant window, has the keyboard focus, the descendant window loses the focus when the parent window is disabled. For more information about the keyboard focus, see Keyboard Input_win32_Keyboard_Input.
+$#A Foreground and Background Windows
Each process can have multiple threads of execution, and each thread can create windows. The thread that created the window with which the user is currently working is called the foreground thread, and the window is called the foreground window. All other threads are background threads, and the windows created by background threads are called background windows.
Each thread has a priority level that determines the amount of CPU time the thread receives. Although an application can set the priority level of its threads, normally the foreground thread has a slightly higher priority level than the background threads. Because it has a higher priority, the foreground thread receives more CPU time than the background threads. The foreground thread has a normal base priority of 9; a background thread has a normal base priority of 7.
The user sets the foreground window by clicking a window, or by using the ALT+TAB or ALT+ESC key combination. An application sets the foreground window by using the SetForegroundWindow_win32_SetForegroundWindow function. If the new foreground window is a top-level window, Windows activates it; otherwise, it activates the associated top-level window. An application retrieves the handle of the foreground window by using the GetForegroundWindow_win32_GetForegroundWindow function.
+$#A Show State
At any one given time, a window may be active or inactive; hidden or visible; and minimized, maximized, or restored. These qualities are referred to collectively as the window's show state.
+$#A Active Window
An active window is the top-level window of the application with which the user is currently working. To allow the user to easily identify the active window, Windows places it at the top of the Z order and changes the color of its title bar and border to the system-defined active window colors. Only a top-level window can be an active window. When the user is working with a child window, Windows activates the top-level parent window associated with the child window.
Only one top-level window in the system is active at a time. The user activates a top-level window by clicking it (or one of its child windows), or by using the ALT+ESC or ALT+TAB key combination. An application activates a top-level window by calling the SetActiveWindow_win32_SetActiveWindow function. A number of other functions can cause Windows to activate a different top-level window, including SetWindowPos_win32_SetWindowPos, DeferWindowPos_win32_DeferWindowPos, SetWindowPlacement_win32_SetWindowPlacement, and DestroyWindow_win32_DestroyWindow. Although an application can activate a different top-level window at any time, to avoid confusing the user, it should do so only in response to a user action. An application uses the GetActiveWindow_win32_GetActiveWindow function to retrieve the handle of the active window.
When the activation changes from a top-level window of one application to the top-level window of another, Windows sends a WM_ACTIVATEAPP_win32_WM_ACTIVATEAPP message to both applications, notifying them of the change. When the activation changes to a different top-level window in the same application, Windows sends both windows a WM_ACTIVATE_win32_WM_ACTIVATE message.
+$#A Visibility
A window can be either visible or hidden. Windows displays a visible window on the screen. It hides a hidden window by not drawing it. If a window is visible, the user can supply input to the window and view the window's output. If a window is hidden, it is effectively disabled. A hidden window can process messages from Windows or from other windows, but it cannot process input from the user or display output. An application sets a window's visibility state when creating the window. Later, the application can change the visibility state.
A window is visible when the WS_VISIBLE style is set for the window. By default, the CreateWindowEx_win32_CreateWindowEx function creates a hidden window unless the application specifies the WS_VISIBLE style. Typically, an application sets the WS_VISIBLE style after it has created a window to keep details of the creation process hidden from the user. For example, an application may keep a new window hidden while it customizes the window's appearance. If the WS_VISIBLE style is specified in CreateWindowEx, Windows sends the WM_SHOWWINDOW_win32_WM_SHOWWINDOW message to the window after creating the window, but before displaying it.
An application can determine whether a window is visible by using the IsWindowVisible_win32_IsWindowVisible function. An application can show (make visible) or hide a window by using the ShowWindow_win32_ShowWindow, SetWindowPos_win32_SetWindowPos, DeferWindowPos_win32_DeferWindowPos, or SetWindowPlacement_win32_SetWindowPlacement function. These functions show or hide a window by setting or removing the WS_VISIBLE style for the window. They also send the WM_SHOWWINDOW message to the window before showing or hiding it.
When an owner window is minimized, Windows automatically hides the associated owned windows. Similarly, when an owner window is restored, Windows automatically shows the associated owned windows. In both cases, Windows sends the WM_SHOWWINDOW message to the owned windows before hiding or showing them. Occasionally, an application may need to hide the owned windows without having to minimize or hide the owner. In this case, the application uses the ShowOwnedPopups_win32_ShowOwnedPopups function. This function sets or removes the WS_VISIBLE style for all owned windows and sends the WM_SHOWWINDOW message to the owned windows before hiding or showing them. Hiding an owner window has no effect on the visibility state of the owned windows.
When a parent window is visible, its associated child windows are also visible. Similarly, when the parent window is hidden, its child windows are also hidden. Minimizing the parent window has no effect on the visibility state of the child windows; that is, the child windows are minimized along with the parent, but the WS_VISIBLE style is not changed.
Even if a window has the WS_VISIBLE style, the user may not be able to see the window on the screen; other windows may completely overlap it or it may have been moved beyond the edge of the screen. Also, a visible child window is subject to the clipping rules established by its parent-child relationship. If the window's parent window is not visible, it will also not be visible. If the parent window moves beyond the edge of the screen, the child window also moves because a child window is drawn relative to the parent's upper left corner. For example, a user may move the parent window containing the child window far enough off the edge of the screen that the user may not be able to see the child window, even though the child window and its parent window both have the WS_VISIBLE style.
+$#A Minimized, Maximized, and Restored Windows
A maximized window is a window that has the WS_MAXIMIZE style. By default, Windows enlarges a maximized window so that it fills the screen or, in the case of a child window, the parent window's client area. Although a window's size can be set to the same size of a maximized window, a maximized window is slightly different. Windows automatically moves the window's title bar to the top of the screen or to the top of the parent window's client area. Also, Windows disables the window's sizing border and the window-positioning capability of the title bar (so that the user cannot move the window by dragging the title bar).
A minimized window is a window that has the WS_MINIMIZE style. By default, Windows reduces a minimized window to the size of an icon and moves the minimized window to the lower part of the screen or the lower part of the parent window's client area. The lower part of the screen or client area is sometimes called the icon area. Windows moves a minimized window into the first available position in the icon area, unless the application specifies another position. A restored window is a window that has been returned to its preminimized or premaximized size and position.
If an application specifies the WS_MAXIMIZE or WS_MINIMIZE style in the CreateWindowEx_win32_CreateWindowEx function, the window is initially maximized or minimized. After creating a window, an application can use the CloseWindow_win32_CloseWindow function to minimize the window. The ArrangeIconicWindows_win32_ArrangeIconicWindows function arranges the application's minimized top-level windows along the bottom of the screen, or it arranges a parent window's minimized child windows along the bottom of the parent window.
The ShowWindow_win32_ShowWindow function can minimize, maximize, or restore a window. It can also set the window's visibility and activation states. The SetWindowPlacement_win32_SetWindowPlacement function includes the same functionality as ShowWindow, but it can override the window's default minimized, maximized, and restored positions.
The IsZoomed_win32_IsZoomed and IsIconic_win32_IsIconic functions determine whether a given window is maximized or minimized, respectively. The GetWindowPlacement_win32_GetWindowPlacement function retrieves the minimized, maximized, and restored positions for the window, and also determines the window's show state.
When Windows receives a command to maximize or restore a minimized window, Windows sends the window a WM_QUERYOPEN_win32_WM_QUERYOPEN message. If the window procedure returns FALSE, Windows ignores the Maximize or Restore command.
Windows automatically sets the size and position of a maximized window to the system-defined defaults for a maximized window. To override these defaults, an application can either call the SetWindowPlacement function or process the WM_GETMINMAXINFO_win32_WM_GETMINMAXINFO message that is received by a window when Windows is about to maximize the window. WM_GETMINMAXINFO includes a pointer to a MINMAXINFO_win32_MINMAXINFO_str structure containing values Windows uses to set the maximized size and position. Replacing these values overrides the defaults.
+$#AK Window Size and Position
A window's size and position are expressed as a bounding rectangle, given in coordinates relative to the screen or the parent window. The coordinates of a top-level window are relative to the upper left corner of the screen; the coordinates of a child window are relative to the upper left corner of the parent window. An application specifies a window's initial size and position when it creates the window, but it can change the window's size and position at any time. For more information about bounding rectangles, see Filled Shapes_win32_Filled_Shapes.
+$#A Size
A window's size (width and height) is given in pixels. A window can have zero width or height. If an application sets a window's width and height to zero, Windows sets the size to the default minimum window size. To discover the default minimum window size, an application uses the GetSystemMetrics_win32_GetSystemMetrics function with the SM_CXMIN and SM_CYMIN flags.
An application may need to create a window with a client area of a particular size. The AdjustWindowRect_win32_AdjustWindowRect and AdjustWindowRectEx_win32_AdjustWindowRectEx functions calculate the required size of a window based on the desired size of the client area. The application can pass the resulting size values to the CreateWindowEx_win32_CreateWindowEx function.
An application can size a window so that it is extremely large; however, it should not size a window so that it is larger than the screen. Before setting a window's size, the application should check the width and height of the screen by using GetSystemMetrics with the SM_CXSCREEN and SM_CYSCREEN flags.
+$#A Position
A window's position is defined as the coordinates of its upper left corner. These coordinates, sometimes called window coordinates, are always relative to the upper left corner of the screen or, for a child window, the upper left corner of the parent window's client area. For example, a top-level window having the coordinates (10,10) is placed 10 pixels to the right of the upper left corner of the screen and 10 pixels down from it. A child window having the coordinates (10,10) is placed 10 pixels to the right of the upper left corner of its parent window's client area and 10 pixels down from it.
The WindowFromPoint_win32_WindowFromPoint function retrieves the handle of the window occupying a particular point on the screen. Similarly, the ChildWindowFromPoint_win32_ChildWindowFromPoint and ChildWindowFromPointEx_win32_ChildWindowFromPointEx functions retrieve the handle of the child window occupying a particular point in the parent window's client area. Although ChildWindowFromPointEx can ignore invisible, disabled, and transparent child windows, ChildWindowFromPoint cannot.
+$#A Default Size and Position
An application can allow Windows to calculate the initial size or position of a top-level window by specifying CW_USEDEFAULT in CreateWindowEx_win32_CreateWindowEx. If the application sets the window's coordinates to CW_USEDEFAULT and has created no other top-level windows, Windows sets the new window's position relative to the upper left corner of the screen; otherwise, it sets the position relative to the position of the top-level window that the application created most recently. If the width and height parameters are set to CW_USEDEFAULT, Windows calculates the size of the new window. If the application has created other top-level windows, Windows bases the size of the new window on the size of the application's most recently created top-level window. Specifying CW_USEDEFAULT when creating a child or pop-up window causes Windows to set the window's size to the default minimum window size.
+$#A Tracking Size
Windows maintains a minimum and maximum tracking size for a window of the WS_THICKFRAME style; a window with this style has a sizing border. The minimum tracking size is the smallest window size the user can produce by dragging the window's sizing border. Similarly, the maximum tracking size is the largest window size the user can produce by dragging the sizing border.
A window's minimum and maximum tracking sizes are set to system-defined default values when Windows creates the window. An application can discover the defaults and override them by processing the WM_GETMINMAXINFO_win32_WM_GETMINMAXINFO message. For more information about this message, see Size and Position Messages_win32_Size_and_Position_Messages.
+$#A System Commands
An application that has a window with a System menu can change the size and position of that window by sending system commands. System commands are generated when the user chooses commands from the System menu. An application can emulate the user action by sending a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message to the window. The following system commands affect the size and position of a window.
Command Description
SC_CLOSE Closes the window. This command sends a WM_CLOSE_win32_WM_CLOSE message to the window. The window carries out any steps needed to clean up and destroy itself.
SC_MAXIMIZE Maximizes the window.
SC_MINIMIZE Minimizes the window.
SC_RESTORE Restores a minimized or maximized window to its previous size and position.
SC_SIZE Starts a Size command. The user can change the size of the window by using the mouse or keyboard.

+$#A Z Order
The Z order of a window indicates the window's position in a stack of overlapping windows. This window stack is oriented along an imaginary axis, the z-axis, extending outward from the screen. The window at the top of the Z order overlaps all other windows. The window at the bottom of the Z order is overlapped by all other windows. An application sets a window's position in the Z order by placing it behind a given window, or at the top or bottom of the stack.
Windows maintains three separate Z orders ¾ one for top-level windows, one for sibling windows, and one for topmost windows. A topmost window overlaps all other non-topmost windows, regardless of whether it is the active or foreground window. A topmost window has the WS_EX_TOPMOST style.
By default, Windows puts a window at the top of the Z order when creating the window. The user changes the Z order by activating a different window; Windows always positions the active window at the top of the Z order. An application uses the BringWindowToTop_win32_BringWindowToTop function to bring a window to the top of the Z order. It can rearrange the Z order by using SetWindowPos_win32_SetWindowPos or DeferWindowPos_win32_DeferWindowPos.
+$#A Size and Position Functions
After creating a window, an application can set the window's size or position by calling one of several different functions, including SetWindowPlacement_win32_SetWindowPlacement, MoveWindow_win32_MoveWindow, SetWindowPos_win32_SetWindowPos, and DeferWindowPos_win32_DeferWindowPos. SetWindowPlacement sets a window's minimized position, maximized position, restored size and position, and show state. The MoveWindow and SetWindowPos functions are similar; both set the size or position of a single application window. The SetWindowPos function includes a set of flags that affect the window's show state; MoveWindow does not include these flags. Use the BeginDeferWindowPos_win32_BeginDeferWindowPos, DeferWindowPos, and EndDeferWindowPos_win32_EndDeferWindowPos functions to simultaneously set the position of a number of windows, including the size, position, position in the Z order, and show state.
An application can retrieve the coordinates of a window's bounding rectangle by using the GetWindowRect_win32_GetWindowRect function. GetWindowRect fills a RECT_win32_RECT_str structure with the coordinates of the window's upper left and lower right corners. The coordinates are relative to the upper left corner of the screen, even for a child window. The ScreenToClient_win32_ScreenToClient or MapWindowPoints_win32_MapWindowPoints function maps the screen coordinates of a child window's bounding rectangle to coordinates relative to the parent window's client area.
The GetClientRect_win32_GetClientRect function retrieves the coordinates of a window's client area. GetClientRect fills a RECT structure with the coordinates of the upper left and lower right corners of the client area, but the coordinates are relative to the client area itself. This means the coordinates of a client area's upper left corner are always (0,0), and the coordinates of the lower right corner are the width and height of the client area.
+$#A Size and Position Messages
Windows sends the WM_GETMINMAXINFO_win32_WM_GETMINMAXINFO message to a window whose size or position is about to change. For example, the message is sent when the user chooses the Move or Size command from the System menu or clicks the sizing border or title bar; the message is also sent when an application calls SetWindowPos_win32_SetWindowPos to move or size the window. WM_GETMINMAXINFO includes a pointer to a MINMAXINFO_win32_MINMAXINFO_str structure containing the default maximized size and position for the window, as well as the default minimum and maximum tracking sizes. An application can override the defaults by processing WM_GETMINMAXINFO and setting the appropriate members of MINMAXINFO. A window must have the WS_THICKFRAME or WS_CAPTION style to receive WM_GETMINMAXINFO. A window with the WS_THICKFRAME style receives this message during the window-creation process, as well as when it is being moved or sized.
Windows sends the WM_WINDOWPOSCHANGING_win32_WM_WINDOWPOSCHANGING message to a window whose size, position, position in the Z order, or show state is about to change. This message includes a pointer to a WINDOWPOS_win32_WINDOWPOS_str structure that specifies the window's new size, position, position in the Z order, and show state. By setting the members of WINDOWPOS, an application can affect the window's new size, position, and appearance.
After changing a window's size, position, position in the Z order, or show state, Windows sends the WM_WINDOWPOSCHANGED_win32_WM_WINDOWPOSCHANGED message to the window. This message includes a pointer to WINDOWPOS that informs the window of its new size, position, position in the Z order, and show state. Setting the members of the WINDOWPOS structure that is passed with WM_WINDOWPOSCHANGED has no effect on the window. A window that must process WM_SIZE_win32_WM_SIZE and WM_MOVE_win32_WM_MOVE messages must pass WM_WINDOWPOSCHANGED to the DefWindowProc_win32_DefWindowProc function; otherwise, Windows does not send WM_SIZE and WM_MOVE messages to the window.
Windows sends the WM_NCCALCSIZE_win32_WM_NCCALCSIZE message to a window when the window is created or sized. Windows uses the message to calculate the size of a window's client area and the position of the client area relative to the upper left corner of the window. A window typically passes this message to the default window procedure; however, this message can be useful in applications that customize a window's nonclient area or preserve portions of the client area when the window is sized. For more information about window size, see Painting and Drawing_win32_Painting_and_Drawing.
+$#A Window Destruction
In general, an application must destroy all the windows it creates. It does this by using the DestroyWindow_win32_DestroyWindow function. When a window is destroyed, the system hides the window, if it is visible, and then removes any internal data associated with the window. This invalidates the window handle, which can no longer be used by the application.
An application destroys many of the windows it creates soon after creating them. For example, an application usually destroys a dialog box window as soon as the application has sufficient input from the user to continue its task. An application eventually destroys the main window of the application (before terminating).
Before destroying a window, an application should save or remove any data associated with the window, and it should release any system resources allocated for the window. If the application does not release the resources, Windows will free any resources not freed by the application.
Destroying a window does not affect the window class from which the window is created. New windows can still be created using that class, and any existing windows of that class continue to operate. Destroying a window also destroys the window's descendant windows. The DestroyWindow function sends a WM_DESTROY_win32_WM_DESTROY message first to the window, then to its child windows and descendant window. In this way, all descendant windows of the window being destroyed are also destroyed.
A window with a System menu receives a WM_CLOSE_win32_WM_CLOSE message when the user chooses the Close command from the System menu. By processing this message, an application can prompt the user for confirmation before destroying the window. If the user confirms that the window should be destroyed, the application can call the DestroyWindow_win32_DestroyWindow function to destroy the window.
If the window being destroyed is the active window, both the active and focus states are transferred to another window. The window that becomes the active window is the next window, as determined by the ALT+ESC key combination. The new active window then determines which window receives the keyboard focus.
+$#A Using Windows
· Creating a main window_win32_Creating_a_Main_Window.
· Creating, enumerating, and sizing child windows_win32_Creating_Enumerating_and_Sizing_Child_Windows.
· Destroying a window_win32_Destroying_a_Window.

+$#A Creating a Main Window
The first window an application creates is typically the main window. You create the main window by using the CreateWindowEx_win32_CreateWindowEx function, specifying the window class, window name, window styles, size, position, menu handle, instance handle, and creation data. A main window belongs to an application-defined window class, so you must register the window class and provide a window procedure for the class before creating the main window.
Most applications typically use the WS_OVERLAPPEDWINDOW style to create the main window. This style gives the window a title bar, a System menu, a sizing border, and minimize and maximize boxes. The CreateWindowEx function returns a handle that uniquely identifies the window.
The following example creates a main window belonging to an application-defined window class. The window name, "Main Window", will appear in the window's title bar. By combining the WS_VSCROLL and WS_HSCROLL styles with the WS_OVERLAPPEDWINDOW style, the application creates a main window with horizontal and vertical scroll bars in addition to the components provided by the WS_OVERLAPPEDWINDOW style. The four occurrences of the CW_USEDEFAULT constant set the initial size and position of the window to the system-defined default values. By specifying NULL instead of a menu handle, the window will have the menu defined for the window class.
HINSTANCE hinst;
HWND hwndMain;

.
. /* Register the window class for the main window. */
.

/* Create the main window. */

hwndMain = CreateWindowEx(
0, /* no extended styles */
"MainWClass", /* class name */
"Main Window", /* window name */
WS_OVERLAPPEDWINDOW | /* overlapped window */
WS_HSCROLL | /* horizontal scroll bar */
WS_VSCROLL, /* vertical scroll bar */
CW_USEDEFAULT, /* default horizontal position */
CW_USEDEFAULT, /* default vertical position */
CW_USEDEFAULT, /* default width */
CW_USEDEFAULT, /* default height */
(HWND) NULL, /* no parent or owner window */
(HMENU) NULL, /* class menu used */
hinstance, /* instance handle */
NULL); /* no window creation data */

if (!hwndMain)
return FALSE;

/*
* Show the window using the flag specified by the program
* that launched the application, and send the application
* a WM_PAINT message.
*/

ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);

.
. /* Start the message loop for the application. */
.

Notice that the preceding example calls the ShowWindow_win32_ShowWindow function after creating the main window. This is done because Windows does not automatically display the main window after creating it. By passing the SW_SHOWDEFAULT flag to ShowWindow, the application allows the program that launched the application to set the initial show state of the main window. The UpdateWindow_win32_UpdateWindow function sends the window its first WM_PAINT_win32_WM_PAINT message.
+$#A Creating, Enumerating, and Sizing Child Windows
You can divide a window's client area into different functional areas by using child windows. Creating a child window is like creating a main window ¾ you use the CreateWindowEx_win32_CreateWindowEx function. To create a window of an application-defined window class, you must register the window class and provide a window procedure before creating the child window. You must give the child window the WS_CHILD style and specify a parent window for the child window when you create it.
The following example divides the client area of an application's main window into three functional areas by creating three child windows of equal size. Each child window is the same height as the main window's client area, but each is one-third its width. The main window creates the child windows in response to the WM_CREATE_win32_WM_CREATE message, which the main window receives during its own window-creation process. Because each child window has the WS_BORDER style, each has a thin line border. Also, because the WS_VISIBLE style is not specified, each child window is initially hidden. Notice also that each child window is assigned a child-window identifier.
The main window sizes and positions the child windows in response to the WM_SIZE_win32_WM_SIZE message, which the main window receives when its size changes. In response to WM_SIZE, the main window retrieves the dimensions of its client area by using the GetWindowRect_win32_GetWindowRect function and then passes the dimensions to the EnumChildWindows_win32_EnumChildWindows function. EnumChildWindows passes the handle of each child window, in turn, to the application-defined EnumChildProc_win32_EnumChildProc callback function. This function sizes and positions each child window by calling the MoveWindow_win32_MoveWindow function; the size and position are based on the dimensions of the main window's client area and the identifier of the child window. Afterward, EnumChildProc calls the ShowWindow_win32_ShowWindow function to make the window visible.
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102

LONG APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
UINT wParam;
LONG lParam;
{
RECT rcClient;
int i;

switch(uMsg) {
case WM_CREATE: /* creating main window */

/* Create three invisible child windows. */

for (i = 0; i < 3; i++)
CreateWindowEx(
0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0,0,0,0,
hwnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hinst,
NULL);

return 0;

case WM_SIZE: /* main window changed size */

/*
* Get the dimensions of the main window's client
* area, and enumerate the child windows. Pass the
* dimensions to the child windows during enumeration.
*/

GetClientRect(hwnd, &rcClient);
EnumChildWindows(hwnd, EnumChildProc,
(LPARAM) &rcClient);
return 0;
.
. /* Process other messages. */
.

}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL CALLBACK EnumChildProc(hwndChild, lParam)
HWND hwndChild;
LPARAM lParam;
{
LPRECT rcParent;
int i, idChild;

/*
* Retrieve the child-window identifier. Use it to set the
* position of the child window.
*/

idChild = GetWindowLong(hwndChild, GWL_ID);

if (idChild == ID_FIRSTCHILD)
i = 0;
else if (idChild == ID_SECONDCHILD)
i = 1;
else
i = 2;

/* Size and position the child window. */

rcParent = (LPRECT) lParam;
MoveWindow(hwndChild,
(rcParent->right / 3) * i,
0,
rcParent->right / 3,
rcParent->bottom,
TRUE);

/* Make sure the child window is visible. */

ShowWindow(hwndChild, SW_SHOW);

return TRUE;
}

+$#A Destroying a Window
You can use the DestroyWindow_win32_DestroyWindow function to destroy a window. Typically, an application sends the WM_CLOSE_win32_WM_CLOSE message before destroying a window, giving the window the opportunity to prompt the user for confirmation before the window is destroyed. A window that includes a System menu automatically receives the WM_CLOSE message when the user chooses the Close command from the menu. If the user confirms that the window should be destroyed, the application calls DestroyWindow. Windows sends the WM_DESTROY_win32_WM_DESTROY message to the window after removing it from the screen. In response to WM_DESTROY, the window saves its data and frees any resources it allocated. A main window concludes its processing of WM_DESTROY by calling the PostQuitMessage_win32_PostQuitMessage function to quit the application.
The following example shows how to prompt for user confirmation before destroying a window. In response to WM_CLOSE, the example displays a dialog box that contains Yes, OK, and Cancel buttons. If the user clicks the Yes button, DestroyWindow is called; otherwise, the window is not destroyed. Because the window being destroyed is a main window, the example calls PostQuitMessage in response to WM_DESTROY.
case WM_CLOSE:

/*
* Create the message box. If the user clicks
* the Yes button, destroy the main window.
*/

if (MessageBox(hwnd, szConfirm, szAppName, B_YESNOCANCEL) == IDYES)
DestroyWindow(hwndMain);
else
return 0;

case WM_DESTROY:

/*
* Post the WM_QUIT message to
* quit the application terminate.
*/

PostQuitMessage(0);
return 0;

+$#AK Window Reference
The following functions and messages are associated with windows.
+$#A Window Functions
Following are the functions to create and manage windows.
AdjustWindowRect_win32_AdjustWindowRectAdjustWindowRectEx_win32_AdjustWindowRectExAnyPopup_win32_AnyPopupBeginDeferWindowPos_win32_BeginDeferWindowPosBringWindowToTop_win32_BringWindowToTopChildWindowFromPoint_win32_ChildWindowFromPointCloseWindow_win32_CloseWindowCreateWindow_win32_CreateWindowCreateWindowEx_win32_CreateWindowExDeferWindowPos_win32_DeferWindowPosDestroyWindow_win32_DestroyWindowEnableWindow_win32_EnableWindowEndDeferWindowPos_win32_EndDeferWindowPosEnumChildProc_win32_EnumChildProcEnumChildWindows_win32_EnumChildWindowsEnumThreadWindows_win32_EnumThreadWindowsEnumThreadWndProc_win32_EnumThreadWndProcEnumWindows_win32_EnumWindowsEnumWindowsProc_win32_EnumWindowsProcFindWindow_win32_FindWindowGetClientRect_win32_GetClientRectGetDesktopWindow_win32_GetDesktopWindowGetForegroundWindow_win32_GetForegroundWindowGetLastActivePopup_win32_GetLastActivePopupGetNextWindow_win32_GetNextWindowGetParent_win32_GetParentGetTopWindow_win32_GetTopWindowGetWindow_win32_GetWindowGetWindowPlacement_win32_GetWindowPlacementGetWindowRect_win32_GetWindowRectGetWindowText_win32_GetWindowTextGetWindowTextLength_win32_GetWindowTextLengthGetWindowThreadProcessId_win32_GetWindowThreadProcessIdIsChild_win32_IsChildIsWindow_win32_IsWindowIsWindowUnicode_win32_IsWindowUnicodeIsWindowVisible_win32_IsWindowVisibleIsZoomed_win32_IsZoomedMoveWindow_win32_MoveWindowSetForegroundWindow_win32_SetForegroundWindowSetParent_win32_SetParentSetWindowLong_win32_SetWindowLongSetWindowPlacement_win32_SetWindowPlacementSetWindowPos_win32_SetWindowPosSetWindowText_win32_SetWindowTextShowOwnedPopups_win32_ShowOwnedPopupsShowWindow_win32_ShowWindowWindowFromPoint_win32_WindowFromPointWinMain_win32_WinMain

Obsolete or Deleted Functions
EnumTaskWindows_win32_EnumTaskWindowsGetSysModalWindow_win32_GetSysModalWindowGetWindowTask_win32_GetWindowTaskSetSysModalWindow_win32_SetSysModalWindow
+$#A Window Messages
Following are the messages to create and manage windows.
WM_ACTIVATE_win32_WM_ACTIVATEWM_ACTIVATEAPP_win32_WM_ACTIVATEAPPWM_CANCELMODE_win32_WM_CANCELMODEWM_CHILDACTIVATE_win32_WM_CHILDACTIVATEWM_CLOSE_win32_WM_CLOSEWM_COPYDATA_win32_WM_COPYDATAWM_CREATE_win32_WM_CREATEWM_DESTROY_win32_WM_DESTROYWM_ENABLE_win32_WM_ENABLEWM_ENDSESSION_win32_WM_ENDSESSIONWM_GETMINMAXINFO_win32_WM_GETMINMAXINFOWM_GETTEXT_win32_WM_GETTEXTWM_MOVE_win32_WM_MOVEWM_NCACTIVATE_win32_WM_NCACTIVATEWM_NCCALCSIZE_win32_WM_NCCALCSIZEWM_NCCREATE_win32_WM_NCCREATEWM_NCDESTROY_win32_WM_NCDESTROYWM_PARENTNOTIFY_win32_WM_PARENTNOTIFYWM_POWER_win32_WM_POWERWM_QUERYENDSESSION_win32_WM_QUERYENDSESSIONWM_QUERYOPEN_win32_WM_QUERYOPENWM_QUIT_win32_WM_QUITWM_SETTEXT_win32_WM_SETTEXTWM_SHOWWINDOW_win32_WM_SHOWWINDOWWM_SIZE_win32_WM_SIZEWM_WINDOWPOSCHANGED_win32_WM_WINDOWPOSCHANGEDWM_WINDOWPOSCHANGING_win32_WM_WINDOWPOSCHANGING

+$#AK Carets
A caret is a flashing line, block, or bitmap in the client area of a window. The caret typically indicates the place at which text or graphics will be inserted. The following illustration shows some common variations in the appearance of the caret.
{ewc msdncd, EWGraphic, group10795 0 /a "SDK_01.bmp"}
Because only one window at a time can have the keyboard focus or be active, there is only one caret in the system. Generally, each window that accepts keyboard input must create the caret when it receives the keyboard focus and destroy the caret when it loses the keyboard focus. For more information on keyboard input, see Keyboard Input_win32_Keyboard_Input.
An application written for Microsoft® Windows® can create a caret, display or hide it, relocate the caret, and change its blink time.
+$#A About Carets
An application uses the CreateCaret_win32_CreateCaret function to specify the parameters for a caret. Windows forms a caret by inverting the pixel color within the rectangle specified by the caret's position, width, and height. The width and height are specified in logical units; therefore, the appearance of a caret is subject to the window's mapping mode.
After the caret is defined, an application uses the ShowCaret_win32_ShowCaret function to make the caret visible. When the caret appears, it automatically begins flashing. To display a solid caret, Windows inverts every pixel in the rectangle; to display a gray caret, Windows inverts every other pixel; to display a bitmap caret, Windows inverts only the white bits of the bitmap.
The elapsed time, in milliseconds, required to invert the caret is called the blink time. An application can determine the caret's blink time by using the GetCaretBlinkTime_win32_GetCaretBlinkTime function. If it is necessary to define a specific blink time, use the SetCaretBlinkTime_win32_SetCaretBlinkTime function to set the rate of the blink time to a specified number of milliseconds. The flash time is the elapsed time, in milliseconds, required to display, invert, and restore the caret's display. The flash time of a caret is twice as much as the blink time.
An application can determine the caret's position by using the GetCaretPos_win32_GetCaretPos function. The position, in client coordinates, is copied to a POINT_win32_POINT_str structure specified by a parameter in GetCaretPos. An application can move a caret in a window by using the SetCaretPos_win32_SetCaretPos function. A window can move a caret only if it already owns the caret. SetCaretPos can move the caret whether it is visible or not.
+$#AK Removing a Caret
An application can use the HideCaret_win32_HideCaret function to remove the caret from the screen. This is useful when your application must redraw the screen while processing a message, but must keep the caret out of the way. When the application finishes drawing, it can display the caret again by using the ShowCaret_win32_ShowCaret function. Hiding the caret does not destroy its shape or invalidate the insertion point. Hiding the caret is cumulative; that is, if the application calls HideCaret five times, it must also call ShowCaret five times before the caret will reappear.
An application can remove the caret from the screen and destroy its shape by using the DestroyCaret_win32_DestroyCaret function. DestroyCaret destroys the caret only if the window involved in the current task owns the caret.
+$#A Using Carets
· Creating and displaying a caret_win32_Creating_and_Displaying_a_Caret
· Hiding a caret_win32_Hiding_a_Caret
· Destroying a caret_win32_Destroying_a_Caret
· Adjusting the blink time_win32_Adjusting_the_Blink_Time
· Processing keyboard input_win32_Processing_Keyboard_Input

+$#A Creating and Displaying a Caret
Upon receiving the keyboard focus, the window should create and display the caret. Use the CreateCaret_win32_CreateCaret function to create a caret in the given window. You can then call SetCaretPos_win32_SetCaretPos to set the current position of the caret and ShowCaret_win32_ShowCaret to make the caret visible.
The system sends the WM_SETFOCUS_win32_WM_SETFOCUS message to the window receiving keyboard focus; therefore, an application should create and display the caret while processing this message.
HWND hwnd, /* window handle */
int x; /* horizontal coordinate of cursor */
int y; /* vertical coordinate of cursor */
int nWidth; /* width of cursor */
int nHeight; /* height of cursor */
char *lpszChar; /* pointer to character */

case WM_SETFOCUS:

/* Create a solid black caret. */

CreateCaret(hwnd, (HBITMAP) NULL, nWidth, nHeight);

/* Adjust the caret position, in client coordinates. */

SetCaretPos(x, y);

/* Display the caret. */

ShowCaret(hwnd);

break;

To create a caret based on a bitmap, you must specify a bitmap handle when using CreateCaret_win32_CreateCaret. You can use a graphics application to create the bitmap and a resource compiler to add the bitmap to your application's resources. Your application can then use the LoadBitmap_win32_LoadBitmap function to load the bitmap handle. For example, you could replace the CreateCaret line in the preceding example with the following lines to create a bitmap caret.
/* Load the application-defined caret resource. */

hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));

/* Create a bitmap caret. */

CreateCaret(hwnd, hCaret, 0, 0);

Alternatively, you can use the CreateBitmap_win32_CreateBitmap or CreateDIBitmap_win32_CreateDIBitmap function to retrieve the handle of the caret bitmap. For more information about bitmaps, see Bitmaps_win32_Bitmaps.
If your application specifies a bitmap handle, CreateCaret_win32_CreateCaret ignores the width and height parameters. The bitmap defines the size of the caret.
+$#A Hiding a Caret
Whenever your application redraws a screen while processing a message other than WM_PAINT_win32_WM_PAINT, it must make the caret invisible by using the HideCaret_win32_HideCaret function. When your application is finished drawing, redisplay the caret by using the ShowCaret_win32_ShowCaret function. If your application processes the WM_PAINT message, it is not necessary to hide and redisplay the caret, because this function does this automatically.
The following code sample shows how to have your application hide the caret while drawing a character on the screen and while processing the WM_CHAR_win32_WM_CHAR message.
HWND hwnd, /* window handle */
HDC hdc; /* device context */

case WM_CHAR:

switch (wParam) {

case 0x08:

.
. /* Process a backspace. */
.

break;

case 0x09:

.
. /* Process a tab. */
.

break;

case 0x0D:

.
. /* Process a carriage return. */
.

break;

case 0x1B:

.
. /* Process an escape. */
.

break;

case 0x0A:

.
. /* Process a linefeed. */
.

break;

default:

/* Process all other characters. */

/* Hide the caret. */

HideCaret(hwnd);

/* Draw the character on the screen. */

hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, x, y, lpszChar, 1);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

}

If your application calls the HideCaret_win32_HideCaret function several times without calling ShowCaret_win32_ShowCaret, the caret will not be displayed until the application also calls ShowCaret the same number of times.
+$#A Destroying a Caret
When a window loses the keyboard focus, the system sends the WM_KILLFOCUS_win32_WM_KILLFOCUS message to the window. Your application should destroy the caret while processing this message by using the DestroyCaret_win32_DestroyCaret function. The following code shows how to destroy a caret in a window that no longer has the keyboard focus.
case WM_KILLFOCUS:

/*
* The window is losing the keyboard focus,
* so destroy the caret.
*/

DestroyCaret();

break;

+$#A Adjusting the Blink Time
Because there is only one caret in the system, use caution when your application must adjust the rate of the blink time, so as not to disrupt subsequent windows that may receive the keyboard focus. If your application changes the blink time, it must restore the blink time to its original value upon losing the keyboard focus. The GetCaretBlinkTime_win32_GetCaretBlinkTime function returns the current blink time, in milliseconds, and the SetCaretBlinkTime_win32_SetCaretBlinkTime function sets the blink time to the specified number of milliseconds.
If you anticipate that your application will adjust the blink time, add the following line to the WM_SETFOCUS_win32_WM_SETFOCUS message processing.
uOldBlink = GetCaretBlinkTime();

Then, you can restore the blink time to its original rate by including the following line in the WM_KILLFOCUS_win32_WM_KILLFOCUS message processing.
SetCaretBlinkTime(uOldBlink);

+$#A Processing Keyboard Input
The following example demonstrates how to use a caret in a simple text editor. The example updates the caret position as the user types printable characters and uses various keys to move through the client area.
#define TEXTMATRIX(x, y) \
*(pTextMatrix + (y * nWindowCharsX) + x)
HINSTANCE hinst; /* current instance */
HBITMAP hCaret; /* caret bitmap */
HDC hdc; /* device context */
PAINTSTRUCT ps; /* client area paint info */
static char *pTextMatrix = NULL; /* points to text matrix */
static int nCharX, /* width of char. in logical units */
nCharY, /* height of char. in logical units */
nWindowX, /* width of client area */
nWindowY, /* height of client area */
nWindowCharsX, /* width of client area */
nWindowCharsY, /* height of client area */
nCaretPosX, /* x-position of caret */
nCaretPosY; /* y-position of caret */
static UINT uOldBlink; /* previous blink rate */
int x, y; /* x and y coordinates to use in text matrix */
TEXTMETRIC tm; /* font information */

LONG APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{

switch (message) {

case WM_CREATE:

/*
* Select a fixed-width system font,
* and get the text metrics of the font.
*/

hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);

/* Save the avg. width and height of characters. */

nCharX = tm.tmAveCharWidth;
nCharY = tm.tmHeight;

return 0;

case WM_SIZE:

/*
* Determine the width of the client area, in pixels
* and in number of characters.
*/

nWindowX = LOWORD(lParam);
nWindowCharsX = max(1, nWindowX/nCharX);

/*
* Determine the height of the client area, in
* pixels and in number of characters.
*/

nWindowY = HIWORD(lParam);
nWindowCharsY = max(1, nWindowY/nCharY);

/* Clear the buffer that holds the text input. */

if (pTextMatrix != NULL) {
free(pTextMatrix);
}

/*
* If there is enough memory, allocate space for the
* text input buffer.
*/

pTextMatrix = malloc(nWindowCharsX * nWindowCharsY);

if (pTextMatrix == NULL) {
ErrorHandler("Not enough memory.");
}
else {
for (y = 0; y < nWindowCharsY; y++)
for (x = 0; x < nWindowCharsX; x++)
TEXTMATRIX(x, y) = ' ';
}

/* Move the caret to the origin. */

SetCaretPos(0, 0);

return 0;

case WM_KEYDOWN:
switch (wParam) {

case VK_HOME: /* Home */
nCaretPosX = 0;
break;

case VK_END: /* End */
nCaretPosX = nWindowCharsX - 1;
break;

case VK_PRIOR: /* Page Up */
nCaretPosY = 0;
break;

case VK_NEXT: /* Page Down */
nCaretPosY = nWindowCharsY -1;
break;

case VK_LEFT: /* Left arrow */
nCaretPosX = max(nCaretPosX - 1, 0);
break;

case VK_RIGHT: /* Right arrow */
nCaretPosX = min(nCaretPosX + 1,
nWindowCharsX - 1);
break;

case VK_UP: /* Up arrow */
nCaretPosY = max(nCaretPosY - 1, 0);
break;

case VK_DOWN: /* Down arrow */
nCaretPosY = min(nCaretPosY + 1,
nWindowCharsY - 1);
break;

case VK_DELETE: /* Delete */

/*
* Move all the characters that followed the
* deleted character (on the same line) one
* space back (to the left) in the matrix.
*/

for (x = nCaretPosX; x < nWindowCharsX; x++)
TEXTMATRIX(x, nCaretPosY) =
TEXTMATRIX(x + 1, nCaretPosY);

/*
* Replace the last character on the
* line with a space.
*/

TEXTMATRIX(nWindowCharsX - 1,
nCaretPosY) = ' ';

/*
* The application will draw outside the
* WM_PAINT message processing, so hide the
* caret.
*/

HideCaret(hwnd);

/*
* Redraw the line, adjusted for the
* deleted character.
*/

hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, nCaretPosX * nCharX,
nCaretPosY * nCharY,
&TEXTMATRIX(nCaretPosX, nCaretPosY),
nWindowCharsX - nCaretPosX);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

break;
}

/*
* Adjust the caret position based on the
* virtual-key processing.
*/

SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);

return 0;

case WM_CHAR:

switch (wParam) {

case 0x08: /* Backspace */

/*
* Move the caret back one space, and then
* process this like the DEL key.
*/

if (nCaretPosX > 0) {
nCaretPosX--;
SendMessage(hwnd, WM_KEYDOWN,
VK_DELETE, 1L);
}
break;

case 0x09: /* Tab */

/*
* Tab stops exist every four spaces, so add
* spaces until the user hits the next tab.
*/

do {
SendMessage(hwnd, WM_CHAR, ' ', 1L);
} while (nCaretPosX % 4 != 0);
break;

case 0x0D: /* Carriage return */

/*
* Go to the beginning of the next line.
* The bottom line wraps around to the top.
*/

nCaretPosX = 0;

if (++nCaretPosY == nWindowCharsY) {
nCaretPosY = 0;
}
break;

case 0x1B: /* Escape */
case 0x0A: /* Linefeed */

/* No processing. */

MessageBeep((UINT) -1);
break;

default: /* all other characters */

/* Add the character to the text buffer. */

TEXTMATRIX(nCaretPosX, nCaretPosY) =
(char) wParam;

/*
* The application will draw outside the
* WM_PAINT message processing, so hide the
* caret.
*/

HideCaret(hwnd);

/* Draw the character on the screen. */

hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, nCaretPosX * nCharX,
nCaretPosY * nCharY,
&TEXTMATRIX(nCaretPosX, nCaretPosY), 1);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

/*
* Prepare to wrap around if you reached the
* end of the line.
*/

if (++nCaretPosX == nWindowCharsX) {
nCaretPosX = 0;
if (++nCaretPosY == nWindowCharsY) {
nCaretPosY = 0;
}
}
break;
}

/*
* Adjust the caret position based on the
* character processing.
*/

SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);

return 0;

case WM_PAINT:

/*
* Draw all the characters in the buffer, line by
* line.
*/

hdc = BeginPaint(hwnd, &ps);

SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));

for (y = 0; y < nWindowCharsY; y++)
TextOut(hdc, 0, y * nCharY, &TEXTMATRIX(0, y),
nWindowCharsX);

EndPaint(hwnd, &ps);

case WM_SETFOCUS:

/*
* The window has the input focus. Load the
* application-defined caret resource.
*/

hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));

/* Create the caret. */

CreateCaret(hwnd, hCaret, 0, 0);

/* Adjust the caret position. */

SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);

/* Display the caret. */

ShowCaret(hwnd);

break;

case WM_KILLFOCUS:

/*
* The window is losing the input focus,
* so destroy the caret.
*/

DestroyCaret();

break;

default: /* passes it on if unprocessed */
return DefWindowProc(hwnd, message, wParam, lParam);

} /* ends switch (message) */

return NULL;
} /* ends main window procedure */

+$#A Caret Functions
The following functions are used with carets.
CreateCaret_win32_CreateCaretDestroyCaret_win32_DestroyCaretGetCaretBlinkTime_win32_GetCaretBlinkTimeGetCaretPos_win32_GetCaretPosHideCaret_win32_HideCaretSetCaretBlinkTime_win32_SetCaretBlinkTimeSetCaretPos_win32_SetCaretPosShowCaret_win32_ShowCaret

+$#AK Clipboard
The clipboard is a set of functions and messages that enable applications designed for the Microsoft® Win32® application programming interface (API) to transfer data. Because all applications have access to the clipboard, data can be easily transferred between applications or within an application.
This overview does not describe how to copy and paste linked or embedded objects. For information on these subjects, see the OLE Programmer's Reference in the OLE volume.
+$#A About the Clipboard
A memory object on the clipboard can be in any data format, called a clipboard format. Each format is identified by an unsigned integer value. For standard (predefined) clipboard formats, this value is a constant defined by the Win32 API; for registered clipboard formats, it is the return value of the RegisterClipboardFormat_win32_RegisterClipboardFormat function.
Except for registering clipboard formats, individual windows perform most clipboard operations. Typically, a window procedure transfers information to or from the clipboard in response to the WM_COMMAND_win32_WM_COMMAND message.
The clipboard is user-driven. A window should transfer data to or from the clipboard only in response to a command from the user. A window must not use the clipboard to transfer data without the user's knowledge.
+$#A Clipboard Formats
A window can place more than one object on the clipboard, each representing the same information in a different clipboard format. Users need not be aware of the clipboard formats used for an object on the clipboard.
+$#A Registered Clipboard Formats
Many applications work with data that cannot be translated into a standard clipboard format without loss of information. Such applications can register new clipboard formats by using the RegisterClipboardFormat_win32_RegisterClipboardFormat function. A clipboard format that is used by an application, as opposed to a standard clipboard format, is called a registered clipboard format. For example, if a word-processing application copied formatted text to the clipboard using a standard text format, the formatting information would be lost. The solution would be to register a new clipboard format, such as rich-text format (RTF).
If more than one application registers a clipboard format with exactly the same name, the clipboard format is registered only once. Both calls to the RegisterClipboardFormat function return the same value. In this way, two different applications can share data by using a registered clipboard format.
For a list of standard clipboard formats, see the SetClipboardData_win32_SetClipboardData function.
+$#A Private Clipboard Formats
An application can identify a private clipboard format by defining a value in the range CF_PRIVATEFIRST through CF_PRIVATELAST. An application can use a private clipboard format for an application-defined data format that does not need to be registered with the system.
Data handles associated with private clipboard formats are automatically freed by the system. Windows that use private clipboard formats can use the WM_DESTROYCLIPBOARD_win32_WM_DESTROYCLIPBOARD message to free any related resources that are no longer needed.
For more information about the WM_DESTROYCLIPBOARD message, see Clipboard Ownership_win32_Clipboard_Ownership.
An application can place data handles on the clipboard by defining a private format in the range CF_GDIOBJFIRST through CF_GDIOBJLAST. When using values in this range, the data handle is not a handle to a GDI object, but is a handle allocated by the GlobalAlloc_win32_GlobalAlloc function with the GMEM_DDESHARE and GMEM_MOVEABLE flags. When the clipboard is emptied the system automatically deletes the object using the GlobalFree_win32_GlobalFree function
+$#A Multiple Clipboard Formats
A window can place more than one clipboard object on the clipboard, each representing the same information in a different clipboard format. When placing information on the clipboard, the window should provide data in as many formats as possible.
Clipboard formats that contain the most information should be placed on the clipboard first, followed by less descriptive formats. A window pasting information from the clipboard typically retrieves a clipboard object in the first format it recognizes. Because clipboard formats are enumerated in the order they are placed on the clipboard, the first recognized format is also the most descriptive.
For example, suppose a user copies styled text from a word-processing document. The window containing the document might first place data on the clipboard in a registered format, such as RTF. Subsequently, the window would place data on the clipboard in a less descriptive format, such as text (CF_TEXT).
When the content of the clipboard is pasted into another window, the window retrieves data in the most descriptive format it recognizes. If the window recognizes RTF, the corresponding data is pasted into the document. Otherwise, the text data is pasted into the document and the formatting information is lost.
+$#A Synthesized Clipboard Formats
The system implicitly converts data between certain clipboard formats: if a window requests data in a format that is not on the clipboard, the system converts an available format to the requested format. The system can convert data between the text formats CF_UNICODETEXT, CF_TEXT, and CF_OEMTEXT; between the metafile formats CF_ENHMETAFILE and CF_METAFILEPICT; and between the bitmap formats CF_BITMAP and CF_DIB. The system can also convert CF_DIB to CF_PALETTE.
If the system provides a conversion between multiple formats, there is no advantage to placing multiple formats on the clipboard. When copying bitmaps, it is best to place only the CF_DIB format on the clipboard. This is because the colors in a device-dependent bitmap (CF_BITMAP) are relative to the system palette, which may change before the bitmap is pasted. If only the CF_DIB format is on the clipboard and a window requests the CF_BITMAP format, the system renders the device-dependent bitmap (DIB) using the current palette at that time.
If you place the CF_BITMAP format on the clipboard (and not CF_DIB), the system renders the CF_DIB clipboard format as soon as the clipboard is closed. This ensures that the correct palette is used to generate the DIB. Conversions between other clipboard formats occur upon demand.
+$#A Clipboard Operations
A window should use the clipboard when cutting, copying, or pasting data. A window places data on the clipboard for cut and copy operations and retrieves data from the clipboard for paste operations. The following sections describe these operations and related issues.
To place data on or retrieve data from the clipboard, a window must first open the clipboard by using the OpenClipboard_win32_OpenClipboard function. Only one window can have the clipboard open at a time. When it has finished, the window must close the clipboard by calling the CloseClipboard_win32_CloseClipboard function.
+$#A Cut and Copy Operations
To place information on the clipboard, a window first clears any previous clipboard content by using the EmptyClipboard_win32_EmptyClipboard function. This function sends the WM_DESTROYCLIPBOARD_win32_WM_DESTROYCLIPBOARD message to the previous clipboard owner, frees resources associated with data on the clipboard, and assigns clipboard ownership to the window that has the clipboard open.
After emptying the clipboard, a window places data on the clipboard in as many clipboard formats as possible. For each format, the window calls the SetClipboardData_win32_SetClipboardData function, specifying the format identifier and a global memory handle. The memory handle can be NULL, indicating that the window renders the data on request. (For more information about the rendering of data, see Delayed Rendering_win32_Delayed_Rendering.) A window should place data on the clipboard in order from the most descriptive clipboard format to the least descriptive.
+$#A Paste Operations
To retrieve paste information from the clipboard, a window first determines the clipboard format to retrieve. Typically, a window enumerates the available clipboard formats by using the EnumClipboardFormats_win32_EnumClipboardFormats function and uses the first format it recognizes. This method selects the best available format according to the priority set when the data was placed on the clipboard.
Alternatively, a window can use the GetPriorityClipboardFormat_win32_GetPriorityClipboardFormat function. This function identifies the best available clipboard format according to a specified priority. A window that recognizes only one clipboard format can simply determine whether that format is available by using the IsClipboardFormatAvailable_win32_IsClipboardFormatAvailable function.
After determining the clipboard format to use, a window calls the GetClipboardData_win32_GetClipboardData function. This function returns the handle of a global memory object containing data in the specified format. A window can briefly lock the memory object in order to examine or copy the data. However, a window should not free the object or leave it locked for a long period of time.
+$#A Clipboard Ownership
The clipboard owner is the window associated with the information on the clipboard. A window becomes the clipboard owner when it places data on the clipboard ¾ specifically, when it calls the EmptyClipboard_win32_EmptyClipboard function. The window remains the clipboard owner until it is closed or another window empties the clipboard.
When the clipboard is emptied, the clipboard owner receives a WM_DESTROYCLIPBOARD_win32_WM_DESTROYCLIPBOARD message. Following are some reasons why a window might process this message:
· The window delayed rendering of one or more clipboard formats. In response to the WM_DESTROYCLIPBOARD message, the window might free resources it had allocated in order to render data on request. For more information about the rendering of data, see Delayed Rendering_win32_Delayed_Rendering.
· The window placed data on the clipboard in a private clipboard format. The data for private clipboard formats is not freed by the system when the clipboard is emptied. Therefore, the clipboard owner should free the data upon receiving the WM_DESTROYCLIPBOARD message. For more information about private clipboard formats, see Clipboard Formats_win32_Clipboard_Formats.
· The window placed data on the clipboard using the CF_OWNERDISPLAY clipboard format. In response to the WM_DESTROYCLIPBOARD message, the window might free resources it had used to display information in the clipboard viewer window. For more information about this alternative format, see Owner-Display Format_win32_Owner_Display_Format.

+$#A Delayed Rendering
When placing a clipboard format on the clipboard, a window can delay rendering the data in that format until the data is needed. To do so, an application can specify NULL for the hData parameter of the SetClipboardData_win32_SetClipboardData function. This is useful if the application supports several clipboard formats, some or all of which are time-consuming to render. By passing a NULL handle, a window renders complex clipboard formats only when and if they are needed.
If a window delays rendering a clipboard format, it must be prepared to render the format upon request for as long as it is the clipboard owner. The system sends the clipboard owner a WM_RENDERFORMAT_win32_WM_RENDERFORMAT message when a request is received for a specific format that has not been rendered. Upon receiving this message, the window should call the SetClipboardData function to place a global memory handle on the clipboard in the requested format.
If the clipboard owner is destroyed and has delayed rendering some or all clipboard formats, it receives the WM_RENDERALLFORMATS_win32_WM_RENDERALLFORMATS message. Upon receiving this message, the window should place valid memory handles on the clipboard for all clipboard formats that it provides. This ensures that these formats remain available after the clipboard owner is destroyed.
An application should not open the clipboard before calling SetClipboardData_win32_SetClipboardData in response to the WM_RENDERFORMAT or WM_RENDERALLFORMATS message.
Any clipboard formats that are not rendered in response to the WM_RENDERALLFORMATS message cease to be available to other applications and are no longer enumerated by the clipboard functions.
+$#A Memory and the Clipboard
A memory object that is to be placed on the clipboard should be allocated by using the GlobalAlloc_win32_GlobalAlloc function with the GMEM_DDESHARE and GMEM_MOVEABLE flags.
Once a memory object is placed on the clipboard, ownership of that memory handle is transferred to the system. When the clipboard is emptied and the memory object has one of the following clipboard formats, the system frees the memory object by calling the indicated Win32 function:
Function called to free object Clipboard format
DeleteMetaFile_win32_DeleteMetaFile CF_DSPENHMETAFILE
CF_DSPMETAFILEPICT
CF_ENHMETAFILE
CF_METAFILEPICT
DeleteObject_win32_DeleteObject CF_BITMAP
CF_DSPBITMAP
CF_PALETTE
GlobalFree_win32_GlobalFree CF_DIB
CF_DSPTEXT
CF_OEMTEXT
CF_TEXT
CF_UNICODETEXT

When the clipboard is emptied of a memory object whose clipboard format is not shown in the preceding list, the application itself must free the memory object.
+$#A Clipboard Viewers
A clipboard viewer is a window that displays the current content of the clipboard. The clipboard viewer window is a convenience for the user and does not affect the data-transaction functions of the clipboard.
Typically, a clipboard viewer window can display at least the three most common formats: CF_TEXT, CF_BITMAP, and CF_METAFILEPICT. If a window does not make data available in any of these three formats, it should provide data in a display format or use the owner-display format.
A clipboard viewer chain is the linking together of two or more entities so that they are dependent upon one another for operation. This interdependency (chain) allows all running clipboard viewer applications to receive the messages sent to the current clipboard.
+$#A Clipboard Viewer Windows
A window adds itself to the clipboard viewer chain by calling the SetClipboardViewer_win32_SetClipboardViewer function. The return value is the handle of the next window in the chain. Each clipboard viewer window must keep track of the next window in the clipboard viewer chain. When the content of the clipboard changes, the system sends a WM_DRAWCLIPBOARD_win32_WM_DRAWCLIPBOARD message to the first window in the chain. After updating its display, each clipboard viewer window must pass this message on to the next window in the chain.
Before closing, a clipboard viewer window must remove itself from the clipboard viewer chain by calling the ChangeClipboardChain_win32_ChangeClipboardChain function. The system then sends a WM_CHANGECBCHAIN_win32_WM_CHANGECBCHAIN message to the first window in the chain.
For more information about processing the WM_DRAWCLIPBOARD and WM_CHANGECBCHAIN messages, see Creating a Clipboard Viewer Window_win32_Creating_a_Clipboard_Viewer_Window.
+$#A Display Formats
A display format is a clipboard format used to display information in a clipboard viewer window. A clipboard owner that uses a private or registered clipboard format, and none of the most common standard formats, must provide data in a display format for viewing in a clipboard viewer window. The display formats are intended for viewing only and must not be pasted into a document.
The four display formats are: CF_DSPBITMAP, CF_DSPMETAFILEPICT, CF_DSPTEXT, and CF_DSPENHMETAFILE. These display formats are rendered in the same way as the standard formats, which are: CF_BITMAP, CF_TEXT, CF_METAFILEPICT, and CF_ENHMETAFILE.
+$#A Owner-Display Format
For a clipboard owner that does not use any of the common standard clipboard formats, an alternative to providing a display format is to use the owner-display (CF_OWNERDISPLAY) clipboard format.
By using the owner-display format, a clipboard owner can avoid the overhead of rendering data in an additional format by taking direct control over painting the clipboard viewer window. The clipboard viewer window sends messages to the clipboard owner whenever a portion of the window must be repainted or when the window is scrolled or resized.
+$#A Clipboard Commands
A user typically carries out clipboard operations by choosing commands from an application's Edit menu. Following is a brief description of the standard clipboard commands.
Command Description
Cut Places a copy of the current selection on the clipboard and deletes the selection from the document. The previous content of the clipboard is destroyed.
Copy Places a copy of the current selection on the clipboard. The document remains unchanged. The previous content of the clipboard is destroyed.
Paste Replaces the current selection with the content of the clipboard. The content of the clipboard is not changed.
Delete Deletes the current selection from the document. The content of the clipboard is not changed. This command does not involve the clipboard, but it should appear with the clipboard commands on the Edit menu.

+$#A Using the Clipboard
· Implementing the Cut, Copy, and Paste commands_win32_Implementing_the_Cut_Copy_and_Paste_Commands
· Creating a clipboard viewer window_win32_Creating_a_Clipboard_Viewer_Window

+$#A Implementing the Cut, Copy, and Paste Commands
This section describes how standard Cut, Copy, and Paste commands are implemented in an application. The example in this section uses these methods to place data on the clipboard using a registered clipboard format, the CF_OWNERDISPLAY format, and the CF_TEXT format. The registered format is used to represent rectangular or elliptical text windows, called labels.
+$#A Selecting Data
Before information can be copied to the clipboard, the user must select specific information to be copied or cut. An application should provide a means for the user to select information within a document and some kind of visual feedback to indicate selected data.
+$#A Creating an Edit Menu
An application should load an accelerator table containing the standard keyboard accelerators for the Edit menu commands. The TranslateAccelerator_win32_TranslateAccelerator function must be added to the application's message loop for the accelerators to take effect. For more information about keyboard acceleratiors, see Keyboard Accelerators_win32_Keyboard_Accelerators.
+$#A Processing the WM_INITMENUPOPUP Message
Not all clipboard commands are available to the user at any given time. An application should process the WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP message to enable the menu items for available commands and disable unavailable commands.
Following is the WM_INITMENUPOPUP case for an application named Label.
case WM_INITMENUPOPUP: InitMenu((HMENU) wParam); break;
The InitMenu function is defined as follows.
void PASCAL InitMenu(HMENU hmenu)
{
int cMenuItems = GetMenuItemCount(hmenu);
int nPos;
UINT id;
UINT fuFlags;
PLABELBOX pbox = (hwndSelected == NULL) ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);

for (nPos = 0; nPos < cMenuItems; nPos++) {
id = GetMenuItemID(hmenu, nPos);

switch (id) {
case IDM_CUT:
case IDM_COPY:
case IDM_DELETE:
if (pbox == NULL || !pbox->fSelected)
fuFlags = MF_BYCOMMAND | MF_GRAYED;
else if (pbox->fEdit)
fuFlags = (id != IDM_DELETE && pbox->ichSel
== pbox->ichCaret) ?
MF_BYCOMMAND | MF_GRAYED :
MF_BYCOMMAND | MF_ENABLED;
else
fuFlags = MF_BYCOMMAND | MF_ENABLED;

EnableMenuItem(hmenu, id, fuFlags);
break;

case IDM_PASTE:
if (pbox != NULL && pbox->fEdit)
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(CF_TEXT) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);
else
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(
uLabelFormat) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);

}
}
}

+$#A Processing the WM_COMMAND Message
To process menu commands, add the WM_COMMAND_win32_WM_COMMAND case to your application's main window procedure. Following is the WM_COMMAND case for the Label application's window procedure.
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_CUT:
if (EditCopy())
EditDelete();
break;

case IDM_COPY:
EditCopy();
break;

case IDM_PASTE:
EditPaste();
break;

case IDM_DELETE:
EditDelete();
break;

case IDM_EXIT:
DestroyWindow(hwnd);
}
break;

To carry out the Copy and Cut commands, the window procedure calls the application-defined EditCopy function. For more information about the EditCopy function, see Copying Information to the Clipboard_win32_Copying_Information_to_the_Clipboard. To carry out the Paste command, the window procedure calls the application-defined EditPaste function. For more information about the EditPaste function, see Pasting Information from the Clipboard_win32_Pasting_Information_from_the_Clipboard.
+$#A Copying Information to the Clipboard
In the Label application, the application-defined EditCopy function copies the current selection to the clipboard. This function does the following:
1. Opens the clipboard by calling the OpenClipboard_win32_OpenClipboard function.
2. Empties the clipboard by calling the EmptyClipboard_win32_EmptyClipboard function.
3. Calls the SetClipboardData_win32_SetClipboardData function once for each clipboard format the application provides.
4. Closes the clipboard by calling the CloseClipboard_win32_CloseClipboard function.

Depending on the current selection, the EditCopy function either copies a range of text or copies an application-defined structure representing an entire label. The structure, called LABELBOX, is defined as follows.
#define BOX_ELLIPSE 0
#define BOX_RECT 1

#define CCH_MAXLABEL 80
#define CX_MARGIN 12

typedef struct tagLABELBOX { /* box */
RECT rcText; /* coordinates of rectangle containing text */
BOOL fSelected; /* TRUE if the label is selected */
BOOL fEdit; /* TRUE if text is selected */
int nType; /* rectangular or elliptical */
int ichCaret; /* caret position */
int ichSel; /* with ichCaret, delimits selection */
int nXCaret; /* window position corresponding to ichCaret */
int nXSel; /* window position corresponding to ichSel */
int cchLabel; /* length of text in atchLabel */
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;

Following is the EditCopy function.
BOOL PASCAL EditCopy(VOID)
{
PLABELBOX pbox;
LPTSTR lptstrCopy;
HGLOBAL hglbCopy;
int ich1, ich2, cch;

if (hwndSelected == NULL)
return FALSE;

/* Open the clipboard, and empty it. */

if (!OpenClipboard(hwndMain))
return FALSE;
EmptyClipboard();

/* Get a pointer to the structure for the selected label. */

pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);

/* If text is selected, copy it using the CF_TEXT format. */

if (pbox->fEdit) {
if (pbox->ichSel == pbox->ichCaret) { /* zero-length */
CloseClipboard(); /* selection */
return FALSE;
}

if (pbox->ichSel < pbox->ichCaret) {
ich1 = pbox->ichSel;
ich2 = pbox->ichCaret;
}
else {
ich1 = pbox->ichCaret;
ich2 = pbox->ichSel;
}
cch = ich2 - ich1;

/* Allocate a global memory object for the text. */

hglbCopy = GlobalAlloc(GMEM_DDESHARE,
(cch + 1) * sizeof(TCHAR));
if (hglbCopy == NULL) {
CloseClipboard();
return FALSE;
}

/* Lock the handle and copy the text to the buffer. */

lptstrCopy = GlobalLock(hglbCopy);
memcpy(lptstrCopy, &pbox->atchLabel[ich1],
cch * sizeof(TCHAR));
lptstrCopy[cch] = (TCHAR) 0; /* null character */
GlobalUnlock(hglbCopy);

/* Place the handle on the clipboard. */

SetClipboardData(CF_TEXT, hglbCopy);
}

/* If no text is selected, the label as a whole is copied. */

else {

/*
* Save a copy of the selected label as a local memory
* object. This copy is used to render data on request.
* It is freed in response to the WM_DESTROYCLIPBOARD
* message.
*/

pboxLocalClip = (PLABELBOX) LocalAlloc(
LMEM_FIXED,
sizeof(LABELBOX)
);
if (pboxLocalClip == NULL) {
CloseClipboard();
return FALSE;
}
memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));
pboxLocalClip->fSelected = FALSE;
pboxLocalClip->fEdit = FALSE;

/*
* Place a registered clipboard format, the owner-display
* format, and the CF_TEXT format on the clipboard using
* delayed rendering.
*/

SetClipboardData(uLabelFormat, NULL);
SetClipboardData(CF_OWNERDISPLAY, NULL);
SetClipboardData(CF_TEXT, NULL);
}

/* Close the clipboard. */

CloseClipboard();

return TRUE;
}

+$#A Pasting Information from the Clipboard
In the Label application, the application-defined EditPaste function pastes the content of the clipboard. This function does the following:
1. Opens the clipboard by calling the OpenClipboard_win32_OpenClipboard function.
2. Determines which of the available clipboard formats to retrieve.
3. Retrieves the handle of the data in the selected format by calling the GetClipboardData_win32_GetClipboardData function.
4. Inserts a copy of the data into the document.
The handle returned by GetClipboardData is still owned by the clipboard, so an application must not free it or leave it locked.
5. Closes the clipboard by calling the CloseClipboard_win32_CloseClipboard function.

If a label is selected and contains an insertion point, the EditPaste function inserts the text from the clipboard at the insertion point. If there is no selection or if a label is selected, the function creates a new label, using the application-defined LABELBOX structure on the clipboard. The LABELBOX structure is placed on the clipboard by using a registered clipboard format.
Following is the EditPaste function.
VOID PASCAL EditPaste(VOID)
{
PLABELBOX pbox;
HGLOBAL hglb;
LPTSTR lptstr;
PLABELBOX pboxCopy;
int cx, cy;
HWND hwnd;

pbox = hwndSelected == NULL ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);

/*
* If the application is in edit mode,
* get the clipboard text.
*/

if (pbox != NULL && pbox->fEdit) {
if (!IsClipboardFormatAvailable(CF_TEXT))
return;
if (!OpenClipboard(hwndMain))
return;

hglb = GetClipboardData(CF_TEXT);
if (hglb != NULL) {
lptstr = GlobalLock(hglb);
if (lptstr != NULL) {

/*
* Call the application-defined ReplaceSelection
* function to insert the text and repaint the
* window.
*/

ReplaceSelection(hwndSelected, pbox, lptstr);
GlobalUnlock(hglb);
}
}
CloseClipboard();

return;
}

/*
* If the application is not in edit mode,
* create a label window.
*/

if (!IsClipboardFormatAvailable(uLabelFormat))
return;
if (!OpenClipboard(hwndMain))
return;

hglb = GetClipboardData(uLabelFormat);
if (hglb != NULL) {
pboxCopy = GlobalLock(hglb);
if (pboxCopy != NULL) {
cx = pboxCopy->rcText.right + CX_MARGIN;
cy = pboxCopy->rcText.top * 2 + cyText;

hwnd = CreateWindowEx(
WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT,
atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy,
hwndMain, NULL, hinst, NULL
);
if (hwnd != NULL) {
pbox = (PLABELBOX) GetWindowLong(hwnd, 0);
memcpy(pbox, pboxCopy, sizeof(LABELBOX));
ShowWindow(hwnd, SW_SHOWNORMAL);
SetFocus(hwnd);
}
GlobalUnlock(hglb);
}
}
CloseClipboard();
}

+$#A Registering a Clipboard Format
To register a clipboard format, add a call to the RegisterClipboardFormat_win32_RegisterClipboardFormat function to your application's instance initialization function, as follows.
/* Register a clipboard format. */

LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,
sizeof(atchTemp));
uLabelFormat = RegisterClipboardFormat(atchTemp);
if (uLabelFormat == 0)
return FALSE;

+$# Processing the WM_RENDERFORMAT and WM_RENDERALLFORMATS Messages
If a window passes a NULL handle to the SetClipboardData_win32_SetClipboardData function, it must process the WM_RENDERFORMAT_win32_WM_RENDERFORMAT and WM_RENDERALLFORMATS_win32_WM_RENDERALLFORMATS messages to render data upon request.
If the WM_RENDERFORMAT message delayed rendering a specific format and an application requested data in that format, the message is sent to the clipboard owner. If the WM_RENDERFORMAT message has delayed rendering one or more formats, the message is sent to the clipboard owner before it is destroyed.
To render a clipboard format, the window procedure must place a data handle on the clipboard by using the SetClipboardData function. It must not open the clipboard before calling SetClipboardData.
The Label application processes the WM_RENDERFORMAT_win32_WM_RENDERFORMAT and WM_RENDERALLFORMATS_win32_WM_RENDERALLFORMATS messages as follows.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;

case WM_RENDERALLFORMATS:
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
break;

In both cases, the window procedure calls the application-defined RenderFormat function, defined as follows.
void PASCAL RenderFormat(UINT uFormat)
{
HGLOBAL hglb;
PLABELBOX pbox;
LPTSTR lptstr;
int cch;

if (pboxLocalClip == NULL)
return;

if (uFormat == CF_TEXT) {

/* Allocate a buffer for the text. */

cch = pboxLocalClip->cchLabel;
hglb = GlobalAlloc(GMEM_DDESHARE,
(cch + 1) * sizeof(TCHAR));
if (hglb == NULL)
return;

/* Copy the text from pboxLocalClip. */

lptstr = GlobalLock(hglb);
memcpy(lptstr, pboxLocalClip->atchLabel,
cch * sizeof(TCHAR));
lptstr[cch] = (TCHAR) 0;
GlobalUnlock(hglb);

/* Place the handle on the clipboard. */

SetClipboardData(CF_TEXT, hglb);
}
else if (uFormat == uLabelFormat) {
hglb = GlobalAlloc(GMEM_DDESHARE, sizeof(LABELBOX));
if (hglb == NULL)
return;
pbox = GlobalLock(hglb);
memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));
GlobalUnlock(hglb);

SetClipboardData(uLabelFormat, hglb);
}
}

+$#A Processing the WM_DESTROYCLIPBOARD Message
A window can process the WM_DESTROYCLIPBOARD_win32_WM_DESTROYCLIPBOARD message in order to free any resources that it set aside to support delayed rendering. For example the Label application, when copying a label to the clipboard, allocates a local memory object. It then frees this object in response to the WM_DESTROYCLIPBOARD message, as follows.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL) {
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;

+$#A Using the Owner-Display Clipboard Format
If a window places information on the clipboard by using the CF_OWNERDISPLAY clipboard format, it must do the following:
· Process the WM_PAINTCLIPBOARD_win32_WM_PAINTCLIPBOARD message. This message is sent to the clipboard owner when a portion of the clipboard viewer window must be repainted.
· Process the WM_SIZECLIPBOARD_win32_WM_SIZECLIPBOARD message. This message is sent to the clipboard owner when the clipboard viewer window has been resized or its content has changed.
Typically, a window responds to this message by setting the scroll positions and ranges for the clipboard viewer window. In response to this message, the Label application also updates a SIZE_win32_SIZE_str structure for the clipboard viewer window.
· Process the WM_HSCROLLCLIPBOARD_win32_WM_HSCROLLCLIPBOARD and WM_VSCROLLCLIPBOARD_win32_WM_VSCROLLCLIPBOARD messages. These messages are sent to the clipboard owner when a scroll bar event occurs in the clipboard viewer window.
· Process the WM_ASKCBFORMATNAME_win32_WM_ASKCBFORMATNAME message. The clipboard viewer window sends this message to an application to retrieve the name of the owner-display format.

The window procedure for the Label application processes these messages, as follows.
LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
static RECT rcViewer;

RECT rc;
LPRECT lprc;
LPPAINTSTRUCT lpps;

.
.
.

switch (msg) {
.
.
.

case WM_PAINTCLIPBOARD:

/* Determine the dimensions of the label. */

SetRect(&rc, 0, 0,
pboxLocalClip->rcText.right + CX_MARGIN,
pboxLocalClip->rcText.top * 2 + cyText
);

/* Center the image in the clipboard viewer window. */

if (rc.right < rcViewer.right) {
rc.left = (rcViewer.right - rc.right) / 2;
rc.right += rc.left;
}
if (rc.bottom < rcViewer.bottom) {
rc.top = (rcViewer.bottom - rc.bottom) / 2;
rc.bottom += rc.top;
}

/*
* Paint the image, using the specified PAINTSTRUCT
* structure, by calling the application-defined
* PaintLabel function.
*/

lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);
PaintLabel(lpps, pboxLocalClip, &rc);
GlobalUnlock((HGLOBAL) lParam);
break;

case WM_SIZECLIPBOARD:

/*
* Save the dimensions of the window in a static
* RECT structure.
*/

lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);
memcpy(&rcViewer, lprc, sizeof(RECT));
GlobalUnlock((HGLOBAL) lParam);

/*
* Set the scroll ranges to zero (thus eliminating
* the need to process the WM_HSCROLLCLIPBOARD and
* WM_VSCROLLCLIPBOARD messages).
*/

SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);
SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);

break;

case WM_ASKCBFORMATNAME:
LoadString(hinst, IDS_OWNERDISPLAY,
(LPSTR) lParam, wParam);
break;

.
.
.

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

+$#A Creating a Clipboard Viewer Window
A clipboard viewer window displays the current content of the clipboard, and receives messages when the clipboard content changes. To create a clipboard viewer window, your application must do the following:
· Add the window to the clipboard viewer chain.
· Process the WM_CHANGECBCHAIN_win32_WM_CHANGECBCHAIN message.
· Process the WM_DRAWCLIPBOARD_win32_WM_DRAWCLIPBOARD message.
· Remove the window from the clipboard viewer chain before it is destroyed.

+$#A Adding a Window to the Clipboard Viewer Chain
A window adds itself to the clipboard viewer chain by calling the SetClipboardViewer_win32_SetClipboardViewer function. The return value is the handle of the next window in the chain. A window must keep track of this value ¾ for example, by saving it in a static variable named hwndNextViewer.
The following example adds a window to the clipboard viewer chain in response to the WM_CREATE_win32_WM_CREATE message.
case WM_CREATE:

/* Add the window to the clipboard viewer chain. */

hwndNextViewer = SetClipboardViewer(hwnd);
break;

+$#A Processing the WM_CHANGECBCHAIN Message
A clipboard viewer window receives the WM_CHANGECBCHAIN_win32_WM_CHANGECBCHAIN message when another window is removing itself from the clipboard viewer chain. If the window being removed is the next window in the chain, the window receiving the message must unlink the next window from the chain. Otherwise, this message should be passed to the next window in the chain.
The following example shows the processing of the WM_CHANGECBCHAIN message.
case WM_CHANGECBCHAIN:

/* If the next window is closing, repair the chain. */

if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;

/* Otherwise, pass the message to the next link. */

else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);

break;

+$#A Removing a Window from the Clipboard Viewer Chain
To remove itself from the clipboard viewer chain, a window calls the ChangeClipboardChain_win32_ChangeClipboardChain function. The following example removes a window from the clipboard viewer chain in response to the WM_DESTROY_win32_WM_DESTROY message.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;

+$#A Processing the WM_DRAWCLIPBOARD Message
The WM_DRAWCLIPBOARD_win32_WM_DRAWCLIPBOARD message notifies a clipboard viewer window that the content of the clipboard has changed. A window should do the following when processing the WM_DRAWCLIPBOARD message:
1. Determine which of the available clipboard formats to display.
2. Retrieve the clipboard data and display it in the window. Or if the clipboard format is CF_OWNERDISPLAY, send a WM_PAINTCLIPBOARD_win32_WM_PAINTCLIPBOARD message to the clipboard owner.
3. Send the message to the next window in the clipboard viewer chain.

For an example of processing the WM_DRAWCLIPBOARD_win32_WM_DRAWCLIPBOARD message, see the example listing in Example of a Clipboard Viewer_win32_Example_of_a_Clipboard_Viewer.
+$#A Example of a Clipboard Viewer
The following example shows a simple clipboard viewer application.
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;

HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;

switch (uMsg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

/* Branch depending on the clipboard format. */

switch (uFormat) {
case CF_OWNERDISPLAY:
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_DDESHARE,
sizeof(PAINTSTRUCT));
lpps = GlobalLock(hglb);
memcpy(lpps, &ps, sizeof(PAINTSTRUCT));
GlobalUnlock(hglb);

SendMessage(hwndOwner, WM_PAINTCLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);

GlobalFree(hglb);
break;

case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL) {
if (OpenClipboard(hwnd)) {
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);

BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;

case CF_TEXT:
if (OpenClipboard(hwnd)) {
hglb = GetClipboardData(uFormat);
lpstr = GlobalLock(hglb);

GetClientRect(hwnd, &rc);
DrawText(hdc, lpstr, -1, &rc, DT_LEFT);

GlobalUnlock(hglb);
CloseClipboard();
}
break;

case CF_ENHMETAFILE:
if (OpenClipboard(hwnd)) {
hemf = GetClipboardData(uFormat);
GetClientRect(hwnd, &rc);
PlayEnhMetaFile(hdc, hemf, &rc);
CloseClipboard();
}
break;

case 0:
GetClientRect(hwnd, &rc);
DrawText(hdc, "The clipboard is empty.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
break;

default:
GetClientRect(hwnd, &rc);
DrawText(hdc, "Unable to display format.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
}
EndPaint(hwnd, &ps);
break;

case WM_SIZE:
if (uFormat == CF_OWNERDISPLAY) {
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_DDESHARE, sizeof(RECT));
lprc = GlobalLock(hglb);
GetClientRect(hwnd, lprc);
GlobalUnlock(hglb);

SendMessage(hwndOwner, WM_SIZECLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);

GlobalFree(hglb);
}
break;

case WM_CREATE:

/* Add the window to the clipboard viewer chain. */

hwndNextViewer = SetClipboardViewer(hwnd);
break;

case WM_CHANGECBCHAIN:

/* If the next window is closing, repair the chain. */

if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;

/* Otherwise, pass the message to the next link. */

else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);

break;

case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;

case WM_DRAWCLIPBOARD: /* clipboard contents changed. */

/*
* Update the window by using Auto clipboard format.
*/

SetAutoView(hwnd);

/*
* Pass the message to the next window in clipboard
* viewer chain.
*/

SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;

case WM_INITMENUPOPUP:
if (!HIWORD(lParam))
InitMenu(hwnd, (HMENU) wParam);
break;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_EXIT:
DestroyWindow(hwnd);
break;

case IDM_AUTO:
SetAutoView(hwnd);
break;

default:
fAuto = FALSE;
uFormat = LOWORD(wParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}

void PASCAL SetAutoView(HWND hwnd)
{
static UINT auPriorityList[] = {
CF_OWNERDISPLAY,
CF_TEXT,
CF_ENHMETAFILE,
CF_BITMAP
};

uFormat = GetPriorityClipboardFormat(auPriorityList, 4);
fAuto = TRUE;

InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}

void PASCAL InitMenu(HWND hwnd, HMENU hmenu)
{
UINT uFormat;
char szFormatName[80];
LPCSTR lpFormatName;
UINT fuFlags;
UINT idMenuItem;

/*
* If a menu is not the display menu, no initialization is necessary. */
*/

if (GetMenuItemID(hmenu, 0) != IDM_AUTO)
return;

/* Delete all menu items except the first. */

while (GetMenuItemCount(hmenu) > 1)
DeleteMenu(hmenu, 1, MF_BYPOSITION);

/* Check or uncheck the Auto menu item. */

fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
CheckMenuItem(hmenu, IDM_AUTO, fuFlags);

/* If there are no clipboard formats, return. */

if (CountClipboardFormats() == 0)
return;

/* Open the clipboard. */

if (!OpenClipboard(hwnd))
return;

/* Add a separator and then a menu item for each format. */

AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
uFormat = EnumClipboardFormats(0);

while (uFormat) {

/*
* Call an application-defined function to get the name
* of the clipboard format.
*/

lpFormatName = GetPredefinedClipboardFormatName(uFormat);

/* For registered formats, get the registered name. */

if (lpFormatName == NULL) {
if (GetClipboardFormatName(uFormat, szFormatName,
sizeof(szFormatName)))
lpFormatName = szFormatName;
else
lpFormatName = "(unknown)";
}

/*
* Add a menu item for the format. For displayable
* formats, use the format ID for the menu ID.
*/

if (IsDisplayableFormat(uFormat)) {
fuFlags = MF_STRING;
idMenuItem = uFormat;
}
else {
fuFlags = MF_STRING | MF_GRAYED;
idMenuItem = 0;
}
AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);

uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();

}

BOOL PASCAL IsDisplayableFormat(UINT uFormat)
{
switch (uFormat) {
case CF_OWNERDISPLAY:
case CF_TEXT:
case CF_ENHMETAFILE:
case CF_BITMAP:
return TRUE;
}
return FALSE;
}

+$#AK Clipboard Reference
The following functions and messages are associated with the Clipboard.
+$#A Clipboard Functions
Following are the functions used with the Clipboard.
ChangeClipboardChain_win32_ChangeClipboardChainCloseClipboard_win32_CloseClipboardCountClipboardFormats_win32_CountClipboardFormatsEmptyClipboard_win32_EmptyClipboardEnumClipboardFormats_win32_EnumClipboardFormatsGetClipboardData_win32_GetClipboardDataGetClipboardFormatName_win32_GetClipboardFormatNameGetClipboardOwner_win32_GetClipboardOwnerGetClipboardViewer_win32_GetClipboardViewerGetOpenClipboardWindow_win32_GetOpenClipboardWindowGetPriorityClipboardFormat_win32_GetPriorityClipboardFormatIsClipboardFormatAvailable_win32_IsClipboardFormatAvailableOpenClipboard_win32_OpenClipboardRegisterClipboardFormat_win32_RegisterClipboardFormatSetClipboardData_win32_SetClipboardDataSetClipboardViewer_win32_SetClipboardViewer
+$#A Clipboard Messages
Following are the messages used with the clipboard.
WM_ASKCBFORMATNAME_win32_WM_ASKCBFORMATNAMEWM_CHANGECBCHAIN_win32_WM_CHANGECBCHAINWM_COPY_win32_WM_COPYWM_CUT_win32_WM_CUTWM_DESTROYCLIPBOARD_win32_WM_DESTROYCLIPBOARDWM_DRAWCLIPBOARD_win32_WM_DRAWCLIPBOARDWM_HSCROLLCLIPBOARD_win32_WM_HSCROLLCLIPBOARDWM_PAINTCLIPBOARD_win32_WM_PAINTCLIPBOARDWM_PASTE_win32_WM_PASTEWM_RENDERALLFORMATS_win32_WM_RENDERALLFORMATSWM_RENDERFORMAT_win32_WM_RENDERFORMATWM_SIZECLIPBOARD_win32_WM_SIZECLIPBOARDWM_VSCROLLCLIPBOARD_win32_WM_VSCROLLCLIPBOARD

+Chapter 1
+$#AK Common Dialog Box Library
The following Windows 95 overview discusses the Common Dialog Box Library. Various common dialog boxes are described, and examples are included to help you use common dialog boxes in your Win32-based applications.
+$#A Common Dialog Box Library Platform Differences
The overviews for common dialog box libraries in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.
+$#A About Common Dialog Boxes
{ewl msdncd, EWGraphic, group10798 0 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview About Common Dialog Boxes_win32_About_Common_Dialog_Boxes_nt.
Common dialog boxes are the standard file, print, color, font, and search dialog boxes used by applications based on the Microsoft Win32 application programming interface (API). Common dialog boxes provide a uniform user interface that lets users carry out common tasks, such as opening files and printing documents, without being forced to learn new techniques with each application.
You can create common dialog boxes for your own applications by using the common dialog box functions. These functions supply the dialog box procedures and templates for the common dialog boxes; you supply the initial values for the dialog boxes and the addresses of the variables and buffers that receive the input from the dialog boxes.
+$#A Common Dialog Box Basics
Common dialog boxes offer varying levels of control to the application. When you use a common dialog box in its simplest form, you fill a structure with initial values, call a function, and retrieve the user's input from the structure after the function returns. But you can also specify options that modify the appearance and behavior of a common dialog; create hook functions that let you monitor and filter messages sent to the dialog box procedure; use registered messages to receive or send information to the dialog box; and modify or replace the template that defines the number, type, and position of controls in the dialog box. The following sections describe the basics you need to work with common dialog boxes at all levels of control.
+$#A Dialog Box Types
Although you create all common dialog boxes in essentially the same way (by filling a structure with initial values and calling a dialog box function), you use different functions and structures to create the different common dialog box types. There are these common dialog boxes:
Color Displays available colors and lets the user select basic colors or create a select custom colors. Use the ChooseColor_win32_ChooseColor function and CHOOSECOLOR_win32_CHOOSECOLOR_str structure.
Font Displays lists of facenames, point sizes, and other font attributes that the user can select. Use the ChooseFont_win32_ChooseFont function and CHOOSEFONT_win32_CHOOSEFONT_str structure.
Open Displays the names of files, directories and drives, and lets the user select the combination of names that uniquely identifies the file to be opened. Use the GetOpenFileName_win32_GetOpenFileName function and OPENFILENAME_win32_OPENFILENAME_str structure.
Save As Displays the names of files, directories and drives, and lets the user select or enter the combination of names that uniquely identifies the file to be saved. Use the GetSaveFileName_win32_GetSaveFileName function and OPENFILENAME_win32_OPENFILENAME_str structure.
Print Displays and lets the user select print job options, such as the range of pages to print and the number of copies. Use the PrintDlg_win32_PrintDlg function. and PRINTDLG_win32_PRINTDLG_str structure.
Page Setup Displays and lets the user select page configuration options, such as paper orientation, size, source, and margins. Use the PageSetupDlg_win32_PageSetupDlg function and PAGESETUPDLG_win32_PAGESETUPDLG_str structure.
Find Displays and lets the user select a search string and search options, such as the direction of the search. Use the FindText_win32_FindText function and FINDREPLACE_win32_FINDREPLACE_str structure.
Replace Displays and lets the user select search and replacement strings and replacement options, such as the direction of the search and the scope of the replacement. Use the ReplaceText_win32_ReplaceText function and FINDREPLACE_win32_FINDREPLACE_str structure.

Although a Print Setup dialog box is also available, it has been superseded by the Page Setup dialog box. Applications written for Win32 API version 4.0 or later should not use the Print Setup dialog box.
All common dialog boxes, except the Find and Replace dialog boxes, are modal. This means the function that you use the create the dialog box does not return until the user has closed the dialog box. The Find and Replace dialog boxes are modeless; the function returns before the dialog box closes. If you use the Find and Replace dialog boxes, you must also use the IsDialogMessage_win32_IsDialogMessage function in the main message loop of your application to ensure that these dialog boxes process dialog box keys, such as TAB and ESC, correctly.
+$#A Initialization Flags
You can modify the behavior and appearance of a common dialog box by using initialization flags. These are values that you set in the Flags member of the structure used to create the dialog box. You use the flags to specify which controls in a dialog box receive initial values, to disable selected controls, and to modify the range of values the user can set with the controls. You also use the flags to enable hook functions and custom templates for the dialog boxes.
For example, you set the CF_EFFECTS value in the Flags member of the CHOOSEFONT_win32_CHOOSEFONT_str structure to direct the Font dialog box to display controls that let the user choose the color of the logical font as well as whether to use strike through and underline effects.
The initialization flag values are unique to each common dialog box and are described in detail with the corresponding structures.
+$#A Hook Functions and Custom Dialog Box Templates
{ewl msdncd, EWGraphic, group10798 1 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Hook Functions and Custom Dialog Box Templates_win32_Hook_Functions_and_Custom_Dialog_Box_Templates_nt.
All common dialog box functions use default dialog box procedures to process messages for the dialog box and its controls. You can customize the operation of a common dialog box by providing a hook function that monitors and responds to messages sent to the default dialog box procedure. You enable a hook procedure by setting an ENABLEHOOK value in the Flags member and the address of the hook function in an lpfnHook member of the structure for the dialog box. A hook function has this general form:
UINT APIENTRY MyHook(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam){ switch (wParam) { case WM_*: // process the message return TRUE; default: return FALSE; }}
The hook function receives messages and notifications before the dialog box procedure attempts to process them. (The exception is the WM_INITDIALOG message which the hook function receives after the dialog box procedure.) The hook function can process a message or let the dialog box procedure process it. If the hook function processes a message, it must return TRUE; otherwise, it must must return FALSE to direct the dialog procedure to process the message.
If the hook function processes the WM_CTLCOLORDLG message, it must return a valid brush handle for painting the background of the dialog box. In general, if it processes any WM_CTLCOLOR* message, it must return a valid brush handle for painting the background of the given control.
A hook function should never attempt to terminate the dialog box by calling the EndDialog_win32_EndDialog function. Instead, the function must use the PostMessage_win32_PostMessage function to post a WM_COMMAND message with the IDABORT value to the dialog box procedure. Posting IDABORT closes the dialog box and causes the dialog box function to return FALSE. If an application needs a specific error code, it must work out its own comunication mechanism with the hook function.
Although you can subclass controls in any of the common dialog boxes, you must do so from the hook function while processing the WM_INITDIALOG message. Because the common dialog box procedure may also subclass the controls, subclassing after the dialog box procedure ensures that your subclass hook procedure receives the control-specific messages before the subclass hook procedure set by the dialog box procedure.
+$#A Registered Messages and Message Strings
Common dialog boxes send registered messages to your window procedure or hook function to notify you of events and to request responses. A registered message is like any other window message except that the message identifier is not predefined. Before you can detect and process a registered message, you must retrieve the message identifier by passing the predefined message string to the RegisterWindowMessage_win32_RegisterWindowMessage function. There are these predefined message strings:
COLOROKSTRING Approve or reject a color selection.
FILEOKSTRING Approve or reject a filename.
FINDMSGSTRING Find or replace a given string.
HELPMSGSTRING Display help for a dialog box.
LBSELCHSTRING Selection in a list box has changed.
SETRGBSTRING Set the current color selection.
SHAREVISTRING Opening a file causes a sharing violation.

The Find and Replace dialog boxes send their registered message, FINDMSGSTRING, to the window procedure of the window that owns the dialog box. Other common dialog boxes send their registered messages to hook functions. This means you must enable the hook function for these dialog boxes to receive the messages. One registered message, SETRGBSTRING, is intended to be sent to the common dialog box from the hook function.
In addition to registered messages, some common dialog boxes send and can accept other window messages. For example, the hook function for a Font dialog box can send the WM_CHOOSEFONT_GETLOGFONT message to retrieve the current logical font selections. These other messages and the registered messages are described in more detail in the sections later in this chapter that described the inidividual common dialog boxes.
+$#A Custom Templates
All common dialog box functions use default templates to define the number, type and position of controls in the dialog box. You can give users access to additional controls that are unique to your application by defining custom templates. For some dialog boxes, the custom template replaces the default template; for others, the custom template defines a "child" dialog box whose controls are combined with the controls specified by the default template.
Because a primary goal for common dialog boxes is to provide a consistent user interface for all applications, it is important that you customize a common dialog box only when it is absolutely necessary. If you do customize, always try to leave the original controls intact. In particular, avoid hiding a control or changing its function. If you add new controls, place them in empty space already in the dialog box or increase the size of the dialog box to make space.
You enable a custom template by setting a ENABLETEMPLATE or ENABLETEMPLATEHANDLE value in the Flags member in the corresponding structure for the dialog box. If the template is a resource in an application or dynamic link library, you must set the hInstance and lpTemplateName members of the structure. If the template is already in memory, the member you set depends on the given structure. In most cases, you must also enable a hook function for the dialog box to support and process input for the additional controls in your custom template.
When making a custom template, make sure the identifiers for the controls that you add are unique and do not conflict with existing controls. The default templates and identifier values are available in resource script (.DLG) and corresponding header (.H) files. Use these to determine the identifiers for the default controls.
+$#A Help Support
All common dialog boxes provide online help by default. The user can display help for individual controls by selecting the control and pressing the F1 key or by clicking the ? button in the title bar and subsequently clicking on a control.
If you customize a dialog box by adding new controls, you also need to extend help support for these controls by processing requests for help in the hook function. The hook function receives WM_HELP_win32_WM_HELP or WM_CONTEXTMENU_win32_WM_CONTEXTMENU messages when the user requests help by pressing the F1 key, using the ? button, or clicking the right mouse button. You should process these messages for the controls you have added, but let the default dialog box procedure process the messages for the original controls. For more information about how to process these messages, see the chapter describing the WinHelp_win32_WinHelp function.
Although no longer recommended, you can supplement the default help by adding and supporting a Help button in the the dialog box. The Help button is useful for describing the general purpose of the dialog box as it applies to your application. You add a Help button to a dialog box by setting a SHOWHELP value in the Flags member of the structure for the dialog box. You support the Help button by processing the registered message associated with the HELPMSGSTRING message string. The common dialog box sends this message to the window procedure of the owner window whenever the user clicks the Help button.
+$#A Color Dialog Box
{ewl msdncd, EWGraphic, group10798 2 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Color Dialog Box_win32_Color_Dialog_Box_nt.
The Color dialog box lets users select from a set of basic colors or create and select from a set of custom colors. You create and display a Color dialog box by initializing a CHOOSECOLOR_win32_CHOOSECOLOR_str structure and passing the structure to the ChooseColor_win32_ChooseColor function. The Color dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 3 /a "SDK_06.bmp"}
If the user chooses a color and clicks the OK button, ChooseColor returns TRUE and sets the rgbResult member of the CHOOSECOLOR structure to the RGB color value corresponding to the color selected by the user. The RGB color value specifies the intensities of the individual red, green, and blue colors that make up the selected color. The individual values range from 0 through 255. Use the RGB macro to create color values, and the GetRValue_win32_GetRValue, GetBValue_win32_GetBValue, and GetGValue_win32_GetGValue macros to extract individual colors from a color value.
If the user cancels the Color dialog box or an error occurs, ChooseColor_win32_ChooseColor returns FALSE and the rgbResult member is not defined. You can determine the cause of an error by using the CommDlgExtendedError_win32_CommDlgExtendedError function to retrieve the extended error value.
The user can select from either the basic colors or custom colors. The basic colors, set by the Color dialog box, represent a reasonable selection of the colors available on the given device. Custom colors are reserved for colors that you choose or that the user creates. You must set the initial values for the 16 custom colors by setting the elements of the array pointed to by the lpCustColors member of the CHOOSECOLOR_win32_CHOOSECOLOR_str structure to the desired RGB color values.
You can modify the appearance and behavior of the Color dialog box by setting the Flags member of the CHOOSECOLOR structure to selected values. For example, you can direct the dialog box to display only solid colors by using the CC_SOLIDCOLOR value and you can direct the dialog box to propose an initial color selection (other than black) by setting the rgbResult member to the desired color value and using the CC_RGBINIT value.
The user can create custom colors by clicking the Define Custom Colors button in the dialog box. This displays an extension to the Color dialog box with which the user can pick a color, enter an RGB color value directly, or enter an RGB value indirectly by supplying hue, saturation, and luminousity (HSL) values. You can direct the Color dialog box to always display this extension by setting the Flags member to CC_FULLOPEN. You can prevent this extension from displaying by using the CC_PREVENTFULLOPEN value to disable the Define Custom Colors button. When the user creates a custom color, the dialog box also copies the new RGB color value to the corresponding element in the array pointed to by the lpCustColors member. To preserve new custom colors between calls to ChooseFont_win32_ChooseFont, it is recommended that you allocate static memory for the array.
You can enable a hook function for the dialog box by setting the CC_ENABLEHOOK value in the Flags member; the lpfnHook member must specify the address of the hook function. The dialog box sends the registered message defined by the COLOROKSTRING message string to the hook function whenever the user selects a color and click the OK button. The hook function returns a nonzero value to accept this color value; zero to reject it and keep the dialog box open. You can force the dialog box to select a given color by sending the registered message defined by the SETRGBSTRING message string from the hook function to the dialog box.
You can customize the Color dialog box by setting the CC_ENABLETEMPLATE or CC_ENABLETEMPLATEHANDLE value in the Flags member. Because the Color dialog box replaces the default template with the custom template, you should always start with the default template, specified in the COLOR.DLG file, when creating the custom template.
+$#A Font Dialog Box
{ewl msdncd, EWGraphic, group10798 4 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Font Dialog Box_win32_Font_Dialog_Box_nt.
The Font dialog box lets the user choose attributes for a logical font, such as typeface name, style (bold, italic, or regular), point size, effect (underline or strikeout), text color, and script (or character set). You create and display a Font dialog box by initializing a CHOOSEFONT_win32_CHOOSEFONT_str structure and passing the structure to the ChooseFont_win32_ChooseFont function. The Font dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 5 /a "SDK_07.bmp"}
If the user chooses font attributes and clicks the OK button, ChooseFont returns TRUE and sets the members of the LOGFONT_win32_LOGFONT_str structure pointed to by the lpLogFont member of the CHOOSEFONT structure. You can use the LOGFONT structure with the CreateFontIndirect_win32_CreateFontIndirect function to create a logical font.
If the user cancels the Font dialog box or an error occurs, ChooseFont_win32_ChooseFont returns FALSE and the LOGFONT structure is not defined. You can determine the cause of an error by using the CommDlgExtendedError_win32_CommDlgExtendedError function to retrieve the extended error value.
When initializing the CHOOSEFONT_win32_CHOOSEFONT_str structure, you must set the Flags member to CF_SCREENFONTS, CF_PRINTERFONTS, or CF_BOTH. To display the options for strikeout, underline, and color, you must use the CF_EFFECTS value.
To list device fonts for a printer, you must obtain a device context (DC) handle for the printer and use this handle in the hDC member of the CHOOSEFONT structure. To restrict the fonts the dialog box displays, you can use the CF_TTONLY, CF_FIXEDPITCHONLY, CF_NOVECTORFONTS, CF_NOVERTFONTS, CF_SCALABLEONLY, and CF_WYSIWYG values. You can also restrict the available styles, point sizes, and effects the dialog box displays for some fonts by using the CF_NOSIMULATIONS value. You can restrict the scripts represented by the displayed fonts by using the CF_SCRIPTSONLY and CF_SELECTSCRIPT values.
You can use the CF_INITTOLOGFONTSTRUCT value in Flags to initialize the controls in the dialog box with the values given in the LOGFONT_win32_LOGFONT_str structure. To initialize the text color, set the rgbColors member of CHOOSEFONT to a RGB color value. You can use the CF_USESTYLE to initialize the style control to the style name given in the buffer pointed to by the lpszStyle member. Use the CF_NOFACESEL, CF_NOSTYLESEL, CF_NOSIZESEL, and CF_NOSCRIPTSEL values to selectively prevent the dialog box from displaying initial values for the corresponding controls. This is useful when working with a selection of text that has more than one typeface, style, point size, or script value. These values will also be set in Flags when ChooseFont_win32_ChooseFont returns if the user did not select a corresponding value.
You can force the user to enter only valid typeface names, styles, and point sizes by using the CF_FORCEFONTEXIST value. You can further force the user to enter only point sizes in a given range by using the CF_LIMITSIZE value and setting the minimum and maximum point sizes in the nSizeMin and nSizeMax members.
You can enable the hook function for the Font dialog box by setting the CF_ENABLEHOOK value in Flags and setting the lpfnHook member to the address of the function. The dialog box procedure sends a WM_INITDIALOG message to the hook function with the lParam parameter set to the address of the CHOOSEFONT_win32_CHOOSEFONT_str structure used to initialize the dialog box. The hook function can send the WM_CHOOSEFONT_GETLOGFONT_win32_WM_CHOOSEFONT_GETLOGFONT message to the dialog box to retrieve the address of the LOGFONT_win32_LOGFONT_str structure that contains the current selections for the font. This is especially useful when processing processing the WM_COMMAND sent by the Apply button. (You can enable the Apply button by setting the Flags member to CF_APPLY.) The function can send the WM_CHOOSEFONT_SETLOGFONT to force the current selections to given values. The function can also send the WM_CHOOSEFONT_SETFLAGS message to set flags to the given value.
You can customize the Font dialog box by setting the CF_ENABLETEMPLATE or CF_ENABLETEMPLATEHANDLE value in the Flags member. Because the Font dialog box replaces the default template with the custom template, you should always start with the default template, specified in the FONT.DLG file, when creating the custom template.
+$#A Open and Save As Dialog Boxes
{ewl msdncd, EWGraphic, group10798 6 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Open and Save As Dialog Boxes_win32_Open_and_Save_As_Dialog_Boxes_nt.
The Open dialog box and the Save As dialog box let the user choose the location (drive and directory) and the name of a file or a set of files to open and save. The dialog boxes are nearly identical in appearance and behavior and you initialize them using the same OPENFILENAME_win32_OPENFILENAME_str structure. You create the Open dialog box by using the GetOpenFileName_win32_GetOpenFileName function and the Save As dialog box by using the GetSaveFileName_win32_GetSaveFileName function.
The appearance of the dialog boxes these functions create depend on the version of the Win32 API and whether you set the OFN_EXPLORER value in the Flags member of the OPENFILENAME structure. When running under version 4.0 or with OFN_EXPLORER, the dialog boxes provide user-interface features that are similar to the Windows Explorer. The Open dialog box looks like this:
{ewc msdncd, EWGraphic, group10798 7 /a "SDK_03.bmp"}
The Save As dialog box looks like this:
{ewc msdncd, EWGraphic, group10798 8 /a "SDK_12.bmp"}
Although the default title for a dialog box is either Open or Save As, you can change the title by setting the lpstrTitle member to the address of a new title. Also, you can hide the Open as read only option by using the OFN_HIDEREADONLY value. If this option is not hidden, the dialog box sets Flags to OFN_READONLY on return if the user checks this option.
+$#A Filenames and Directories
To retrieve a filename from the dialog box, you must set the lpstrFile member of the OPENFILENAME_win32_OPENFILENAME_str structure to the address of the buffer to receive the filename and set the nMaxFile member to the size of that buffer. Buffer sizes must be in bytes for the ANSI version of the structure and characters for the Unicode version. If the user chooses a filename and clicks the OK button, the dialog box copies the selected drive, directory, and filename to the given buffer. The function also sets the nFileOffset and nFileExtension members to the offsets, in bytes or characters, from the start of the buffer to the filename and to the filename extension, respectively. You can retrieve just the filename from the buffer pointed to by lpstrFile by using the GetFileTitle_win32_GetFileTitle function. Alternately you can have the dialog box copy the filename to a buffer by setting the lpstrFileTitle member to the address of the buffer and the nMaxFileTitle member to size of the buffer.
If the user cancels the dialog box or an error occurs, the function returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError_win32_CommDlgExtendedError function to retrieve the extended error value. If the buffer is too small to receive the full name, the CommDlgExtendedError function returns FNERR_BUFFERTOOSMALL and the first 2 bytes of the buffer pointed to by the lpstrFile member are set to an integer value specifying the size required to receive the full name.
The dialog box uses the current directory as the initial directory from which to display files and directories. You can set your own initial directory by setting the lpstrInitialDir member to the address of a string that specifies a directory. To prevent the dialog box from changing the current directory for the application, you can set the Flags member to OFN_NOCHANGEDIR. The user can still change directories to find a file, but the dialog box returns to the original directory when it closes.
You can direct the dialog box to add a default filename extension, if the user does not, by setting the lpstrDefExt member to the address of a string specifying an extension. Otherwise, the dialog box leaves filenames without extensions. If the user explicitly enters an extension or selects a file that already has an extension, the dialog box sets the Flags member to OFN_EXTENSIONDIFFERENT if that extension is different than that given by lpstrDefExt.
You can let the user select more than one file from a directory by using the OFN_ALLOWMULTISELECT value. If this case, the dialog box copies two or more consecutive, null-terminated strings to the buffer pointed to by lpstrFile. The first string specifies the selected drive and directory; all subsequent strings specify the names of files. The last string ends with an extra null character. The nFileExtension member is not used, and the nFileOffset member is the offset to the first filename.
+$#A Filters
You can restrict the number of files displayed by the dialog box by setting the lpstrFilter member to the address of a buffer that contains one or more filters. A filter consists of two null-terminated strings, a description and a pattern, one concatenated to the other. The dialog box displays the description to let the user pick which filter to use; it uses the pattern to pick out which files to display. The pattern can be a combination of valid filename characters and the asterisk (*). The asterisk is a wildcard that represents any combination of valid filename characters. The dialog box displays only those files that match the pattern. For example, if the pattern is "*.TXT", the dialog box displays only the files having the filename extension TXT. You can have multiple patterns for a given description, but must separate each pattern from the preceding with a semicolon (;).
You can supply multiple filters by concatenating them in the buffer. You must terminate the last filter with an extra null character. You can specify which filter the dialog box should initially use by setting the nFilterIndex member to the index of the filter. The first filter in the buffer has index 1, the second 2, and so on. If the user changes the filter while using the dialog box, the nFilterIndex member is set to the index of the selected filter on return.
You can create a custom filter by setting the lpstrCustomFilter member to the address of a buffer that contains a single filter and setting the nMaxCustFilter member to the size of the buffer, in characters or bytes. The dialog box always places the custom filter at the beginning of the list of filters and, on return, always updates the pattern part of the filter with the pattern from the filter selected by the user.
+$#A File and Directory Validation
The dialog box automatically checks filenames entered by the user to make sure the names contain valid characters. You can also direct the dialog to verify that the given file or drive and directory exist by using the OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST value, respectively. You can override the filename character validation by using the OFN_NOVALIDATE value.
The dialog box returns an error if opening the selected file would cause a sharing violation. You can direct the dialog box to send notification messages about sharing violations by using the OFN_SHAREAWARE value.
When using the Save As dialog box, you can direct the dialog box to prompt the user for permission to create a new file or overwrite an existing file by using the OFN_CREATEPROMPT and OFN_OVERWRITEPROMPT values. By default, the dialog box tests whether a new file can be created in the selected directory by creating a zero-length test file. You can prevent the creation of this test file by using the OFN_NOTESTFILECREATE value.
You can prevent the dialog box from return names of existing files that have the read-only attribute by using the OFN_NOREADONLYRETURN value. You can also prevent the dialog box from dereferencing link files by using the OFN_NODEREFERENCELINKS value. In this case, the dialog box returns the name of the link file rather than the name of the file referenced by the link file.
+$#A Hook Function
{ewl msdncd, EWGraphic, group10798 9 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview The Hook Function_win32_The_Hook_Function.
You can enable a hook function for the dialog box by using the OFN_ENABLEHOOK value and setting the lpfnHook member to the address of the function. The hook function lets you monitor messages sent from the dialog box procedure. It also lets you support any controls you may add to the dialog box as described in the next section. When enabling the hook function, you should also set the OFN_EXPLORER value in the Flags member to make sure the dialog box displays the Windows Explorer-style user interface. This section only describes the hook function for dialog boxes created using OFN_EXPLORER.
The hook function receives a variety of messages while the dialog box is created and eventually destroyed. For example, the hook function receives a WM_INITDIALOG message with the lParam parameter set to the address of the OPENFILENAME_win32_OPENFILENAME_str structure used to initialize the dialog box. Most messages, however, are WM_NOTIFY notification messages indicating actions taken by the user in the dialog box. The lParam parameter for each WM_NOTIFY message is the address of a OFNOTIFY structure that defines the action. The code member in the header for the OFNOTIFY structure contains one of these notification values:
CDN_FILEOK The user click the Open or Save button; the dialog box is about to close.
CDN_FOLDERCHANGE A new folder has been opened in the Open or Save As dialog box.
CDN_HELP The user clicked the Help button.
CDN_INITDONE The system has finished initializing the dialog box, and the dialog box has finished processing the WM_INITDIALOG message. Also, the system has finished arranging controls in the common dialog box to make room for the controls of the child dialog box (if any).
CDN_SELCHANGE The user has selected a new file or folder from the file list.
CDN_SHAREVIOLATION The common dialog box encountered a sharing violation on the file about to be returned.
CDN_TYPECHANGE The user selected a new file type from the "Files of type" combo box.

These messages are useful for modifying the behavior of the dialog box. For example, the hook function receives the CDN_FILEOK message when to user chooses a filename and clicks the OK button. The hook function can accept or reject the selected name by returning 0 or 1, respectively. Rejecting the filename forces the dialog box to remain open. To return a value from the hook function, you must set the message result value by using the SetWindowLong_win32_SetWindowLong function and the DWL_MSGRESULT value.
Although the WM_NOTIFY messages supersede the registered messages previously used with the Open and Save As dialog boxes (such as the FILEOKSTRING, LBSELCHSTRING, SHAREVISTRING, and HELPMSGSTRING messages), the hook function also receives the superseded message after the WM_NOTIFY message if it does not use SetWindowLong to set the message result value or sets this value to zero.
You can send messages to the dialog box from the hook function to retrieve information about the status of the dialog box. The messages include:
CDM_GETFILEPATH Retrieves the path and filename of the selected file.
CDM_GETFOLDERIDLIST Retrieves the address of the item identifier list corresponding to the folder that the dialog box currently has open.
CDM_GETFOLDERPATH Retrieves the path and name of the currently open folder or directory for the dialog box.
CDM_GETSPEC Retrieves the filename (not including the path) of the currently selected file in the dialog box.
CDM_HIDECONTROL Hides the given control.
CDM_SETCONTROLTEXT Sets the text in the given control.
CDM_SETDEFEXT Sets the default filename extension for the dialog box.

+$#A Customizing Dialog Boxes
You can customize the Open or Save As dialog box by using the OFN_ENABLETEMPLATE or OFN_ENABLETEMPLATEHANDLE value and setting the hInstance and lpTemplateName members to values that specify a custom dialog box template. Customizing a dialog box lets you add controls to the default Open or Save As dialog box, giving users options that are specific to your application. When customizing a dialog box, you must use the OFN_EXPLORER value to make sure the controls in your custom dialog box are added to (rather than replace) the controls in the default dialog box. This section only describes customizing dialog boxes that you have created using OFN_EXPLORER.
If you use the OFN_ENABLETEMPLATE value, the lpTemplateName member must point to the name or resource identifier of a dialog template resource in the module identified by hInstance. If you use the OFN_ENABLETEMPLATEHANDLE value, hInstance must be the handle of a block of memory containing the the dialog template. In both cases, you must also enable a hook function by using the OFN_ENABLEHOOK value. This hook function serves as the dialog box procedure for the custom dialog box as well as monitors notification messages sent from the default dialog box.
You can create your custom dialog box template as you do any other template, except that you must specify the WS_CHILD and WS_CLIPSIBLINGS styles and should specify the DS_3DLOOK and DS_CONTROL styles. The system requires that the custom dialog box be created as a child of the default Open or Save As dialog box. The WS_CLIPSIBLINGS style ensures that the custom dialog box does not paint over any of the controls in the default dialog box. The DS_3DLOOK style makes sure that the controls in the custom dialog box are consistent with the controls in the default dialog box. The DS_CONTROL style makes sure that the user can use the TAB and other navigation keys to move between all controls, default or custom, in the customized dialog box.
To make room for the new controls, the system expands the default dialog box by the width and height of the custom dialog box. By default, all controls from the custom dialog box are positioned below the controls in the default dialog box. However, you can override this default positioning by including a static text control in your custom dialog box template and assigning it the control identifier value of stc32. (This value is defined in the DLG.H header file.) In this case, the system uses the control as the point of reference for determining where to position the new controls. All new controls above and to the left of the stc32 control are positioned the same amount above and to the left of the controls in the default dialog box. New controls below and to the right of the stc32 control are positioned below and to the right of the default controls. In general, each new control is positioned such that it has the same position relative to the default controls as it had to the stc32 control. To make room for these new controls, the system adds space to the left, right, bottom, and top of the default dialog box as needed.
The system requires the hook function to process all messages intended for the custom dialog box and therefore sends the same window messages to the hook function as to any other dialog box procedure. For example, the hook function receives WM_COMMAND messages when the user clicks on button controls in the custom dialog box. The hook function is responsible for initializing these controls and retrieve values from the controls when the dialog box is closed. Note that when the hook function receives the WM_INITDIALOG message, the system has not yet moved the controls to their final positions.
The default dialog box procedure handles messages for all the controls in the default dialog box, but the hook function receives the WM_NOTIFY notification messages for user actions on these controls as described in the previous section.
+$#A Print Dialog Box
{ewl msdncd, EWGraphic, group10798 10 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Print Dialog Box_win32_Print_Dialog_Box_nt.
The Print dialog box lets the user select options for a particular print job, such as such as the printer to use, the range of pages to print, and the number of copies. You create and display a Print dialog box by initializing a PRINTDLG_win32_PRINTDLG_str structure and passing the structure to the PrintDlg_win32_PrintDlg function. The Print dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 11 /a "SDK_08.bmp"}
If the user chooses options and clicks the OK button, PrintDlg returns TRUE and sets the hDevMode and hDevNames members to global memory handles for the DEVMODE_win32_DEVMODE_str and DEVNAMES_win32_DEVNAMES_str structures that specify the user's selections. You can use the information in these structures to create a device or information context for the selected printer. If the user cancels the Print dialog box or an error occurs, PrintDlg returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.
When the PrintDlg_win32_PrintDlg function returns, you can determine which options the user selected by checking the Flags member. The PD_ALLPAGES, PD_SELECTION, or PD_PAGENUMS value is set to indicate whether the user wants to print all pages, selected pages, or pages in a range of page numbers. If PD_PAGENUMS is set, the nFromPage and nToPage members specify the page range. The PD_COLLATE value indicates that the user wants the pages to be collated when printed. The PD_PRINTTOFILE value indicates that the user selected the Print To File option. You can also set these values in the Flags member before calling PrintDlg to initially set the corresponding option. Use the PD_NOPAGENUMS and PD_NOSELECTION values to disable the corresponding options.
By default, the Print dialog box initially displays information about the current default printer. You can direct it to display information for another installed printer by initializing a DEVMODE_win32_DEVMODE_str or DEVNAMES_win32_DEVNAMES_str structure and assigning the global memory handle for the structure to the hDevMode or hDevNames member. The device name you specify in the dmDeviceName member of the DEVMODE structure or in the wDriverOffset member of the DEVNAMES structure must identify a printer device that is also listed in the [Devices] section of the WIN.INI file. Otherwise, PrintDlg_win32_PrintDlg returns an error.
When the PrintDlg function successfully returns, the application must render the number of copies in nCopies, printing them collated or not collated depending on whether the PD_COLLATE flag is set. If the application does not support multiple copies or simulated collation, it should specify PD_USEDEVMODECOPIESANDCOLLATE to allow these options only if the printer driver supports them.
You can direct PrintDlg_win32_PrintDlg to automatically create a device or information context for the printer by using the PD_RETURNDC or PD_RETURNIC value. The function returns the handle of the context in the hDC member. If you use the PD_RETURNDC value, you can use the device context to generate output for the printer. If the user also selects the Print To File option, however, you should retrieve the address of the filename at the offset indicated by the wOutputOffset member of the DEVNAMES_win32_DEVNAMES_str structure. You should specify the address of the filename in the lpszOutput member of the DOCINFO_win32_DOCINFO_str structure. The print subsystem will query the user for the filename. If you do not want the user to select Print To File option, set the Flags member to PD_HIDEPRINTTOFILE to hide the option or PD_DISABLEPRINTTOFILE to disable it.
You can retrieve information about the default printer without displaying the Print dialog box by using the PD_RETURNDEFAULT value. In this case, PrintDlg_win32_PrintDlg returns immediately after setting the hDevMode and hDevNames members to handles for structures containing the information.
You can prevent PrintDlg from displaying warnings on errors by using the PD_NOWARNING value. Otherwise, the function displays message boxes on errors such as no printers installed.
You can enable a hook function by using the PD_ENABLEPRINTHOOK value and setting the lpfnPrintHook member to the address of your hook function. You can customize the Print dialog box by using the PD_ENABLEPRINTTEMPLATE or PD_ENABLEPRINTTEMPLATEHANDLE value and specifying a custom dialog box template. PrintDlg_win32_PrintDlg uses the custom dialog box to replace the default dialog box. If you use PD_ENABLEPRINTTEMPLATE, you set the hInstance and lpPrintTemplateName members to specify the name and module instance for a dialog box template. If PD_ENABLEPRINTTEMPLATEHANDLE, set hPrintTemplate to the handle of a dialog box template in memory.
Although you can also create and display a Printer Setup dialog box using the PrintDlg_win32_PrintDlg function and the PD_PRINTSETUP value, the Printer Setup dialog box has been superseded by the Page Setup dialog box and should not be used. The PD_ENABLESETUPHOOK, PD_ENABLESETUPTEMPLATE, and PD_ENABLESETUPTEMPLATEHANDLE values apply only to the Page Setup dialog box.
+$#A Page Setup Dialog Box
{ewl msdncd, EWGraphic, group10798 12 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Page Setup Dialog Box_win32_Page_Setup_Dialog_Box_nt.
The Page Setup dialog box lets the user to set the attributes of the printed page, such as the paper type (envelope, legal, letter, and so on), the paper source (manual feed, tractor feed, sheet feeder, and so on), the page orientation (portrait or landscape), and the width of the page margins. You create and display a Page Setup dialog box by initializing a PAGESETUPDLG_win32_PAGESETUPDLG_str structure and passing the structure to the PageSetupDlg_win32_PageSetupDlg function. The Page Setup dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 13 /a "SDK_09.bmp"}
If the user chooses options and clicks the OK button, PageSetupDlg_win32_PageSetupDlg returns TRUE after setting various members in the PAGESETUPDLG_win32_PAGESETUPDLG_str structure to specify the user's selections. You can use this information to prepare the output to be sent to the selected printer. If the user cancels the Page Setup dialog box or an error occurs, the function returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError_win32_CommDlgExtendedError function to retrieve the extended error value.
When PageSetupDlg returns, the ptPaperSize and rtMargin members contain the values selected by the user. The hDevMode and hDevNames members contain to global memory handles for the DEVMODE_win32_DEVMODE_str and DEVNAMES_win32_DEVNAMES_str structures that contain additional page information as well as information about the printer.
By default, the Page Setup dialog box displays information about the current default printer. You can direct the dialog box to display information about a specific printer by setting the members of a DEVMODE or DEVNAMES structure and assigning the global memory handles of the structures to the corresponding member in PAGESETUPDLG_win32_PAGESETUPDLG_str . If you specify the name of a printer that is not currently installed, the dialog box displays an error message. You can prevent the dialog box from displaying error messages by using the PSD_NOWARNING value. You can use the PSD_RETURNDEFAULT value to retrieve information about the default printer without displaying the Page Setup dialog box.
By default, measurements returned by the dialog box are in thousandths of inches if inches are the default measurement, or hundredths of millimeters if metric is the default measurement system. You can set the units of measurement for the margin widths in the dialog box by using the PSD_INHUNDREDTHSOFMILLIMETERS and PSD_INTHOUSANDTHSOFINCHES value. You can set the initial values for the margins by using the PSD_MARGINS value and the rtMargin member. The default initial values are one inch. You can set the minimum values that the user can enter for the margins by using the PSD_MINMARGINS values and the rtMinMargin member. The default minimum values are the minimum margins allowed by the printer.
You can prevent users from selecting given options by using the PSD_DISABLEMARGINS, PSD_DISABLEORIENTATION, PSD_DISABLEPAPER, and PSD_DISABLEPRINTER values to disable the corresponding controls.
You can enable a hook function for the dialog box by using the PSD_ENABLEPAGESETUPHOOK value and setting the lpfnPageSetupHook member to the address of your hook function. You can customize the Page Setup dialog box by using the PSD_ENABLEPAGESETUPTEMPLATE or PSD_ENABLEPAGESETUPTEMPLATEHANDLE value and specifying a custom dialog box template. PageSetupDlg_win32_PageSetupDlg uses the custom dialog box to replace the default dialog box. If you use PD_ENABLEPAGESETUPTEMPLATE, you set the hInstance and lpPageSetupTemplateName members to specify the name and module instance for a dialog box template. If PD_ENABLEPAGESETUPTEMPLATEHANDLE, set hPageSetupTemplate to the handle of a dialog box template in memory.
The Page Setup dialog box includes an image of a sample page that shows how the user's selections affect the appearance of the printed output. The image consists of a rectangle that represents the selected paper or envelope type, with a dotted-line rectangle representing the current margins and partial (greek-text) characters to show how text looks on the printed page. You can customize the appearance of the sample page by using the PSD_ENABLEPAGEPAINTHOOK value and setting the lpfnPagePaintHook member to the address of a hook function. The dialog box sends the following paint messages to the hook function whenever it needs a portion of the sample page redrawn:
WM_PSD_PAGESETUPDLG Notifies the hook function to carry out initialization tasks.
WM_PSD_FULLPAGERECT Specifies the bounding rectangle of the sample page.
WM_PSD_MINMARGINRECT Specifies the minimum margin rectangle.
WM_PSD_MARGINRECT Specifies the margin rectangle.
WM_PSD_GREEKTEXTRECT Specifies the greek-text rectangle.
WM_PSD_ENVSTAMPRECT Specifies the envelope-stamp rectangle (envelopes only).
WM_PSD_YAFULLPAGERECT Specifies the bounding rectangle of the sample page.

The hook function receives these messages in the order in which they are listed. If the hook function processes a given message, it should draw the particular area within the sample page and return TRUE. Otherwise, it should return FALSE.
You can disable the display of the sample page by using the PSD_DISABLEPAGEPAINTING in the Flags member. Disabling the sample page prevents the WM_PSD_* message from being sent to your hook function. It also prevents the default dialog box procedure from painting the same page.
+$#A Find and Replace Dialog Boxes
{ewl msdncd, EWGraphic, group10798 14 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Find and Replace Dialog Boxes_win32_Find_and_Replace_Dialog_Boxes_nt.
The Find dialog box and the Replace dialog box let users enter information that you can use to search for and possibly replace text in a document. The Find dialog box lets users enter a string to search for and specify search options. The Replace dialog box lets users enter both a search and replace string and specify search-and-replace options. You create and display a Find dialog box by initializing a FINDREPLACE_win32_FINDREPLACE_str structure and passing the structure to the FindText_win32_FindText function. The Find dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 15 /a "SDK_10.bmp"}
You create and display a Replace dialog box by initializing a FINDREPLACE structure and passing the structure to the ReplaceText_win32_ReplaceText function. The Replace dialog box typically looks like this:
{ewc msdncd, EWGraphic, group10798 16 /a "SDK_11.bmp"}
Unlike other common dialog boxes, the Find and the Replace dialog boxes are modeless. This means the user can switch between the dialog box and the window that created that dialog box. This is useful for letting the user search for a string, switch to the application window to work on the string, and switch back to the dialog box to search for another string without repeating the command needed to open the dialog box.
If the FindText or ReplaceText function successfully creates the dialog box, it returns the handle of the dialog box. You can use this handle to move and communicate with the dialog box. If the function cannot create the dialog box, it returns NULL. You can determine the cause of an error by using the CommDlgExtendedError_win32_CommDlgExtendedError function to retrieve the extended error value.
When the user clicks the Find Next, Replace, or Replace All button in a dialog box, the dialog box procedure sends a registered message to the window procedure of the owner window. To receive and process these messages, you must set the hwndOwner member of the FINDREPLACE_win32_FINDREPLACE_str structure to the handle of the window to receive the messages and you must register the FINDMSGSTRING message string to retrieve the value of the registered message.
Each registered message contains the address of the FINDREPLACE structure. Before sending the message, the dialog box sets the members of this structure with the latest user selections, including the string to search for, the replacement string (if any) and the search-and-replace options. You must check the Flags member to determine the action to carry out and the options selected.
You can determine whether the search for a string, replace a string, or replace all occurrences of a string by checking the Flags member for the the FR_FINDNEXT, FR_REPLACE, and FR_REPLACEALL value, respectively. When the user eventually closes the dialog box, the dialog box sets the FR_DIALOGTERM value in the Flags member of the FINDREPLACE_win32_FINDREPLACE_str structure in the last registered message sent to the owner window. After the owner window processes this message, the handle of the dialog box is no longer valid.
You can determine the search options by checking for the FR_DOWN, FR_MATCHCASE, and FR_WHOLEWORD values. If a value is not present, the opposite action is implied. For example, if FR_DOWN is present, the search should be carried out from the current location to the end of the document; but if not present, the search should be to the beginning of the document. You can disable the search options by using the FR_NOUPDOWN, FR_NOMATCHCASE, and FR_NOWHOLEWORD values. You can remove the options from the dialog box by using the FR_HIDEUPDOWN, FR_HIDEMATCHCASE, and FR_HIDEWHOLEWORD values.
You can enable a hook function for a Find dialog box or Replace dialog box by using the FR_ENABLEHOOK value and setting the lpfnHook member to the address of the hook function. If the hook function returns FALSE in response to the WM_INITDIALOG message, the function is responsible for calling the ShowWindow_win32_ShowWindow and UpdateWindow_win32_UpdateWindow functions to display the dialog box any painting operations are complete. You can customize the dialog box by using the FR_ENABLETEMPLATE or FR_ENABLETEMPLATEHANDLE value. The custom dialog box replaces the default dialog box.
+$#A Using Common Dialog Boxes
{ewl msdncd, EWGraphic, group10798 17 /a "SDK.BMP"} Windows 95 OverviewFor more information, see the Windows NT overview Using Common Dialog Boxes_win32_Using_Common_Dialog_Boxes_nt.
· Choosing a color_win32_Choosing_a_Color.
· Choosing a font_win32_Choosing_a_Font.
· Opening a file_win32_Opening_a_File.
· Displaying the Print dialog box_win32_Displaying_the_Print_Dialog_Box.
· Setting up the printed page_win32_Setting_Up_the_Printed_Page.
· Finding text_win32_Finding_Text.
+$#A Choosing a Color
You can let the user choose a color, such as for a new solid brush, by displaying the Color dialog box. To display the dialog box, you initialize the lStructSize, hwndOwner, lpCustColors and Flags members in a CHOOSECOLOR_win32_CHOOSECOLOR_str structure and call the ChooseColor_win32_ChooseColor function as in the following example:
CHOOSECOLOR cc; // common dialog box structure COLORREF acrCustClr[16]; // array of custom colors HWND hwnd; // owner windowHBRUSH hbrush; // brush handle// Initialize CHOOSECOLOR cc.lStructSize = sizeof(CHOOSECOLOR);cc.hwndOwner = hwnd;cc.lpCustColors = (LPDWORD) acrCustClr;cc.Flags = CC_FULLOPEN;
if (ChooseColor(&cc)==TRUE) hbrush = CreateSolidBrush(cc.rgbResult);
In this example, the CC_FULLOPEN value in the Flags member directs the dialog box to display the Add Custom Colors extension to the dialog box with which the user can create new custom colors. Custom colors are copied back into the array pointed to by lpCustColors.
The ChooseColor_win32_ChooseColor function returns TRUE if the user selected a color. The RGB value of the selected is in the rgbResult member of the structure.
+$#A Choosing a Font
You can let the user choose the attributes of font, such as when creating a new logical font, by displaying the Font dialog box. To display the dialog box, you must initialize the members in a CHOOSEFONT_win32_CHOOSEFONT_str structure and call the ChooseFont_win32_ChooseFont function as in the following example:
CHOOSEFONT cf; // common dialog box structureLOGFONT lf; // logical font structureHWND hwnd; // owner windowHFONT hfont; // new logical font handle// Initialize CHOOSEFONTcf.lStructSize = sizeof (CHOOSEFONT);cf.hwndOwner = hwnd;cf.lpLogFont = &lf;cf.Flags = CF_SCREENFONTS | CF_EFFECTS;if (ChooseFont(&cf)==TRUE) hfont = CreateFontIndirect(cf.lpLogFont);
In this example, the CF_SCREENFONTS defines the type of font to display information for and CF_EFFECTS causes the dialog box to display options to let the user select strikethrough, underlining, and color for the font. The function copies the font attributes to the lf variable, a LOGFONT_win32_LOGFONT_str structure.
+$#A Opening a File
You can let the user select the name of a file to open by displaying an Open dialog box. You initialize the lStructSize, hwndOwner, lpstrFile, and nMaxFile members of an OPENFILENAME_win32_OPENFILENAME_str structure and call the GetOpenFileName_win32_GetOpenFileName function as in the following example:
OPENFILENAME ofn; // common dialog box structurechar szFile[260]; // buffer for filenameHWND hwnd; // owner windowHANDLE hf; // file handle// Initialize OPENFILENAMEofn.lStructSize = sizeof(OPENFILENAME);ofn.hwndOwner = hwnd;ofn.lpstrFile = szFile;ofn.nMaxFile = sizeof(szFile);ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";ofn.nFilterIndex = 1;ofn.lpstrFileTitle = NULL;ofn.nMaxFileTitle = 0;ofn.lpstrInitialDir = NULL;ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;// Display the Open dialog box. if (GetOpenFileName(&ofn)==TRUE) hf = CreateFile(ofn.lpstrFile, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
In this example, the lpstrFilter member receives the address of a filter that contains two patterns that the user can choose to view all files or just text files. The nFilterIndex member specifies that the first pattern is used when the dialog box is created. The OFN_EXPLORER value ensures that the Open dialog box displays a user-interface that is similar to the Windows Explorer. The OFN_PATHMUSTEXIST and OFN_FILEMUSTEXIST values direct the function to verify the the path and filename that the user enters exists before returning.

+$#A Displaying the Print Dialog Box
You can let the user choose options for printing a document by displaying a Print dialog box. To display the dialog box for the default printer, you initialize a PRINTDLG_win32_PRINTDLG_str structure and call the PrintDlg_win32_PrintDlg function as in the following example:
PRINTDLG pd;HWND hwnd;pd.lStructSize = sizeof(PRINTDLG);pd.hwndOwner = hwnd;pd.hDevMode = NULL; // don't forget to free or store hDevModepd.hDevNames = NULL; // don't forget to free or store hDevNamespd.Flags = PD_USEDEVMODECOPIESANDCOLLATE|PD_RETURNDC; pd.nCopies = 1;if (PrintDlg(&pd)==TRUE) { // GDI calls to render output // Delete DC when done DeleteDC(pd.hDC);}
In this example, the PD_RETURNDC value directs PrintDlg to return a device context handle for the selected printer. This handle is used to render output on the printer.
+$#A Setting Up the Printed Page
You can let the user select the attributes of the printed page, such as the paper type, paper source, page orientation, and page margins by displaying a Page Setup dialog box. To display the dialog box, you initialize a PAGESETUPDLG_win32_PAGESETUPDLG_str structure and call the PageSetupDlg_win32_PageSetupDlg function as in the following example:
PAGESETUPDLG psd; // common dialog box structureHWND hwnd; // owner windowpsd.lStructSize = sizeof(PAGESETUPDLG);psd.hwndOwner = hwnd;psd.hDevMode = NULL; // don't forget to free or store hDevModepsd.hDevNames = NULL; // don't forget to free or store hDevNamespsd.Flags = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS; psd.rtMargin.top = 1000;psd.rtMargin.left = 1250;psd.rtMargin.right = 1250;psd.rtMargin.bottom = 1000;if (PageSetupDlg(&psd)==TRUE) { // check paper size and margin values here}
In this example, the PSD_MARGINS value directs the function to set the initial margin values as specified in the rtMargin member. In this case, the initial and returned margin values are expressed in thousandths of an inch. The initial paper size is the paper size specified in the device mode identified by the hDevMode member of the PAGESETUPDLG_win32_PAGESETUPDLG_str structure, or by the default printer's device mode if no printer was specified.
One of the ways an application can customize the Page Setup dialog box is to use a hook function to paint the sample page of the dialog box. You set the hook function by using the PSD_ENABLEPAGEPAINTHOOK value when creating the dialog box. The following example shows a sample hook function that draws the margin rectangle in the sample page area:
BOOL CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
LPRECT lprc;
COLORREF crMargRect;
HDC hdc, hdcOld;

switch (uMsg) {

// Drawing the margin rectangle.
case WM_PSD_MARGINRECT:
hdc = (HDC) wParam;
lprc = (LPRECT) lParam;

// Get the system highlight color.
crMargRect = GetSysColor(COLOR_HIGHLIGHT);

// Create a dash-dot pen of the system highlight color and
// select it into the DC of the sample page.
hdcOld = SelectObject(hdc, CreatePen(PS_DASHDOT, .5,
crMargRect));

// Draw the margin rectangle.
Rectangle(hdc, lprc->left, lprc->top, lprc->right,
lprc->bottom);

// Restore the previous pen to the DC.
SelectObject(hdc, hdcOld);
return TRUE;

default:
return FALSE;
}
return TRUE;
}

+$#A Finding Text
You can let the user enter a string and search options for a search by displaying a Find dialog box and monitoring messages sent by the dialog box. To display and manage the Find dialog box, you need to carry out three steps: register the FINDMSGSTRING message string, call the FindText_win32_FindText function to create and display the dialog box, and check for the registered messages in the window procedure of the window that owns the dialog box.
To register the message string, you make the following call when initializing your application:
UINT uFindReplaceMsg; // message id for FINDMSGSTRING uFindReplaceMsg = RegisterWindowMessage();
To make sure the Find dialog box operates properly when eventually displayed, you must include a call to the IsDialogMessage_win32_IsDialogMessage function in your main message loop.
To display the Find dialog box, you initialize the FINDREPLACE_win32_FINDREPLACE_str structure and call the FindText_win32_FindText function as in the following example:
FINDREPLACE fr; // common dialog box structureHWND hwnd; // owner windowCHAR szFindWhat[80]; // buffer receiving stringHWND hdlg = NULL; // handle of Find dialog boxfr.lStructSize = sizeof(FINDREPLACE);fr.hwndOwner = hwnd;fr.lpstrFindWhat = szFindWhat;fr.wFindWhatLen = 80;fr.Flags = 0;hdlg = FindText(&fr);
Note that the FINDREPLACE_win32_FINDREPLACE_str structure and the buffer for the search string should be a global or static variable. You must set the hwndOwner member to specify the window that receives the registered messages. Once the dialog box is created, you can move or manipulate it by using the returned handle.
To monitor messages sent from the dialog box, you check for the registered message and process the values passed in the FINDREPLACE structure as in the following example:
LPFINDREPLACE lpfr;if (msg == uFindReplaceMsg) { lpfr = (LPFINDREPLACE)lParam; if (lpfr->Flags & FR_FINDNEXT) { // search for string specified by lpfr->lpstrFindWhat } else if (lpfr->FR_DIALOGTERM) { // user has closed the dialog box hdlg = NULL; }}
To fully process the message, you must check the Flags member for search options, indicated by values such as FR_DOWN, FR_WHOLEWORD, and FR_MATCHCASE.
+$#AK Common Dialog Box Reference
The following functions, structures, and messages are associated with common dialog boxes in Windows 95.
+$#A Common Dialog Box Functions
Following are the functions used with common dialog boxes.
ChooseColor_win32_ChooseColorChooseFont_win32_ChooseFontCommDlgExtendedError_win32_CommDlgExtendedErrorFindText_win32_FindTextGetFileTitle_win32_GetFileTitleGetOpenFileName_win32_GetOpenFileNameGetSaveFileName_win32_GetSaveFileNamePageSetupDlg_win32_PageSetupDlgPrintDlg_win32_PrintDlgReplaceText_win32_ReplaceText
+$#A Common Dialog Box Structures
Following are the structures used with common dialog boxes.
OFNOTIFY_win32_OFNOTIFY_strPAGESETUPDLG_win32_PAGESETUPDLG_str
+$#A Common Dialog Box Messages
Following are the messages used with common dialog boxes.
CDM_GETFILEPATH_win32_CDM_GETFILEPATHCDM_GETFOLDERIDLIST_win32_CDM_GETFOLDERIDLISTCDM_GETFOLDERPATH_win32_CDM_GETFOLDERPATHCDM_GETSPEC_win32_CDM_GETSPECCDM_HIDECONTROL_win32_CDM_HIDECONTROLCDM_SETCONTROLTEXT_win32_CDM_SETCONTROLTEXTCDM_SETDEFEXT_win32_CDM_SETDEFEXTCDN_FILEOK_win32_CDN_FILEOKCDN_FOLDERCHANGE_win32_CDN_FOLDERCHANGECDN_HELP_win32_CDN_HELPCDN_INITDONE_win32_CDN_INITDONECDN_SELCHANGE_win32_CDN_SELCHANGECDN_SHAREVIOLATION_win32_CDN_SHAREVIOLATIONCDN_TYPECHANGE_win32_CDN_TYPECHANGECOLOROKSTRING_win32_COLOROKSTRINGFILEOKSTRING_win32_FILEOKSTRINGFINDMSGSTRING_win32_FINDMSGSTRINGLBSELCHSTRING_win32_LBSELCHSTRINGSETRGBSTRING_win32_SETRGBSTRINGSHAREVISTRING_win32_SHAREVISTRINGWM_CHOOSEFONT_SETFLAGS_win32_WM_CHOOSEFONT_SETFLAGSWM_CHOOSEFONT_SETLOGFONT_win32_WM_CHOOSEFONT_SETLOGFONTWM_PSD_ENVSTAMPRECT_win32_WM_PSD_ENVSTAMPRECTWM_PSD_FULLPAGERECT_win32_WM_PSD_FULLPAGERECTWM_PSD_GREEKTEXTRECT_win32_WM_PSD_GREEKTEXTRECTWM_PSD_MARGINRECT_win32_WM_PSD_MARGINRECTWM_PSD_MINMARGINRECT_win32_WM_PSD_MINMARGINRECTWM_PSD_PAGESETUPDLG_win32_WM_PSD_PAGESETUPDLGWM_PSD_YAFULLPAGERECT_win32_WM_PSD_YAFULLPAGERECT

+$#AK Common Dialog Box Library (Windows NT)
The following Windows NT overview discusses the Common Dialog Box Library. Various common dialog boxes are described, and examples are included to help you use common dialog boxes in your Win32-based applications.
+$#A About Common Dialog Boxes
{ewl msdncd, EWGraphic, group10797 0 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview About Common Dialog Boxes_win32_About_Common_Dialog_Boxes.
Common dialog boxes make it easier to develop applications based on the Microsoft® Win32® application programming interface (API). A common dialog box is a dialog box that an application displays by calling a single function rather than by supplying a dialog box procedure and a resource file containing a dialog box template. The dynamic-link library COMDLG32.DLL provides a default procedure and a default template for each type of common dialog box. Each default dialog box procedure processes messages and notifications for a common dialog box and its controls. The default dialog box template defines the appearance of a common dialog box and its controls.
In addition to simplifying the development of Win32-based applications, common dialog boxes are helpful to users because they provide a standard set of controls for performing certain operations. Once Windows developers begin using common dialog boxes extensively in their applications, users will find that they can easily perform the same operations in all applications after mastering one.
This chapter describes the various common dialog boxes and includes examples to help you use common dialog boxes in your Win32-based applications.
Following are the types of common dialog boxes in the order in which they are presented in this chapter.
Name Description
Color Displays available colors as well as controls that allow the user to define custom colors.
Font Displays lists of fonts and point sizes that correspond to available fonts. After the user selects a font, the dialog box displays sample text rendered with that font.
Open Displays a list of filenames matching any specified extensions, directories, and drives. By selecting one of the listed filenames, the user indicates the file an application should open.
Save As Displays a list of filenames matching any specified extensions, directories, and drives. By selecting one of the listed filenames, the user indicates the file an application should save.
Print Displays information about the installed printer and its configuration. By altering and selecting controls in this dialog box, the user specifies how output should be printed and starts the printing process.
Page Setup Displays the current page configuration and provides options for setting the paper orientation, size, source, and margins (when the printer driver supports these options).
Print Setup This common dialog box has been superseded by the Page Setup dialog box. Win-32 applications should not use the Print Setup dialog box.
Find Displays an edit control in which the user can type a string for an application to search. The user can specify which direction to search and can indicate whether the application should match the case of the specified string, and whether the string to match is an entire word.
Replace Displays two edit controls in which the user can type strings. The first string identifies a word or value that the application should replace, and the second string identifies the replacement word or value.

+$#A Color Dialog Box
{ewl msdncd, EWGraphic, group10797 1 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Color Dialog Box_win32_Color_Dialog_Box.
The Color dialog box contains controls that enable a user to select and create colors.
Following is a Color dialog box.
{ewc msdncd, EWGraphic, group10797 2 /a "SDK_06.bmp"}
The Basic Colors control displays up to 48 colors. The actual number of colors displayed is determined by the display driver. For example, a VGA driver displays 48 colors, and a monochrome display driver displays only 16. By using the Basic Colors control, the user can select a displayed color.
To display the Custom Colors control, the user clicks the Define Custom Colors button. The Custom Colors control displays custom colors. The user can select one of the 16 rectangles in this control and then create a new color by using one of the following methods:
· Specifying red, green, and blue (RGB) values by using the Red, Green, and Blue edit controls, and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.
· Moving the cursor in the color spectrum control (the box at the upper right of the dialog box) to select hue and saturation values, moving the cursor in the luminosity control (the rectangle to the right of the spectrum control), and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.
· Specifying hue, saturation, and luminosity (HSL) values by using the Hue, Sat, and Lum edit controls, and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.
The Color|Solid control displays the dithered and solid colors that correspond to the user's selection. (A dithered color is a color created by combining one or more pure or solid colors.) The Flags member of the CHOOSECOLOR_win32_CHOOSECOLOR_str structure contains a flag that, when specified, displays a Help button.
An application can display the Color dialog box in one of two ways: fully open or partially open. When the Color dialog box is displayed partially open, the user cannot change the custom colors.
+$#A Color Models Used by the Color Dialog Box
The Color dialog box uses two models for specifying colors: the RGB model and the HSL model. Regardless of the model used, internal storage is accomplished by use of the RGB model.
RGB Color Model
The RGB model is used to designate colors for displays and other devices that emit light. Valid red, green, and blue values are in the range 0 through 255, with 0 indicating minimum intensity and 255 indicating maximum intensity. The following illustration shows how the primary colors red, green, and blue can be combined to produce four additional colors. (For display devices, the color black results when the red, green, and blue values are set to 0; in display technology, black is the absence of all colors.)
{ewc msdncd, EWGraphic, group10797 3 /a "SDK_02.bmp"}
Following are eight colors and their associated RGB values.
Color RGB values
Red 255, 0, 0
Green 0, 255, 0
Blue 0, 0, 255
Cyan 0, 255, 255
Magenta 255, 0, 255
Yellow 255, 255, 0
White 255, 255, 255
Black 0, 0, 0

Windows stores internal colors as 32-bit RGB values. The high-order byte of the high-order word is reserved; the low-order byte of the high-order word specifies the intensity of the blue component; the high-order byte of the low-order word specifies the intensity of the green component; and the low-order byte of the low-order word specifies the intensity of the red component.
HSL Color Model
The Color dialog box provides controls for specifying HSL values. The following illustration shows the color spectrum control and the luminosity control that appear in the Color dialog box and shows the ranges of values the user can specify with these controls.
{ewc msdncd, EWGraphic, group10797 4 /a "SDK_03.bmp"}
In the Color dialog box, the saturation and luminosity values must be in the range 0 through 240, and the hue value must be in the range 0 through 239.
Converting HSL Values to RGB Values
The dialog box procedure provided in COMDLG32.DLL for the Color dialog box contains code that converts HSL values to the corresponding RGB values. Following are several colors and their associated HSL and RGB values:
Color HSL values RGB values
Red (0, 240, 120) (255, 0, 0)
Yellow (40, 240, 120) (255, 255, 0)
Green (80, 240, 120) (0, 255, 0)
Cyan (120, 240, 120) (0, 255, 255)
Blue (160, 240, 120) (0, 0, 255)
Magenta (200, 240, 120) (255, 0, 255)
White (0, 0, 240) (255, 255, 255)
Black (0, 0, 0) (0, 0, 0)

+$#A Font Dialog Box
{ewl msdncd, EWGraphic, group10797 5 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Font Dialog Box_win32_Font_Dialog_Box.
The Font dialog box contains controls that allow a user to choose a font, a font style (such as bold, italic, or regular), a point size, an effect (such as underline or strikeout), or a text color. Following is a Font dialog box.
{ewc msdncd, EWGraphic, group10797 6 /a "SDK_07.bmp"}
+$#A Open and Save As Dialog Boxes
{ewl msdncd, EWGraphic, group10797 7 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Open and Save As Dialog Boxes_win32_Open_and_Save_As_Dialog_Boxes.
The Open dialog box and the Save As dialog box are similar in appearance. Each contains controls that enable a user to specify the location (drive and directory) and the name of a file or a set of files. In the case of the Open dialog box, the user selects the file or files to be opened. In the case of the Save As dialog box, however, the user selects the file or files to be saved. Following is an Open dialog box.
{ewc msdncd, EWGraphic, group10797 8 /a "SDK_03.bmp"}
+$#A Print Dialog Box
{ewl msdncd, EWGraphic, group10797 9 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Print Dialog Box_win32_Print_Dialog_Box.
The Print dialog box contains controls that allow a user to configure a printer for a particular print job. The user can make selections, such as print quality, print range, and number of copies (if the printer supports multiple copies).
The Print dialog box differs from other common dialog boxes in that part of its dialog box procedure resides in COMDLG32.DLL and part in a printer driver. A printer driver is a program that configures a printer, converts graphics device interface (GDI) commands to low-level printer commands, and stores commands for a particular print job in a printer's queue.
A printer driver exports a function called ExtDeviceMode_win32_ExtDeviceMode that displays a dialog box and its controls. Applications written for previous versions of Windows called the LoadLibrary_win32_LoadLibrary function to load a device driver, and the GetProcAddress_win32_GetProcAddress function to obtain the address of the ExtDeviceMode function. Win32-based applications no longer need to call LoadLibrary and GetProcAddress. Instead, an application calls a single function, PrintDlg_win32_PrintDlg, to display the Print dialog box and begin a print job. The code for PrintDlg resides in COMDLG32.DLL. The dialog box that appears when an application calls PrintDlg differs slightly from the dialog box that appears when the application calls directly into the device driver. The functionality is very similar in spite of the different appearance.
+$#A Page Setup Dialog Box
{ewl msdncd, EWGraphic, group10797 10 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Page Setup Dialog Box_win32_Page_Setup_Dialog_Box.
The Page Setup dialog box allows the user to set the attibutes of the printed page, including the paper type (envelope, legal, letter, and so on), the paper source (manual feed, tractor feed, sheet feeder, and so on), the page orientation (portrait or landscape), and the width of the page margins. The attributes presented in the dialog box vary depending on the capabilities of the printer.
The dialog box includes an image of a sample page that shows how the user's selections affect the appearance of the printed output. The image consists of a rectangle that represents the selected paper or envelope type. The system uses a dotted-line rectangle to represent the current margins, and fills the rectangle with greeking characters to show how text will look on a printed page.
The system uses a multiple-step process to draw the sample page. You can customize the appearance of the sample page by specifying a hook function that the system calls at each step in the drawing process. When the system calls the hook procedure, it sends a message that contains information that the hook procedure uses to draw in a particular area within the sample page.
+$#A Find and Replace Dialog Boxes
{ewl msdncd, EWGraphic, group10797 11 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Find and Replace Dialog Boxes_win32_Find_and_Replace_Dialog_Boxes.
The Find dialog box and the Replace dialog box are similar in appearance. You can use the Find dialog box to add string-search capabilities to an application and the Replace dialog box to add both string-search and string-substitution capabilities.
The Find and the Replace dialog boxes differ from other common dialog boxes in that they are modeless. This means the user can switch between the dialog box and the window that created that dialog box.
+$#A Using Common Dialog Boxes
{ewl msdncd, EWGraphic, group10797 12 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Using Common Dialog Boxes_win32_Using_Common_Dialog_Boxes.
This section explains how to perform the following tasks:
· Using the Color dialog box to display basic colors_win32_Using_the_Color_Dialog_Box_to_Display_Basic_Colors.
· Using the Color dialog box to display custom colors_win32_Using_the_Color_Dialog_Box_to_Display_Custom_Colors.
· Displaying the Font dialog box in an application_win32_Displaying_the_Font_Dialog_Box_in_An_Application.
· Displaying the Open dialog box in an application_win32_Displaying_the_Open_Dialog_Box_in_An_Application.
· Displaying the Save As dialog box in an application_win32_Displaying_the_Save_As_Dialog_Box_in_An_Application.
· Monitoring List Box Controls in an Open or Save As dialog box!AL(`_win32_Monitoring_List_Box_Controls_in_an_Open_or_Save_As_Dialog_Box',3,`No_SDK').
· Monitoring Filenames in an Open or Save As dialog box_win32_Monitoring_Filenames_in_an_Open_or_Save_As_Dialog_Box.
· Displaying Explorer-Style Open and Save As Dialog Boxes_win32_Displaying_Explorer_Style_Open_and_Save_As_Dialog_Boxes.
· Displaying a Print dialog box for the default printer_win32_Displaying_a_Print_Dialog_Box_for_the_Default_Printer.
· Displaying the Find dialog box_win32_Displaying_the_Find_Dialog_Box.
· Displaying the Replace dialog box_win32_Displaying_the_Replace_Dialog_Box.
· Processing Messages for a Find or Replace dialog box_win32_Processing_Messages_for_a_Find_or_Replace_Dialog_Box.
· Customizing common dialog boxes_win32_Customizing_Common_Dialog_Boxes.
+$#A Using the Color Dialog Box to Display Basic Colors
You can have your application display the Color dialog box so that a user can select one color from a list of basic screen colors. This section describes how you can provide code and structures in your application to make this possible.
+$# Initializing the CHOOSECOLOR Structure to Display Basic Colors
Before displaying a Color dialog box, an application must initialize a CHOOSECOLOR_win32_CHOOSECOLOR_str structure. This structure should be global or be declared as a static variable. The members of this structure contain information about the following items:
· The structure size
· The window that owns the dialog box
· Any customization of the common dialog box by the application
· The hook function and custom dialog box template to use for a customized version of the Color dialog box
· The RGB values for the selected basic color

If your application does not customize the dialog box and you want the user to be able to select a single color from the basic colors, initialize the CHOOSECOLOR_win32_CHOOSECOLOR_str structure in the following manner.
/* Color variables */

CHOOSECOLOR cc; /* common dialog box structure */
COLORREF acrCustClr[16] = {
RGB(255, 255, 255), RGB(239, 239, 239),
RGB(223, 223, 223), RGB(207, 207, 207),
RGB(191, 191, 191), RGB(175, 175, 175),
RGB(159, 159, 159), RGB(143, 143, 143),
RGB(127, 127, 127), RGB(111, 111, 111),
RGB(95, 95, 95), RGB(79, 79, 79),
RGB(63, 63, 63), RGB(47, 47, 47),
RGB(31, 31, 31), RGB(15, 15, 15)
}; /* array of gray colors */

/* Initialize the necessary members. */

cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD) acrCustClr;
cc.Flags = CC_PREVENTFULLOPEN;

/* Call the ChooseColor function. */

if (ChooseColor(&cc)){

.
. /* Use cc.lpCustColors to select the requested color. */
.

}

In this example, the array pointed to by lpCustColors contains 16 doubleword RGB values that specify the color white, and the CC_PREVENTFULLOPEN flag is specified in the Flags member to disable the Define Custom Colors button and prevent the user from selecting a custom color.
+$#A Calling the ChooseColor Function to Display Basic Colors
After you initialize the structure, call the ChooseColor_win32_ChooseColor function. If the function succeeds and the user chooses the OK button to close the dialog box, the rgbResult member of the CHOOSECOLOR_win32_CHOOSECOLOR_str structure contains the RGB_win32_RGB values for the basic color the user selected.
+$#A Using the Color Dialog Box to Display Custom Colors
You can have your application display the Color dialog box so that the user can create and select a custom color. This section describes how you can provide code and structures in your application to make this possible.
+$# Initializing the CHOOSECOLOR Structure to Display Custom Colors
Before displaying a Color dialog box, an application must initialize a CHOOSECOLOR_win32_CHOOSECOLOR_str structure. This structure should be global or declared as a static variable. The members of this structure contain information about the following items:
· The structure size
· The window that owns the dialog box
· Any customization of the common dialog box by the application
· The hook function and custom dialog box template to use for a customized version of the Color dialog box
· The RGB_win32_RGB values for the custom color control

If your application does not customize the dialog box and you want the user to be able to create and select custom colors, initialize the CHOOSECOLOR_win32_CHOOSECOLOR_str structure in the following manner.
/* Color variables */

CHOOSECOLOR cc; /* common dialog box structure */
COLORREF acrCustClr[16] = {
RGB(255, 255, 255), RGB(239, 239, 239),
RGB(223, 223, 223), RGB(207, 207, 207),
RGB(191, 191, 191), RGB(175, 175, 175),
RGB(159, 159, 159), RGB(143, 143, 143),
RGB(127, 127, 127), RGB(111, 111, 111),
RGB(95, 95, 95), RGB(79, 79, 79),
RGB(63, 63, 63), RGB(47, 47, 47),
RGB(31, 31, 31), RGB(15, 15, 15)
}; /* array of gray colors */

/* Initialize the necessary members. */

cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD) acrCustClr;
cc.Flags = CC_FULLOPEN;

In the previous example, the array pointed to by the lpCustColors member contains sixteen 32-bit RGB values that specify 16 scales of gray, and the CC_FULLOPEN flag is specified in the Flags member to display the complete Color dialog box.
+$#A Calling the ChooseColor Function to Display Custom Colors
After you initialize the structure, you should call the ChooseColor_win32_ChooseColor function. If the function succeeds and the user chooses the OK button to close the dialog box, the lpCustColors member points to an array that contains the RGB values for the custom colors requested by the user.
You can give your application more control over custom colors by creating a new message identifier for the string defined by COLOROKSTRING . The application creates the new message identifier by calling RegisterWindowMessage_win32_RegisterWindowMessage and passing this value as the single parameter. After calling RegisterWindowMessage, the application receives a message immediately before the dialog box closes. The lParam parameter of this message contains a pointer to the CHOOSECOLOR_win32_CHOOSECOLOR_str structure. The application can use the lpCustColors member of this structure to check the current color. If the application returns a nonzero value when it processes this message, the dialog box does not close.
Similarly, you can have your application create a new message identifier for the string defined by the SETRGBSTRING value. The application's hook function can use the message identifier returned by calling RegisterWindowMessage with the SETRGBSTRING value to set a color in the dialog box. For example, the following line sets the color selection to blue.
SendMessage(hwndDlg, wSetRGBMsg, 0, (LPARAM) RGB(0, 0, 255));

In this example, the wSetRGBMsg argument is the message identifier returned by RegisterWindowMessage_win32_RegisterWindowMessage. The lParam parameter of the SendMessage_win32_SendMessage function is set to the RGB_win32_RGB values of the desired color. The wParam parameter is not used.
You can have your application specify any valid RGB values in the call to SendMessage. If the RGB values match one of the basic colors, the operating system selects the basic color and updates the spectrum and luminosity controls. If the RGB values do not match one of the basic colors, the system updates the spectrum and luminosity controls, but the basic color selection remains unchanged.
Note that if the Color dialog box is not fully open and the application sends RGB values that do not match one of the basic colors, the operating system does not update the dialog box. Updates are unnecessary because the spectrum and luminosity controls are not visible when the dialog box is only partially open.
For more information about processing registered window messages, see Displaying the Find Dialog Box_win32_Displaying_the_Find_Dialog_Box.
+$#A Displaying the Font Dialog Box in An Application
The Font dialog box appears after you initialize the members in a CHOOSEFONT_win32_CHOOSEFONT_str structure and call the ChooseFont_win32_ChooseFont function. This structure should be global or be declared as a static variable. The members of the CHOOSEFONT structure contain information about the following items:
· Which attributes of the font initially appear in the dialog box.
· Which attributes of the font the user has selected.
· Which point size of the font the user has selected.
· Whether the list of fonts corresponds to a printer, a screen, or both.
· Whether the available fonts listed are TrueType only.
· Whether the Effects box should appear in the dialog box.
· Whether dialog box messages should be processed by an application-defined hook function.
· Whether the point sizes of the selectable fonts should be limited to a specified range.
· Whether the dialog box displays only what-you-see-is-what-you-get (WYSIWYG) fonts. (These fonts are resident on both the screen and the printer.)
· Which color ChooseFont_win32_ChooseFont uses to render text in the Sample box the first time the application displays the dialog box.
· Which color the user selected for text output.
To display the Font dialog box, include the following steps in your application:
1. If the application requires printer fonts, obtain a device context (DC) handle for the printer and use this handle in the hDC member of the CHOOSEFONT_win32_CHOOSEFONT_str structure. (If the Font dialog box displays only screen fonts, this member should be NULL.)
2. Use appropriate flags in the Flags member of the CHOOSEFONT structure. They must include CF_SCREENFONTS, CF_PRINTERFONTS, or CF_BOTH.
3. Set the rgbColors member of the CHOOSEFONT structure if the default color (black) is not appropriate.
4. Set the nFontType member of the CHOOSEFONT structure by using the appropriate constant.
5. Set the nSizeMin and nSizeMax members of the CHOOSEFONT_win32_CHOOSEFONT_str structure if the CF_LIMITSIZE flag is specified in the Flags member.
6. Call the ChooseFont_win32_ChooseFont function.

The following example initializes the CHOOSEFONT structure, calls ChooseFont, and uses the requested font to draw text on the display.
/* Font variables */

CHOOSEFONT cf; /* common dialog box structure */
LOGFONT lf; /* logical font structure */
HDC hdc; /* display DC handle */
HFONT hfont; /* new logical font handle */
HFONT hfontOld; /* original logical font handle */
COLORREF crOld; /* original text color */

/* Initialize the necessary members. */

cf.lStructSize = sizeof (CHOOSEFONT);
cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
cf.rgbColors = RGB(0, 255, 255);
cf.nFontType = SCREEN_FONTTYPE;

/*
* Display the dialog box, allow the user to
* choose a font, and render the text in the
* window with that selection.
*/

if (ChooseFont(&cf)){
hdc = GetDC(hwnd);
hfont = CreateFontIndirect(cf.lpLogFont);
hfontOld = SelectObject(hdc, hfont);
crOld = SetTextColor(hdc, cf.rgbColors);
TextOut(hdc, 50, 150,
"AaBbCcDdEeFfGgHh", 16);
SetTextColor(hdc, crOld);
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
ReleaseDC(hwnd, hdc);
}

An application can also use the WM_CHOOSEFONT_GETLOGFONT_win32_WM_CHOOSEFONT_GETLOGFONT message to retrieve the current LOGFONT_win32_LOGFONT_str structure for the Font dialog box before the user closes the dialog box.
+$#A Displaying the Open Dialog Box in An Application
The Open dialog box appears after you initialize the members of an OPENFILENAME_win32_OPENFILENAME_str structure and call the GetOpenFileName_win32_GetOpenFileName function.
Before you call GetOpenFileName, structure members contain certain data, such as the name of the directory and the filter that appears in the dialog box. (A filter is a filename extension. The common dialog box code uses the extension to filter appropriate filenames from a directory.) After the call, structure members contain other data, such as the name of the selected file and the number of characters in that filename.
To display an Open dialog box, include the following steps in your application:
1. Store the valid filters in a character array.
2. Set the lpstrFilter member to point to this array.
3. Set the nFilterIndex member to the value of the index that identifies the default filter.
4. Set the lpstrFile member to point to an array that contains the initial filename and receives the selected filename.
5. Set the nMaxFile member to the value that specifies the length of the filename array.
6. Set the lpstrFileTitle member to point to a buffer that receives the title of the selected file.
7. Set the nMaxFileTitle member to specify the length of the buffer.
8. Set the lpstrInitialDir member to point to a string that specifies the initial directory. (If this member does not point to a valid string, it must be set to zero or point to a string set to NULL.)
9. Set the lpstrTitle member to point to a string specifying the name that appears in the title bar of the dialog box. (If this pointer is NULL, the title is Open by default.)
10. Initialize the lpstrDefExt member to point to the default extension. (This extension can be 0, 1, 2, or 3 characters long.)
11. Call the GetOpenFileName_win32_GetOpenFileName function.

The following example initializes an OPENFILENAME_win32_OPENFILENAME_str structure, calls GetOpenFileName, and opens the file by using the lpstrFile member of the structure. The OPENFILENAME structure should be global or be declared as a static variable.
OPENFILENAME ofn; /* common dialog box structure */
char szDirName[MAX_PATH]; /* directory string */
char szFile[260]; /* filename string */
char szFileTitle[260]; /* file title string */
char szFilter[260]; /* filter string */
char chReplace; /* string separator for szFilter */
int i, cbString; /* integer count variables */
HANDLE hf; /* file handle */

.
.
.
/*
* Obtain the system directory name and
* store it in szDirName.
*/

GetSystemDirectory(szDirName, sizeof(szDirName));

/* Place the terminating null character in the szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,
szFilter, sizeof(szFilter));

/*
* Add a terminating null character to the filter string.
*/

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++){
if (szFilter[i] == chReplace)
szFilter[i] = '\0';
}

/* Set the members of the OPENFILENAME structure. */

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrInitialDir = szDirName;
ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
OFN_FILEMUSTEXIST;

/* Display the Open dialog box. */

if (GetOpenFileName(&ofn)) {

/* Open the file. */

hf = CreateFile(ofn.lpstrFile, GENERIC_READ,
0, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

.
. /* Perform the necessary file operations. */
.

/* Close the file. */

CloseHandle(hf);

} /* end if */

The string referred to by the IDS_FILTERSTRING value in the preceding example is defined as follows in the resource-definition file.
STRINGTABLE
BEGIN
IDS_FILTERSTRING "Bitmap File(*.BMP)|*.bmp|"
END

The vertical bars in this string are used as wildcards. After the LoadString_win32_LoadString function is used to retrieve the string, the wildcards are replaced with NULL. The wildcard can be any unique character and must be included as the last character in the string. Initializing strings in this manner guarantees that the parts of the string are contiguous in memory and that the string is terminated with two null characters.
An application that can open files over a network can create a new message identifier for the string defined by the SHAREVISTRING value. The application creates the new message identifier by calling RegisterWindowMessage_win32_RegisterWindowMessage and passing this value as the single parameter. After calling RegisterWindowMessage, the application is notified whenever a sharing violation occurs during a call to the OpenFile_win32_OpenFile function. For more information about processing registered window messages, see Displaying the Find Dialog Box_win32_Displaying_the_Find_Dialog_Box.
+$#A Displaying the Save As Dialog Box in An Application
The Save As dialog box appears after you initialize the members of an OPENFILENAME_win32_OPENFILENAME_str structure and call the GetSaveFileName_win32_GetSaveFileName function.
Before you call GetSaveFileName, structure members contain certain data, such as the name of the initial directory and a filter string. After the call, structure members contain other data, such as the name of the file to be saved and the number of characters in that filename.
The following example initializes an OPENFILENAME structure, calls GetSaveFileName, and saves the file. The OPENFILENAME structure should be global or be declared as a static variable.
OPENFILENAME ofn; /* common dialog box structure */
char szDirName[MAX_PATH]; /* directory string */
char szFile[260]; /* filename string */
char szFileTitle[260]; /* file title string */
char szFilter[260]; /* filter string */
char chReplace; /* string separator for szFilter */
int i, cbString; /* integer count variables */
HANDLE hf; /* file handle */

/*
* Obtain the system directory name and
* store it in szDirName.
*/

GetSystemDirectory(szDirName, sizeof(szDirName));

/* Place the terminating null character in szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,
szFilter, sizeof(szFilter));

/*
* Add a terminating null character to the filter string.
*/

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++){
if (szFilter[i] == chReplace)
szFilter[i] = '\0';
}

/* Set the members of the OPENFILENAME structure. */

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrInitialDir = szDirName;
ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;

/* Display the Save As dialog box. */

if (GetSaveFileName(&ofn)) {

.
. /* Perform the necessary file operations. */
.

/* Close the file. */

CloseHandle(hf);

} /* end if */

The string referred to by the IDS_FILTERSTRING value in the preceding example is defined in the resource-definition file. It is used in exactly the same way as the IDS_FILTERSTRING value discussed in Displaying the Open Dialog Box in An Application_win32_Displaying_the_Open_Dialog_Box_in_An_Application.
+$# Monitoring List Box Controls in an Open or Save As Dialog Box
An application can monitor list box selections to process and display data in custom controls. For example, an application can use a custom control to display the total length, in bytes, of all files selected in the File Name box. One method that the application can use to obtain this value is to recompute the total count of bytes each time the user selects a file or cancels the selection of a file. A faster method is for the application to use the LBSELCHSTRING message to identify a new selection and to add the corresponding file length to the value that appears in the custom control. (Note that, in this example, the custom control is a standard Windows control that is identified in a resource file template for one of the common dialog boxes.)
An application uses RegisterWindowMessage_win32_RegisterWindowMessage to register the selection change message. After registering the message, the application uses this function's return value to identify messages from the dialog box. The message is processed in the application-defined hook function for the common dialog box. The wParam parameter of each message identifies the list box in which the selection occurred. The low-order word of the lParam parameter identifies the list box item. The high-order word of the lParam parameter is one of the following values.
Value Meaning
CD_LBSELCHANGE The item identified by the low-order word of lParam was the item in a single-selection list box.
CD_LBSELSUB The item identified by the low-order word of lParam is no longer selected in a multiple-selection list box.
CD_LBSELADD The item identified by the low-order word of lParam was selected from a multiple-selection list box.
CD_LBSELNOITEMS No items exist in a multiple-selection list box.

For an example that registers a common dialog box message, see Displaying the Find Dialog Box_win32_Displaying_the_Find_Dialog_Box.
+$#A Monitoring Filenames in an Open or Save As Dialog Box
An application can alter the normal processing of an Open or Save As dialog box by monitoring the filename the user types and by performing other unique operations. For example, one application can prevent the user from closing the dialog box if the selected filename is prohibited. Another application, however, can enable the user to select multiple filenames.
To monitor filenames, register the FILEOKSTRING message. Do this by calling the RegisterWindowMessage_win32_RegisterWindowMessage function and passing the message name as its single parameter. After the message has been registered, the dialog box procedure in COMDLG32.DLL uses it to signal that the user has selected a filename and chosen the OK button and that the dialog box has checked the filename and is ready to return. The dialog box procedure signals these actions by sending the message to the application's hook function. After receiving the message, the hook function should return a value to the dialog box procedure that called it. If the hook function did not process the message, it should return 0; if the hook function processed the message and the dialog box should close, the hook function should return 0; and if the hook function processed the message but the dialog box should not close, the hook function should return 1. All other return values are reserved.
+$#A Displaying Explorer-Style Open and Save As Dialog Boxes
If you specify OFN_EXPLORER in the Flags member of the OPENFILENAME_win32_OPENFILENAME_str structure used to create the Open or Save As common dialog box, the system creates a dialog box that incorporates many of the user-interface features of the Windows Explorer. In addition, specifying OFN_EXPLORER allows you to customize the Open and Save As dialog boxes by adding a dialog template that includes the items that you want to add to the dialog box.
The OFN_EXPLORER value affects the way that the system interprets the hInstance, lpfnHook, and lpTemplateName members of the OPENFILENAME structure. If the Flags member includes OFN_ENABLETEMPLATE, the lpTemplateName member must point to the name of a dialog template resource in the module identified by hInstance. The dialog template must have the WS_CHILD style, and should also have the DS_3DLOOK style to ensure that the child dialog box incorporates the same user interface style as the common dialog box. The system uses the dialog template to create a child dialog box and positions it at the upper left corner of the standard file dialog box, growing the child dialog box if necessary to fit all controls for both dialog boxes.
If the child dialog box has a control with a identifier equal to stc32, the system attempts to place all of the standard dialog box controls within the stc32 control. If the stc32 control is too small to fit the standard controls, the system moves other controls in the child dialog box to the right of or below the stc32 control to make room ¾ you don't need to know the exact dimensions of the common dialog box. If there is no stc32 control, the system positions all controls defined by the child dialog template below the standard controls. The system resizes the stc32 control to the size of the standard dialog box.
If the Flags member includes OFN_ENABLETEMPLATEHANDLE, the hInstance member is the memory handle of the dialog template. The system uses this template exactly as it uses the dialog template specified in lpTemplateName when Flags includes OFN_ENABLETEMPLATE, as described in the previous paragraphs.
The system communicates with the child dialog box by sending messages to the dialog procedure of the child dialog box. To specify the dialog box procedure, you must include OFN_ENABLEHOOK in the Flags member of the OPENFILENAME_win32_OPENFILENAME_str structure, and specify the address the procedure in the lpfnHook member.
When the system creates the dialog box, it sends WM_INITDIALOG_win32_WM_INITDIALOG message to the child dialog procedure specified in the lpfnHook member. The child dialog procedure receives the message before the system has moved any controls. The lParam parameter specifies the address of the OPENFILENAME structure that was passed to the GetOpenFileName_win32_GetOpenFileName or GetSaveFileName_win32_GetSaveFileName function.
The system also sends notification messages to the child dialog procedure to notify it of user and system activity affecting the child dialog box. The notification messages, sent using the WM_NOTIFY message, consist of the following:
Notification message Description
CDN_FILEOK The user click the Open or Save button; the common dialog box is about to close.
CDN_FOLDERCHANGE A new folder has been opened in the Open or Save As dialog box.
CDN_HELP The user clicked the Help button.
CDN_INITDONE The system has finished initializating the dialog box, and the dialog box has finished processing the WM_INITDIALOG_win32_WM_INITDIALOG message. Also, the system has finished arranging controls in the common dialog box to make room for the controls of the child dialog box.
CDN_SELCHANGE The user has selected a new file or folder from the file list.
CDN_SHAREVIOLATION The common dialog box encountered a sharing violation on the file about to be returned.
CDN_TYPECHANGE The user selected a new file type from the "Files of type" combo box.

If the child dialog procedure needs to return a value to the common dialog box, the procedure must use the SetWindowLong_win32_SetWindowLong function to set the DWL_MSGRESULT value.
An application can send messages to an explorer-style Open or Save As dialog box to retrieve information about the status of the dialog box. The messages include:
Message Description
CDM_GETFILEPATH Retrieves the path and filename of the selected file.
CDM_GETFOLDERIDLIST Retrieves the address of the item identifier list corresponding to the folder that the dialog box currently has open.
CDM_GETFOLDERPATH Retrieves the path and name of the currently open folder or directory for the dialog box.
CDM_GETSPEC Retrieves the filename (not including the path) of the currently selected file in the dialog box.

+$#A Displaying a Print Dialog Box for the Default Printer
To display a Print dialog box for the default printer, initialize a PRINTDLG_win32_PRINTDLG_str structure and then call the PrintDlg_win32_PrintDlg function.
The members of the PRINTDLG structure can contain information about the following items:
· The printer device context
· The values that should appear in the dialog box controls
· The hook function and custom dialog box template to use for a customized version of the Print dialog box or Print Setup dialog box

To display a Print dialog box for the currently installed printer, include the following steps in your application:
1. Use the PD_RETURNDC flag in the Flags member of the PRINTDLG_win32_PRINTDLG_str structure. This flag should be specified only if the application requires a device context (DC) handle.
2. Initialize the lStructSize, hDevMode, and hDevNames members.
3. Call the PrintDlg_win32_PrintDlg function and pass a pointer to the PRINTDLG structure just initialized.

Using the PD_RETURNDC flag causes PrintDlg to display the Print dialog box and return a handle identifying a printer device context in the hDC member of the PRINTDLG_win32_PRINTDLG_str structure. The application passes the DC handle as the first parameter to the graphics device interface (GDI) functions that render output on the printer.
+$#A Displaying the Page Setup Dialog Box
The Page Setup dialog box allows the user to set the attibutes of the printed page, including the paper type, the paper source, the page orientation, and the width of the page margins. To display the Page Setup dialog box, initialize a PAGESETUPDLG_win32_PAGESETUPDLG_str structure and call the PageSetupDlg_win32_PageSetupDlg function.
One of the ways an application can customize the Page Setup dialog box is to use a hook function to paint the sample page of the dialog box. To use such a hook function, you must specify the PSD_ENABLEPAGEPAINTHOOK flag in the Flags member of the PAGESETUPDLG structure, and the name of your hook function in the lpfnPagePaintHook member.
After the dialog box is created, the system makes a sequence of calls to the hook function. Each call corresponds to a step in the drawing process, and includes a message that identifies the area of the sample page to be drawn, and the device context for the sample page. By processing these messages, the hook function can draw in one or more areas of the sample page. For a list of messages, see the description of the PAGESETUPDLG_win32_PAGESETUPDLG_str structure.
The following example creates a Page Setup dialog box and uses a hook function to draw the margin rectangle in the sample page area:
void DoPage(HWND hwnd, RECT rcMarg, RECT rcMinMarg, POINT ptPaperSize)
{
PAGESETUPDLG psd = {0};

psd.lStructSize = sizeof(PAGESETUPDLG);
psd.hwndOwner = hwnd;
psd.hDevMode = hDevMode;
psd.hDevNames = hDevNames;
psd.Flags = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS |
PSD_MINMARGINS | PSD_ENABLEPAGEPAINTHOOK;
psd.ptPaperSize = ptPaperSize;
psd.rtMargin = rcMarg;
psd.rtMinMargin = rcMinMarg;
psd.hInstance= g_hinst;
psd.lCustData = 0;
psd.lpfnPagePaintHook = PaintHook;

PageSetupDlg(&psd);
}

BOOL CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
LPRECT lprc;
COLORREF crMargRect;
HDC hdc, hdcOld;

switch (uMsg) {

// Drawing the margin rectangle.
case WM_PSD_MARGINRECT:
hdc = (HDC) wParam;
lprc = (LPRECT) lParam;

// Get the system highlight color.
crMargRect = GetSysColor(COLOR_HIGHLIGHT);

// Create a dash-dot pen of the system highlight color and
// select it into the DC of the sample page.
hdcOld = SelectObject(hdc, CreatePen(PS_DASHDOT, .5,
crMargRect));

// Draw the margin rectangle.
Rectangle(hdc, lprc->left, lprc->top, lprc->right,
lprc->bottom);

// Restore the previous pen to the DC.
SelectObject(hdc, hdcOld);
return TRUE;

default:
return FALSE;
}
return TRUE;
}

+$#A Displaying the Find Dialog Box
The Find dialog box contains controls that allow a user to specify the following items:
· Which string the application should find
· Whether the string specifies a complete word or part of a word
· Whether the application should match the case of the specified string
· Which direction the application should search (preceding or following the current cursor location)
· Whether the application should resume the search, searching for the next occurrence of the string
To display the Find dialog box, initialize a FINDREPLACE_win32_FINDREPLACE_str structure and call the FindText_win32_FindText function. Members of the FINDREPLACE structure contain information about the following items:
· Which window owns the dialog box
· How the application should perform the search
· Which character buffer should receive the string

To initialize the FINDREPLACE_win32_FINDREPLACE_str structure, perform the following tasks:
1. Set the lStructSize member by using the sizeof operator.
2. Set the hwndOwner member by using the handle that identifies the owner window of the dialog box.
3. If you are customizing the Find dialog box, set the hInstance member to identify the instance of the module that contains your custom dialog box template.
4. Set the Flags member to indicate the selection state of the dialog box options. (For example, specifying the FR_NOUPDOWN flag disables the Up and Down buttons, specifying the FR_NOWHOLEWORD flag disables the Match Whole Word Only check box, and specifying the FR_NOMATCHCASE flag disables the Match Case check box.)
5. If you are supplying a custom dialog box template or hook function, specify additional flags in the Flags member.
6. Set the lpstrFindWhat member to point to the buffer receiving the string to be found.
7. Set the wFindWhatLen member to specify the size, in bytes, of the buffer pointed to by lpstrFindWhat.
8. Set the lCustData member with any custom data your application may need to access.
9. If your application customizes the Find dialog box, set the lpfnHook member to point to your hook function.
10. If your application uses a custom dialog box template, set the lpTemplateName member to point to the string that identifies the template.

The following example initializes the FINDREPLACE_win32_FINDREPLACE_str structure and then calls FindText_win32_FindText. This structure should be global or be declared as a static variable.
/* Find/Replace variables */

FINDREPLACE fr; /* common dialog box structure */
FINDREPLACE FAR* lpfr; /* pointer to dialog box structure */
CHAR szFindWhat[80]; /* buffer receiving string */
HWND hdlg; /* handle of dialog window */
UINT uFindReplaceMsg; /* returned by RegisterMessage() */

/* Initialize the necessary members. */

fr.lStructSize = sizeof(FINDREPLACE);
fr.hwndOwner = hwnd;
fr.lpstrFindWhat = szFindWhat;
fr.wFindWhatLen = sizeof(szFindWhat);

/* Display the modeless Find dialog box. */

hdlg = FindText(&fr);

+$#A Displaying the Replace Dialog Box
The Replace dialog box is similar to the Find dialog box. However, the Replace dialog box has no Direction check box and has three additional controls that enable the user to specify the following:
· Which string to replace
· Whether the application should replace the occurrence of the string currently highlighted
· Whether the application should replace all occurrences of the string
To display the Replace dialog box, initialize a FINDREPLACE_win32_FINDREPLACE_str structure and call the ReplaceText_win32_ReplaceText function.
+$#A Processing Messages for a Find or Replace Dialog Box
The Find and Replace dialog boxes differ from the other common dialog boxes in two respects: they are modeless and their respective dialog box procedures send messages to the application that calls the FindText_win32_FindText or ReplaceText_win32_ReplaceText function. These messages contain data specified by the user in the dialog box controls, including which direction the application should search for a string, whether the application should match the case of the specified string, and whether the application should match the entire string.
To process messages from a Find or Replace dialog box, an application must register the dialog box's unique message, FINDMSGSTRING.
The application registers this message with the RegisterWindowMessage_win32_RegisterWindowMessage function. After the application registers the message, it uses the function's return value to identify messages from the Find or Replace dialog box. The following example registers the message with RegisterWindowMessage.
/* Register the FindReplace message. */

uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING);

After the application registers this message, it can process messages for the Find or Replace dialog box by using the RegisterWindowMessage_win32_RegisterWindowMessage function's return value. The following example processes messages for the Find dialog box and then calls its own SearchFile function to locate the string of text. If the user is closing the dialog box (that is, if the Flags member of the FINDREPLACE_win32_FINDREPLACE_str structure is FR_DIALOGTERM), the handle is invalidated and the procedure returns zero.
if (message == uFindReplaceMsg){

/*
* Extract a pointer to the FINDREPLACE
* structure from lParam.
*/

lpfr = (FINDREPLACE FAR*) lParam;

/*
* If the system has set the FR_DIALOGTERM
* flag, invalidate the handle identifying
* the dialog box.
*/

if (lpfr->Flags & FR_DIALOGTERM){
hdlg = NULL;
return 0;
}

/*
* If the dialog box is still valid, call the
* application-defined search routine (SearchFile) to
* search for the requested string.
*/

SearchFile((BOOL) (lpfr->Flags & FR_DOWN),
(BOOL) (lpfr->Flags & FR_MATCHCASE));
return 0;

}

+$#A Customizing Common Dialog Boxes
A custom common dialog box is a common dialog box altered to suit a particular Windows-based application. The customization may be complex and include the hiding of original controls, the addition of new controls, or a change in the size of the original dialog box; or it may be simple, involving the alteration of a single existing control.
If you want to customize a common dialog box, you must provide a special hook function and, in most cases, a custom dialog box template. Customizations of this kind require a significant amount of additional code ¾ displaying a customized common dialog box is not as simple as initializing the members of a structure and calling a single function.
Applications that subclass controls in any of the common dialog boxes must do so while processing the WM_INITDIALOG_win32_WM_INITDIALOG message in the application's hook function. This method allows the application to receive the control-specific messages first, because it subclasses the control after the common dialog box has installed its subclassing procedures. (The previous hook function should be called for all messages not handled by the application's subclass function, as is standard for subclassing.)
An application cannot subclass a control by defining a local class to override a specific control type. This is not possible because the data segment will not be initialized correctly when the class is called ¾ the data segment will be the common dialog box's data segment, not the application's data segment.
+$#A Appropriate and Inappropriate Customizations
From the user's perspective, the chief benefit of the common dialog box is its consistent appearance and functionality from application to application. Therefore, it is important that you customize a common dialog box only when it is absolutely necessary for an application. Otherwise, the consistent appearance and simple coding interface are lost. Appropriate customizations leave intact as many of the original controls as possible. Increasing the size of the dialog box or adding new controls in available space already in the dialog box is an appropriate customization. Hiding original controls or otherwise changing the intended functionality of the original controls is a less appropriate customization.
+$#A Hook Functions and Custom Dialog Box Templates
{ewl msdncd, EWGraphic, group10797 13 /a "SDK.BMP"} Windows NT OverviewFor more information, see the Windows 95 overview Hook Functions and Custom Dialog Box Templates_win32_Hook_Functions_and_Custom_Dialog_Box_Templates.
Each common dialog box uses the dialog box procedure and dialog box template provided for it in COMDLG32.DLL. The dialog box procedure processes messages and notifications for the common dialog box and its controls. The dialog box template defines the appearance of the dialog box ¾ its dimensions, its location, and the dimensions and locations of controls within it.
In addition to the procedure and template provided, a custom dialog box requires a hook function that you provide and, usually, a custom version of the dialog box template.
+$#A The Hook Function
{bmc bm0.BMP} Windows NT OverviewFor more information, see the Windows 95 overview Hook Function_win32_Hook_Function.
The dialog box procedure provided in COMDLG32.DLL for a common dialog box calls the application's hook function if the application specifies the appropriate flag and pointer in the structure for that common dialog box. The structure for each common dialog box contains a Flags member that specifies whether the application defines a hook function and contains an lpfnHook member that points to the hook function, if one exists. If the application sets the Flags member to indicate that a hook function exists, it must also set the lpfnHook member. The following example sets the Flags and lpfnHook members of an OPENFILENAME_win32_OPENFILENAME_str structure to support an application's hook function.
#include <windows.h>
#include <commdlg.h>
#include <dlgs.h>
#include "generic.h"

OPENFILENAME ofn; /* common dialog box structure */
char szDirName[256]; /* directory string */
char szFile[256]; /* filename string */
char szFileTitle[256]; /* file title string */
char szFilter[256]; /* filter string */
char chReplace; /* string separator for szFilter */
int i, cbString; /* integer count variables */
DWORD cBufLen; /* dword count */
DWORD cTmp; /* dword count */
HANDLE hf; /* original file handle */
HANDLE hfBackup; /* backup file handle */
char szBuffer[256]; /* character buffer */
char szChar[256]; /* character buffer */
char* pszBackupFile; /* pointer to backup filename */
BOOL fBackup; /* TRUE if user checked "backup" */

/* Obtain the system directory name. */

GetSystemDirectory((LPSTR)szDirName, sizeof(szDirName));

/* Place the terminating null character in the szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,
szFilter, sizeof(szFilter));

/* Add a terminating null character to the string. */

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++) {
if (szFilter[i] == chReplace)
szFilter[i] = '\0';
}

/* Initialize the required OPENFILENAME members. */

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.hInstance = hinst;
ofn.lpstrFilter = szFilter;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrInitialDir = szDirName;
ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
OFN_FILEMUSTEXIST | OFN_ENABLEHOOK |
OFN_ENABLETEMPLATE;
ofn.lpfnHook = FileOpenHookProc;
ofn.lpTemplateName = "FILEOPEN";

In the previous example, the address of the application-defined hook function is assigned to the lpfnHook member of the OPENFILENAME_win32_OPENFILENAME_str structure. The hook function processes any messages or notifications the custom dialog box requires. With the exception of one message (WM_INITDIALOG_win32_WM_INITDIALOG), the hook function receives messages and notifications before the dialog box procedure provided in COMDLG32.DLL receives them. In the case of WM_INITDIALOG, the hook function receives the message after the dialog box procedure. When the hook function finishes processing a message, it returns a value that indicates whether the dialog box procedure provided in COMDLG32.DLL should also process the message. If the dialog box procedure should process the message, the return value is FALSE. However, if the dialog box procedure should ignore the message, the return value is TRUE.
To process the message from the OK button after the dialog box procedure processes it, an application must post a message to itself when the OK message is received. When the application receives the message it has posted, the common dialog box procedure will have finished processing messages for the dialog box. This technique is particularly useful when working with the Find and Replace dialog boxes, because the Flags member of the FINDREPLACE_win32_FINDREPLACE_str structure does not reflect changes to the dialog box until after the messages have been processed by COMDLG32.DLL.
The following example shows a hook function for a custom Open dialog box.
UINT FAR PASCAL FileOpenHookProc(HWND hdlg, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch (msg) {

/* WM_INITDIALOG is received after commdlg
/* is processed.
*/

case WM_INITDIALOG:

return TRUE;

case WM_COMMAND:

/* Use IsDlgButtonChecked to set lCustData. */

if (wParam == IDOK)

/* Set the "backup file" flag. */

fBackup = (DWORD) IsDlgButtonChecked(hdlg,
ID_CUSTCHX);

return FALSE; /* Allow standard processing. */
break;

default:
return FALSE;

} /* end switch */

return FALSE;
}

This hook function tests a custom check box when the user chooses the OK button. If the check box is selected, the hook function sets a global variable to a nonzero value; otherwise, it sets this variable to zero.
A hook function should never call the EndDialog_win32_EndDialog function. Instead, if a hook function contains code that abnormally terminates a common dialog box, this code should pass the IDABORT value to the dialog box procedure by using the PostMessage_win32_PostMessage function, as shown in the following example.
PostMessage(hDlg, WM_COMMAND, IDABORT, (LONG) FALSE);

When a hook function posts the IDABORT value, the common dialog box function returns the value contained in the low-order word of the lParam parameter. For example, if the hook function for GetOpenFileName_win32_GetOpenFileName calls the PostMessage_win32_PostMessage function with (LONG) 100 as the last parameter, GetOpenFileName returns 100.
A hook function must be exported in an application's module-definition (.DEF) file, as shown in the following example.
NAME Generic ; application's module name

DESCRIPTION 'Sample Win32-based Application'

EXETYPE WINDOWS ; required for all Win32-based applications

STUB 'WINSTUB.EXE' ; Generates an error message if the
; application is run without
; Windows

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

EXPORTS
MainWndProc @1 ; window processing function
About @2 ; "About" processing function
FileOpenHookProc @3; common dialog box callback

+$#A Customizing a Dialog Box Template
The dialog box template provided in COMDLG32.DLL for each common dialog box contains the data the dialog box procedure uses to display that common dialog box. Most applications that customize a common dialog box also need to create a custom dialog box template to use instead of the dialog box template in COMDLG32.DLL. (A custom dialog box template is not required for all custom dialog boxes. A template would not be necessary, for instance, if an application changed a dialog box in a relatively minor way and only in an unusual situation.)
A developer should create a custom dialog box template by modifying the appropriate dialog box template in COMDLG32.DLL. Following are the template filenames and the names of their corresponding common dialog boxes.
Template filename Corresponding dialog box
COLOR.DLG Color
FILEOPEN.DLG Open (single selection)
FILEOPEN.DLG Open (multiple selection)
FINDTEXT.DLG Find
FINDTEXT.DLG Replace
FONT.DLG Font
PRNSETUP.DLG Print
PRNSETUP.DLG Print Setup

Note The custom dialog box templates listed above are defined in DLGS.H, except for COLOR.DLG, which is defined in COLORDLG.H.

The following example is from a custom dialog box template created for an Open dialog box.
FILEOPEN DIALOG LOADONCALL MOVEABLE DISCARDABLE
36, 24, 264, 134
CAPTION "Open"
STYLE WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_MODALFRAME
FONT 8, "Helv"
BEGIN
LTEXT "File &Name:", stc3, 6, 6, 76, 10
CONTROL "", edt1, "edit", ES_LEFT | ES_AUTOHSCROLL |
WS_BORDER | WS_TABSTOP | WS_CHILD | ES_OEMCONVERT |
ES_LOWERCASE, 6, 16, 90, 12
CONTROL "", lst1, "listbox", LBS_SORT | LBS_NOTIFY |
LBS_DISABLENOSCROLL | WS_VSCROLL | WS_BORDER |
WS_TABSTOP | LBS_EXTENDEDSEL | LBS_HASSTRINGS |
LBS_OWNERDRAWFIXED, 6, 32, 90, 68

LTEXT "&Directories:", -1, 110, 6, 92, 9
LTEXT "", stc1, 110, 16, 92, 9, SS_NOPREFIX
CONTROL "", lst2, "listbox", LBS_SORT | LBS_HASSTRINGS |
LBS_NOTIFY | LBS_DISABLENOSCROLL | WS_VSCROLL |
WS_CHILD | WS_BORDER | WS_TABSTOP |
LBS_OWNERDRAWFIXED, 110, 32, 92, 68

LTEXT "List Files of &Type:", stc2, 6, 102, 90, 9
CONTROL "", cmb1, "combobox", CBS_DROPDOWNLIST |
CBS_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |
WS_TABSTOP | WS_CHILD, 6, 112, 90, 36

LTEXT "Dri&ves:", stc4, 110, 102, 92, 9
CONTROL "", cmb2, "combobox", CBS_SORT |
CBS_HASSTRINGS | CBS_OWNERDRAWFIXED |
CBS_DROPDOWNLIST | WS_CHILD |
CBS_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |
WS_TABSTOP, 110, 112, 92, 68

DEFPUSHBUTTON "OK", IDOK, 208, 6, 50, 14, WS_GROUP
PUSHBUTTON "Cancel", IDCANCEL, 208, 24, 50, 14, WS_GROUP

PUSHBUTTON "&Help", pshHelp, 208, 46, 50, 14,
WS_GROUP
PUSHBUTTON "Net&work...", psh14, 208, 114, 50, 14,
WS_GROUP
AUTOCHECKBOX "&Read Only", chx1, 208, 68, 50, 12,
WS_TABSTOP | WS_GROUP

AUTOCHECKBOX "&Backup File", ID_CUSTCHX,
208, 86, 50, 12, BS_AUTOCHECKBOX | WS_CHILD |
WS_TABSTOP | WS_GROUP

END

The last entry supports the addition of a new Backup File check box immediately below the existing Read Only check box.
The custom template should be added to the application's resource file. You must use all of the unique control identifiers (that is, all identifiers whose values are other than -1) in the template, even if the dialog box does not use those controls. If you do not want to display all of the controls, you can specify coordinates for them outside the dialog box. You should also disable unwanted buttons and remove unnecessary tab stops.
+$#A Displaying the Custom Dialog Box
After your application creates the hook function and the dialog box template, it should set the members of the structure for the common dialog box being customized and call the appropriate function to display the custom dialog box.
The following example calls the GetOpenFileName_win32_GetOpenFileName function and creates a backup file if the user selects the Backup File check box in the custom Open dialog box.
/* Process the requested file. */

if (GetOpenFileName(&ofn)) {

/*
* If the user checked the Backup File Check box,
* create a backup file.
*/

if (fBackup) {

/* Open the requested file. */

hf = CreateFile(ofn.lpstrFile, GENERIC_READ,
0, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

/* Process the extension (if it exists). */

if (ofn.nFileExtension) {
for(i = 0; i < (int)ofn.nFileExtension;
i++)
szChar[i] = *ofn.lpstrFile++;
} /* end if */

else {
i = 0;
while (*ofn.lpstrFile != '\0')
szChar[i++] = *ofn.lpstrFile++;
szChar[i] = '.';
} /* end else */

/* Create the backup filename string. */

pszBackupFile = lstrcat(szChar, "BAK");

/* Create the backup file. */

hfBackup = CreateFile(pszBackupFile, GENERIC_WRITE,
0, (LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

/*
* Copy the contents of the original file
* to the new file.
*/

ReadFile(hf, szBuffer, 256, &cBufLen,
NULL);

while (cBufLen == 256) {
WriteFile(hfBackup, szBuffer, 256, &cBufLen,
NULL);
ReadFile(hf, szBuffer, 256, &cBufLen, NULL);
}

WriteFile(hfBackup, szBuffer, cBufLen, &cTmp,
NULL);

/* Close the files. */

CloseHandle(hfBackup);
CloseHandle(hf);

} /* end if */
} /* end if */

+$#A Supporting Help for Common Dialog Boxes
An application can display a Help button in any of the common dialog boxes by specifying the appropriate flag in the Flags member of the structure for a specific common dialog box. Following are the structures for the common dialog boxes and the Help flag that corresponds to each structure.
Structure Flag
OPENFILENAME_win32_OPENFILENAME_str OFN_SHOWHELP
CHOOSECOLOR_win32_CHOOSECOLOR_str CC_SHOWHELP
FINDREPLACE_win32_FINDREPLACE_str FR_SHOWHELP
CHOOSEFONT_win32_CHOOSEFONT_str CF_SHOWHELP
PRINTDLG_win32_PRINTDLG_str PD_SHOWHELP

If an application displays the Help button, it must process the user's request for help. The processing can be done either in one of the application's window procedures or in a hook function.
If the application processes the request for help in one of the application's window procedures, it must first create a new message identifier for the string defined by the HELPMSGSTRING value. The application creates the new message identifier by calling the RegisterWindowMessage_win32_RegisterWindowMessage function and passing this value as the single parameter. (For more information about processing registered window messages, see "Displaying the Find Dialog Box" earlier in this chapter.) In addition to creating a new message identifier, the application must use the hwndOwner member of the appropriate structure for the common dialog box, so this member contains the handle of the dialog box's owner window. After the message identifier is created and the hwndOwner member is specified, the dialog box procedure notifies the window procedure of the owner window whenever the user chooses the Help button.
The following example processes a user's request for help in the window procedure of its owner window. The if statement should be in the default section of the switch statement that processes messages.
MyHelpMsg = RegisterWindowMessage(HELPMSGSTRING);
.
.
.
if (message == MyHelpMsg)
WinHelp(hWnd, "appfile.hlp", HELP_CONTEXT, ID_MY_CONTEXT);

If the application processes the request for help in a hook function, it should test for the following condition in the WM_COMMAND_win32_WM_COMMAND message.
wParam == pshHelp

When this condition is true, the hook function should call the WinHelp_win32_WinHelp function, as shown in the preceding example. (To process the request for help in a hook function, you must include the DLGS.H header file in the source file that contains the hook-function code.)
+$#A Error Detection
Whenever a common dialog box function fails, an application can call the CommDlgExtendedError_win32_CommDlgExtendedError function to find out the cause of the failure. This function returns an error value that identifies the cause of the most recent error.
Six values are defined in the CDERR.H header file that identify the ranges of error values for categories of errors returned by CommDlgExtendedError. Following are these values in ascending order by value range.
Value Meaning
CDERR_GENERALCODES General error codes for common dialog boxes. These errors are in the range 0x0000 through 0x0FFF.
PDERR_PRINTERCODES Error codes for the Print common dialog box. These errors are in the range 0x1000 through 0x1FFF.
CFERR_CHOOSEFONTCODES Error codes for the Font common dialog box. These errors are in the range 0x2000 through 0x2FFF.
FNERR_FILENAMECODES Error codes for the Open and Save As common dialog boxes. These errors are in the range 0x3000 through 0x3FFF.
FRERR_FINDREPLACECODES Error codes for the Find and Replace common dialog boxes. These errors are in the range 0x4000 through 0x4FFF.
CCERR_CHOOSECOLORCODES Error codes for the Color common dialog box. These errors are in the range 0x5000 through 0x5FFF.

+$#AK Common Dialog Box Reference
The following functions, structures, and messages are associated with common dialog boxes in Windows NT.
+$#A Common Dialog Box Functions
Following are the functions used with common dialog boxes.
ChooseColor_win32_ChooseColorChooseFont_win32_ChooseFontCommDlgExtendedError_win32_CommDlgExtendedErrorFindText_win32_FindTextGetFileTitle_win32_GetFileTitleGetOpenFileName_win32_GetOpenFileNameGetSaveFileName_win32_GetSaveFileNamePageSetupDlg_win32_PageSetupDlgPrintDlg_win32_PrintDlgReplaceText_win32_ReplaceText
+$#A Common Dialog Box Structures
Following are the structures used with common dialog boxes.
OFNOTIFY_win32_OFNOTIFY_strPAGESETUPDLG_win32_PAGESETUPDLG_str
+$#A Common Dialog Box Messages
Following are the messages used with common dialog boxes.
CDM_GETFILEPATH_win32_CDM_GETFILEPATHCDM_GETFOLDERIDLIST_win32_CDM_GETFOLDERIDLISTCDM_GETFOLDERPATH_win32_CDM_GETFOLDERPATHCDM_GETSPEC_win32_CDM_GETSPECCDM_HIDECONTROL_win32_CDM_HIDECONTROLCDM_SETCONTROLTEXT_win32_CDM_SETCONTROLTEXTCDM_SETDEFEXT_win32_CDM_SETDEFEXTCDN_FILEOK_win32_CDN_FILEOKCDN_FOLDERCHANGE_win32_CDN_FOLDERCHANGECDN_HELP_win32_CDN_HELPCDN_INITDONE_win32_CDN_INITDONECDN_SELCHANGE_win32_CDN_SELCHANGECDN_SHAREVIOLATION_win32_CDN_SHAREVIOLATIONCDN_TYPECHANGE_win32_CDN_TYPECHANGECOLOROKSTRING_win32_COLOROKSTRINGFILEOKSTRING_win32_FILEOKSTRINGFINDMSGSTRING_win32_FINDMSGSTRINGLBSELCHSTRING_win32_LBSELCHSTRINGSETRGBSTRING_win32_SETRGBSTRINGSHAREVISTRING_win32_SHAREVISTRINGWM_CHOOSEFONT_SETFLAGS_win32_WM_CHOOSEFONT_SETFLAGSWM_CHOOSEFONT_SETLOGFONT_win32_WM_CHOOSEFONT_SETLOGFONTWM_PSD_ENVSTAMPRECT_win32_WM_PSD_ENVSTAMPRECTWM_PSD_FULLPAGERECT_win32_WM_PSD_FULLPAGERECTWM_PSD_GREEKTEXTRECT_win32_WM_PSD_GREEKTEXTRECTWM_PSD_MARGINRECT_win32_WM_PSD_MARGINRECTWM_PSD_MINMARGINRECT_win32_WM_PSD_MINMARGINRECTWM_PSD_PAGESETUPDLG_win32_WM_PSD_PAGESETUPDLGWM_PSD_YAFULLPAGERECT_win32_WM_PSD_YAFULLPAGERECT

+$#AK Cursors
A cursor is a small picture whose location on the screen is controlled by a pointing device, such as a mouse, pen, or trackball. (In the remainder of this section, the term mouse refers to any pointing device.) When the user moves the mouse, Microsoft® Windows® moves the cursor accordingly. Microsoft Win32® cursor functions enable applications to create, load, display, move, confine, and destroy cursors.
+$#A About Cursors
Windows provides a set of standard cursors that are available for any application to use at any time. The Windows header files contain identifiers for the standard cursors ¾ the identifiers begin with the "IDC_" prefix.
Each standard cursor has a corresponding default image associated with it. The user or an application can replace the default image associated with any standard cursor at any time. An application replaces a default image by using the SetSystemCursor_win32_SetSystemCursor function.
An application can use the GetIconInfo_win32_GetIconInfo function to retrieve the current image for a cursor, and can draw the cursor by using the DrawIconEx_win32_DrawIconEx function. To draw the default image for a standard cursor, specify the DI_COMPAT flag in the call to DrawIconEx. If you do not specify the DI_COMPAT flag, DrawIconEx draws the standard cursor using the image that the user specified.
Custom cursors are designed for use in a specific application and can be any design the developer defines. The following illustration shows several custom cursors.
{ewc msdncd, EWGraphic, group10799 0 /a "SDK_02.bmp"}
Cursors can be either monochrome or color, and either static or animated. The type of cursor used on a particular computer system depends on the system's display. Old displays such as VGA do not support color or animated cursors, but new displays (whose display drivers use the DIB engine) do support them.
Cursors and icons are similar and can be used interchangeably in many situations. The only difference between them is that an image specified as a cursor must be in the format that the display can support. For example, a cursor must be monochrome for a VGA display.
+$#A The Hot Spot
In the cursor, a pixel called the hot spot marks the exact screen location that is affected by a mouse event, such as clicking a mouse button. Typically, the hot spot is the focal point of the cursor. The system tracks and recognizes this point as the position of the cursor. For example, typical hot spots are the pixel at the tip of an arrow-shaped cursor and the pixel in the middle of a crosshair-shaped cursor.
When a mouse input event occurs, the Windows mouse driver translates the event into an appropriate mouse message that includes the coordinates of the hot spot. Windows sends the mouse message to the window that contains the hot spot or to the window that is capturing mouse input. For more information about mouse input, see Mouse Input_win32_Mouse_Input.
+$#A The Mouse and the Cursor
If the system includes a mouse, Windows reflects the movement of the mouse by moving the cursor on the screen accordingly. As the cursor moves over different parts of windows or into different windows, Windows (or an application) changes the appearance of the cursor. For example, when the cursor crosses a window border, Windows changes the cursor into a two-headed arrow.
If the system does not have a mouse, Windows displays and moves the cursor only when the user chooses certain system commands, such as those used to size or move a window. To provide the user with a method of displaying and moving the cursor when a mouse isn't available, an application can use the cursor functions to simulate mouse movement. Given this simulation capability, the user can use the arrow keys to move the cursor.
+$#A Cursor Creation
Because standard cursors are predefined, it is not necessary to create them. To use a standard cursor, an application retrieves a cursor handle by using the LoadCursor_win32_LoadCursor or LoadImage_win32_LoadImage function. A cursor handle is a unique value of the HCURSOR type that identifies a standard or custom cursor.
To create a custom cursor for an application, developers typically use a graphics application and include the cursor as a resource in the application's resource-definition file. At run time, an application calls LoadCursor to retrieve the cursor handle. Cursor resources contain data for several different display devices. LoadCursor automatically selects the most appropriate data for the current display device.
An application can also create a custom cursor at run time by using the CreateIconIndirect_win32_CreateIconIndirect function, which creates a cursor based on the content of an ICONINFO_win32_ICONINFO_str structure. The GetIconInfo_win32_GetIconInfo function fills this structure with hot spot coordinates and information concerning the associated bitmask and color.
Applications should implement custom cursors as resources and use LoadCursor or LoadImage rather than create the cursor at run time. Using cursor resources avoids device dependence, simplifies localization, and enables applications to share cursor designs.
The CreateIconFromResourceEx_win32_CreateIconFromResourceEx function enables an application to create icons and cursors based on resource data. CreateIconFromResourceEx creates a cursor based on binary resource data from other executable (.EXE) files or dynamic-link libraries (DLLs). It must be preceded by calls to the LookupIconIdFromDirectoryEx_win32_LookupIconIdFromDirectoryEx function, as well as several resource functions. LookupIconIdFromDirectoryEx identifies the most appropriate cursor data for the current display device. For more information about resource functions, see Resources_win32_Resources.
+$#A Cursor Location and Appearance
If the system includes a mouse, Windows automatically displays a cursor and updates its position on the screen. Applications can obtain current screen coordinates of the cursor; applications move the cursor to any location on the screen by using the GetCursorPos_win32_GetCursorPos and SetCursorPos_win32_SetCursorPos functions, respectively.
Applications can also retrieve the handle of the current cursor by using the GetCursor_win32_GetCursor function and can set the cursor by using the SetCursor_win32_SetCursor function. After an application uses SetCursor, the appearance of the cursor does not change until the mouse moves, until the cursor is explicitly set to a different cursor, or until a system command is executed.
When the user moves the mouse, the system redraws the cursor at the new location as designated by the mouse. The system automatically redraws the cursor design associated with the window to which the cursor is pointing.
Applications can hide and redisplay the cursor, without changing the cursor design, by using the ShowCursor_win32_ShowCursor function. This function uses an internal counter to determine when to hide or display the cursor. An attempt to show the cursor increments the counter; an attempt to hide the cursor decrements the counter. The cursor is visible only if this counter is greater than or equal to zero.
+$#A The Window Class Cursor
When registering a window class, an application can assign it a default cursor, known as the class cursor. After the application registers the window class, each window of that class will have the specified class cursor.
To override the class cursor, an application can process the WM_SETCURSOR_win32_WM_SETCURSOR message. Applications can also replace a class cursor by using the SetClassLong_win32_SetClassLong function. This function changes the default window settings for all windows of a given class. For more information about window classes, see Window Classes_win32_Window_Classes.
+$#A Cursor Confinement
Applications can confine the cursor to a rectangular area on the screen by using the ClipCursor_win32_ClipCursor function. This can be useful when the user must respond to a certain event within the confined area of the rectangle. For example, an application might use ClipCursor to confine the cursor to a modal dialog box, preventing the user from interacting with other windows until the dialog box is closed.
The GetClipCursor_win32_GetClipCursor function retrieves the screen coordinates of the rectangular area to which the cursor is temporarily confined. When it is necessary to confine the cursor, the application can also use this function to save the coordinates of the original area in which the cursor can move. Then, the application can restore the cursor to the original area when the new confinement is no longer necessary.
+$#A Cursor Destruction
When an application no longer needs a cursor it created by using the CreateIconIndirect_win32_CreateIconIndirect function, it should destroy the cursor. The DestroyCursor_win32_DestroyCursor function destroys the cursor handle and frees any memory that it used. Applications should use this function only on cursors that were created with CreateIconIndirect; it is not necessary to destroy other cursors.
The DestroyCursor function has no effect on a shared cursor; that is, a cursor that was loaded by using the LR_SHARED flag with the LoadImage_win32_LoadImage function. A shared cursor is valid as long as the module from which it was loaded remains in memory.
+$#A Cursor Duplication
The CopyCursor_win32_CopyCursor function copies a cursor handle. This enables an application or DLL to retrieve the handle of a cursor owned by another module. Then, if the other module is freed, the application that copied the cursor will still be able to use the cursor design.
For information on how to add, remove, or replace cursor resources in executable files, see Resources_win32_Resources.
+$#A Using Cursors
· Creating a cursor_win32_Creating_a_Cursor
· Displaying a cursor_win32_Displaying_a_Cursor
· Confining a cursor_win32_Confining_a_Cursor
· Using cursor functions to create a mousetrap_win32_Using_Cursor_Functions_to_Create_a_Mousetrap
· Using the keyboard to move the cursor_win32_Using_the_Keyboard_to_Move_the_Cursor

+$#A Creating a Cursor
The following example creates two cursor handles: one for the standard hourglass cursor and one for a custom cursor included as a resource in the application's resource-definition file.
HINSTANCE hinst; /* handle of current instance */
HCURSOR hCurs1, hCurs2; /* cursor handles */

/* Create a standard hourglass cursor. */

hCurs1 = LoadCursor(NULL, IDC_WAIT);

/* Create a custom cursor based on a resource. */

hCurs2 = LoadCursor(hinst, MAKEINTRESOURCE(240));

An application should implement custom cursors as resources. Rather than create the cursors at run time, use the LoadCursor_win32_LoadCursor or LoadImage_win32_LoadImage function to avoid device dependence, to simplify localization, and to enable applications to share cursor designs.
The following example uses the CreateCursor_win32_CreateCursor function to create a custom cursor at run time. The example is included here to illustrate how the system interprets cursor bitmasks. Even though this example uses the CreateCursor function, you should use the CreateIconIndirect_win32_CreateIconIndirect function instead because it is more efficient.
HINSTANCE hinst; /* handle of current instance */
HCURSOR hCurs1, hCurs2; /* cursor handles */

HCURSOR hCurs3; /* cursor handle */

/* Yin cursor AND bitmask */

BYTE ANDmaskCursor[] = {
0xFF, 0xFC, 0x3F, 0xFF, /* line 1 */
0xFF, 0xC0, 0x1F, 0xFF, /* line 2 */
0xFF, 0x00, 0x3F, 0xFF, /* line 3 */
0xFE, 0x00, 0xFF, 0xFF, /* line 4 */

0xF7, 0x01, 0xFF, 0xFF, /* line 5 */
0xF0, 0x03, 0xFF, 0xFF, /* line 6 */
0xF0, 0x03, 0xFF, 0xFF, /* line 7 */
0xE0, 0x07, 0xFF, 0xFF, /* line 8 */

0xC0, 0x07, 0xFF, 0xFF, /* line 9 */
0xC0, 0x0F, 0xFF, 0xFF, /* line 10 */
0x80, 0x0F, 0xFF, 0xFF, /* line 11 */
0x80, 0x0F, 0xFF, 0xFF, /* line 12 */

0x80, 0x07, 0xFF, 0xFF, /* line 13 */
0x00, 0x07, 0xFF, 0xFF, /* line 14 */
0x00, 0x03, 0xFF, 0xFF, /* line 15 */
0x00, 0x00, 0xFF, 0xFF, /* line 16 */

0x00, 0x00, 0x7F, 0xFF, /* line 17 */
0x00, 0x00, 0x1F, 0xFF, /* line 18 */
0x00, 0x00, 0x0F, 0xFF, /* line 19 */
0x80, 0x00, 0x0F, 0xFF, /* line 20 */

0x80, 0x00, 0x07, 0xFF, /* line 21 */
0x80, 0x00, 0x07, 0xFF, /* line 22 */
0xC0, 0x00, 0x07, 0xFF, /* line 23 */
0xC0, 0x00, 0x0F, 0xFF, /* line 24 */

0xE0, 0x00, 0x0F, 0xFF, /* line 25 */
0xF0, 0x00, 0x1F, 0xFF, /* line 26 */
0xF0, 0x00, 0x1F, 0xFF, /* line 27 */
0xF8, 0x00, 0x3F, 0xFF, /* line 28 */

0xFE, 0x00, 0x7F, 0xFF, /* line 29 */
0xFF, 0x00, 0xFF, 0xFF, /* line 30 */
0xFF, 0xC3, 0xFF, 0xFF, /* line 31 */
0xFF, 0xFF, 0xFF, 0xFF}; /* line 32 */

/* Yin cursor XOR bitmask */

BYTE XORmaskCursor[] = {
0x00, 0x00, 0x00, 0x00, /* line 1 */
0x00, 0x03, 0xC0, 0x00, /* line 2 */
0x00, 0x3F, 0x00, 0x00, /* line 3 */
0x00, 0xFE, 0x00, 0x00, /* line 4 */

0x0E, 0xFC, 0x00, 0x00, /* line 5 */
0x07, 0xF8, 0x00, 0x00, /* line 6 */
0x07, 0xF8, 0x00, 0x00, /* line 7 */
0x0F, 0xF0, 0x00, 0x00, /* line 8 */

0x1F, 0xF0, 0x00, 0x00, /* line 9 */
0x1F, 0xE0, 0x00, 0x00, /* line 10 */
0x3F, 0xE0, 0x00, 0x00, /* line 11 */
0x3F, 0xE0, 0x00, 0x00, /* line 12 */

0x3F, 0xF0, 0x00, 0x00, /* line 13 */
0x7F, 0xF0, 0x00, 0x00, /* line 14 */
0x7F, 0xF8, 0x00, 0x00, /* line 15 */
0x7F, 0xFC, 0x00, 0x00, /* line 16 */

0x7F, 0xFF, 0x00, 0x00, /* line 17 */
0x7F, 0xFF, 0x80, 0x00, /* line 18 */
0x7F, 0xFF, 0xE0, 0x00, /* line 19 */
0x3F, 0xFF, 0xE0, 0x00, /* line 20 */

0x3F, 0xC7, 0xF0, 0x00, /* line 21 */
0x3F, 0x83, 0xF0, 0x00, /* line 22 */
0x1F, 0x83, 0xF0, 0x00, /* line 23 */
0x1F, 0x83, 0xE0, 0x00, /* line 24 */

0x0F, 0xC7, 0xE0, 0x00, /* line 25 */
0x07, 0xFF, 0xC0, 0x00, /* line 26 */
0x07, 0xFF, 0xC0, 0x00, /* line 27 */
0x01, 0xFF, 0x80, 0x00, /* line 28 */

0x00, 0xFF, 0x00, 0x00, /* line 29 */
0x00, 0x3C, 0x00, 0x00, /* line 30 */
0x00, 0x00, 0x00, 0x00, /* line 31 */
0x00, 0x00, 0x00, 0x00}; /* line 32 */

/* Create a custom cursor at run time. */

hCurs3 = CreateCursor(hinst, /* app instance */
19, /* horiz pos of hot spot */
2, /* vert pos of hot spot */
32, /* cursor width */
32, /* cursor height */
ANDmaskCursor, /* AND bitmask */
XORmaskCursor); /* XOR bitmask */

To create the cursor, CreateCursor_win32_CreateCursor applies the following truth table to the AND and XOR bitmasks.
AND bitmask XOR bitmask Display
0 0 Black
0 1 White
1 0 Screen
1 1 Reverse screen

For more information about bitmaps, see Bitmaps_win32_Bitmaps.
Before closing, your application must use the DestroyCursor_win32_DestroyCursor function to destroy any cursors it created with CreateCursor_win32_CreateCursor. It is not necessary to destroy cursors created by other functions.
+$#A Displaying a Cursor
If the system includes a mouse, Windows automatically displays the class cursor (the cursor associated with the window to which the cursor is pointing). Your application can assign a class cursor while registering a window class. The following example illustrates this by assigning a cursor handle to the hCursor member of the WNDCLASS_win32_WNDCLASS_str structure identified by the wc parameter.
WNDCLASS wc;

/*
* Fill the window class structure with parameters that
* describe the main window.
*/

wc.style = NULL; /* class style(s) */
wc.lpfnWndProc = (WNDPROC) MainWndProc; /* window proc. */
wc.cbClsExtra = 0; /* no per-class extra data */
wc.cbWndExtra = 0; /* no per-window extra data */
wc.hInstance = hinstance; /* app that owns the class */
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* class icon */
wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(230));
/* class cursor */
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
/* class background */
wc.lpszMenuName = "GenericMenu"; /* class menu */
wc.lpszClassName = "GenericWClass" /* class name */

/* Register the window class. */

return RegisterClass(&wc);

When the window class is registered, the cursor identified by 230 in the application's resource-definition file will be the default cursor for all windows based on the class.
Your application can change the design of the cursor by using the SetCursor_win32_SetCursor function and specifying a different cursor handle. However, when the cursor moves, Windows redraws the class cursor at the new location. To prevent the class cursor from being redrawn, your application must process the WM_SETCURSOR_win32_WM_SETCURSOR message. Each time the cursor moves and mouse input is not captured, Windows sends this message to the window in which the cursor is moving.
You can specify different cursors for different conditions while processing WM_SETCURSOR. For example, the following example shows how to display the hCurs3 cursor whenever the cursor moves over the icon of a minimized application.
case WM_SETCURSOR:

/*
* If the window is minimized, draw the hCurs3 cursor.
* If the window is not minimized, draw the default
* cursor (class cursor).
*/

if (IsIconic(hwnd)) {
SetCursor(hCurs3);
break;
}

When the window is not minimized, Windows displays the class cursor.
Your application can replace a class cursor by using the SetClassLong_win32_SetClassLong function. This function changes the default window settings for all windows of a given class. The following example replaces the existing class cursor with the hCurs2 cursor.
/* Change the cursor for hwnd's window class. */

SetClassLong(hwnd, /* window handle */
GCL_HCURSOR, /* change cursor */
(LONG) hCurs2); /* new cursor */

For more information about window classes and mouse input, see Window Classes_win32_Window_Classes and Mouse Input_win32_Mouse_Input.
+$#A Confining a Cursor
The following example confines the cursor to the application's window and then restores the cursor to its previous window. The example uses the GetClipCursor_win32_GetClipCursor function to record the area in which the cursor can move and the ClipCursor_win32_ClipCursor function to confine and restore the cursor.
RECT rcClip; /* new ClipCursor area */
RECT rcOldClip; /* previous ClipCursor area */

/* Record the area in which the cursor can move. */

GetClipCursor(&rcOldClip);

/* Get the dimensions of the application's window. */

GetWindowRect(hwnd, &rcClip);

/* Confine the cursor to the application's window. */

ClipCursor(&rcClip);

.
. /* Process input from the confined cursor. */
.

/* Restore the cursor to its previous area. */

ClipCursor(&rcOldClip);

Because there is only one cursor at a time available in the system, an application that confines the cursor must restore the cursor before relinquishing control to another window.
+$#A Using Cursor Functions to Create a Mousetrap
The following example uses the SetCursorPos_win32_SetCursorPos, GetCursorPos_win32_GetCursorPos, CreateCursor_win32_CreateCursor, LoadCursor_win32_LoadCursor, and SetCursor_win32_SetCursor functions to create a simple mousetrap. It also uses cursor and timer functions to monitor the cursor's position every 10 seconds. If the cursor position has not changed in the last 10 seconds and the application's main window is minimized, the application changes the cursor and moves it to the mousetrap icon.
An example for a similar mousetrap is included in Icons_win32_Icons. It uses the LoadCursor and LoadIcon_win32_LoadIcon functions instead of the more device-dependent CreateCursor and CreateIcon_win32_CreateIcon functions.
HICON hIcon1; /* icon handles */
POINT ptOld; /* previous cursor location */
HCURSOR hCurs1; /* cursor handle */

/*
* The following cursor bitmasks are defined in a code
* example that appears earlier in this topic.
*/

/* Yin cursor AND and XOR bitmasks */

BYTE ANDmaskCursor[] = ...
BYTE XORmaskCursor[] = ...

/* Yang icon AND bitmask */

BYTE ANDmaskIcon[] = {0xFF, 0xFF, 0xFF, 0xFF, /* line 1 */
0xFF, 0xFF, 0xC3, 0xFF, /* line 2 */
0xFF, 0xFF, 0x00, 0xFF, /* line 3 */
0xFF, 0xFE, 0x00, 0x7F, /* line 4 */

0xFF, 0xFC, 0x00, 0x1F, /* line 5 */
0xFF, 0xF8, 0x00, 0x0F, /* line 6 */
0xFF, 0xF8, 0x00, 0x0F, /* line 7 */
0xFF, 0xF0, 0x00, 0x07, /* line 8 */

0xFF, 0xF0, 0x00, 0x03, /* line 9 */
0xFF, 0xE0, 0x00, 0x03, /* line 10 */
0xFF, 0xE0, 0x00, 0x01, /* line 11 */
0xFF, 0xE0, 0x00, 0x01, /* line 12 */

0xFF, 0xF0, 0x00, 0x01, /* line 13 */
0xFF, 0xF0, 0x00, 0x00, /* line 14 */
0xFF, 0xF8, 0x00, 0x00, /* line 15 */
0xFF, 0xFC, 0x00, 0x00, /* line 16 */

0xFF, 0xFF, 0x00, 0x00, /* line 17 */
0xFF, 0xFF, 0x80, 0x00, /* line 18 */
0xFF, 0xFF, 0xE0, 0x00, /* line 19 */
0xFF, 0xFF, 0xE0, 0x01, /* line 20 */

0xFF, 0xFF, 0xF0, 0x01, /* line 21 */
0xFF, 0xFF, 0xF0, 0x01, /* line 22 */
0xFF, 0xFF, 0xF0, 0x03, /* line 23 */
0xFF, 0xFF, 0xE0, 0x03, /* line 24 */

0xFF, 0xFF, 0xE0, 0x07, /* line 25 */
0xFF, 0xFF, 0xC0, 0x0F, /* line 26 */
0xFF, 0xFF, 0xC0, 0x0F, /* line 27 */
0xFF, 0xFF, 0x80, 0x1F, /* line 28 */

0xFF, 0xFF, 0x00, 0x7F, /* line 29 */
0xFF, 0xFC, 0x00, 0xFF, /* line 30 */
0xFF, 0xF8, 0x03, 0xFF, /* line 31 */
0xFF, 0xFC, 0x3F, 0xFF}; /* line 32 */

/* Yang icon XOR bitmask */

BYTE XORmaskIcon[] = {0x00, 0x00, 0x00, 0x00, /* line 1 */
0x00, 0x00, 0x00, 0x00, /* line 2 */
0x00, 0x00, 0x00, 0x00, /* line 3 */
0x00, 0x00, 0x00, 0x00, /* line 4 */

0x00, 0x00, 0x00, 0x00, /* line 5 */
0x00, 0x00, 0x00, 0x00, /* line 6 */
0x00, 0x00, 0x00, 0x00, /* line 7 */
0x00, 0x00, 0x38, 0x00, /* line 8 */

0x00, 0x00, 0x7C, 0x00, /* line 9 */
0x00, 0x00, 0x7C, 0x00, /* line 10 */
0x00, 0x00, 0x7C, 0x00, /* line 11 */
0x00, 0x00, 0x38, 0x00, /* line 12 */

0x00, 0x00, 0x00, 0x00, /* line 13 */
0x00, 0x00, 0x00, 0x00, /* line 14 */
0x00, 0x00, 0x00, 0x00, /* line 15 */
0x00, 0x00, 0x00, 0x00, /* line 16 */

0x00, 0x00, 0x00, 0x00, /* line 17 */
0x00, 0x00, 0x00, 0x00, /* line 18 */
0x00, 0x00, 0x00, 0x00, /* line 19 */
0x00, 0x00, 0x00, 0x00, /* line 20 */

0x00, 0x00, 0x00, 0x00, /* line 21 */
0x00, 0x00, 0x00, 0x00, /* line 22 */
0x00, 0x00, 0x00, 0x00, /* line 23 */
0x00, 0x00, 0x00, 0x00, /* line 24 */

0x00, 0x00, 0x00, 0x00, /* line 25 */
0x00, 0x00, 0x00, 0x00, /* line 26 */
0x00, 0x00, 0x00, 0x00, /* line 27 */
0x00, 0x00, 0x00, 0x00, /* line 28 */

0x00, 0x00, 0x00, 0x00, /* line 29 */
0x00, 0x00, 0x00, 0x00, /* line 30 */
0x00, 0x00, 0x00, 0x00, /* line 31 */
0x00, 0x00, 0x00, 0x00}; /* line 32 */

hIcon1 = CreateIcon(hinstance, /* handle of app instance */
32, /* icon width */
32, /* icon height */
1, /* number of XOR planes */
1, /* number of bits per pixel */
ANDmaskIcon, /* AND bitmask */
XORmaskIcon); /* XOR bitmask */

hCurs1 = CreateCursor(hinstance, /* handle of app inst. */
19, /* horiz. position of hot spot */
2, /* vert. position of hot spot */
32, /* cursor width */
32, /* cursor height */
ANDmaskCursor, /* AND bitmask */
XORmaskCursor); /* XOR bitmask */

.
. /* Fill in the window class structure. */
.

WNDCLASS wc;

wc.hIcon = hIcon1; /* class icon */
wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* class cursor */

.
. /*
. * Register the window class and perform
. * other application initialization.
. */
.

/* Set a timer for the mousetrap. */

GetCursorPos(&ptOld);

SetTimer(hwnd, IDT_CURSOR, 10000, (TIMERPROC) NULL);

LONG APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{

HDC hdc; /* handle of device context */
POINT pt; /* current cursor location */
RECT rc; /* iconized window location */

switch (message) {
.
. /* Process other messages. */
.

case WM_TIMER:

/*
* If the window is minimized, compare the
* current cursor position with the one 10
* seconds before. If the cursor position has
* not changed, move the cursor to the icon.
*/

if (IsIconic(hwnd)) {
GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {
GetWindowRect(hwnd, &rc);
SetCursorPos(rc.left + 20, rc.top + 4);

/*
* Note that the additional constants
* (20 and 4) are application-specific
* values to align the yin-shaped cursor
* and the yang-shaped icon.
*/

}
else {
ptOld.x = pt.x;
ptOld.y = pt.y;
}
}

return 0;

case WM_SETCURSOR:

/*
* If the window is minimized, draw hCurs1.
* If the window is not minimized, draw the
* default * cursor (class cursor).
*/

if (IsIconic(hwnd)) {
SetCursor(hCurs1);
break;
}

case WM_DESTROY:

/* Destroy timer. */

KillTimer(hwnd, IDT_CURSOR);

PostQuitMessage(0);
break;

.
. /* Process other messages. */
.

} /* ends switch (message) */

+$#A Using the Keyboard to Move the Cursor
Because Windows does not require a mouse, an application should be able to simulate mouse actions with the keyboard. The following example shows how to achieve this, by using the GetCursorPos_win32_GetCursorPos and SetCursorPos_win32_SetCursorPos functions, and by processing input from the arrow keys.
HCURSOR hCurs1, hCurs2; /* cursor handles */

POINT pt; /* cursor location */
RECT rc; /* client area coordinates */
static int repeat = 1; /* repeat key counter */

.
. /* Other declarations and initialization. */
.

switch (message) {

.
. /* Process other messages. */
.

case WM_KEYDOWN:

if (wParam != VK_LEFT && wParam != VK_RIGHT &&
wParam != VK_UP && wParam != VK_DOWN) {
break;
}

GetCursorPos(&pt);

/*
*Convert screen coordinates to client
* coordinates.
*/

ScreenToClient(hwnd, &pt);

switch (wParam) {

/*
* Move the cursor to reflect which
* arrow keys are pressed.
*/

case VK_LEFT: /* left arrow */
pt.x -= repeat;
break;

case VK_RIGHT: /* right arrow */
pt.x += repeat;
break;

case VK_UP: /* up arrow */
pt.y -= repeat;
break;

case VK_DOWN: /* down arrow */
pt.y += repeat;
break;

default:
return NULL;

}

repeat++; /* increments repeat count */

/* Keep the cursor in the client area. */

GetClientRect(hwnd, &rc);

if (pt.x >= rc.right) {
pt.x = rc.right - 1;
}
else {
if (pt.x < rc.left) {
pt.x = rc.left;
}
}

if (pt.y >= rc.bottom) {
pt.y = rc.bottom - 1;
}
else {
if (pt.y < rc.top) {
pt.y = rc.top;
}
}

/*
* Convert client coordinates to screen
*coordinates.
*/

ClientToScreen(hwnd, &pt);
SetCursorPos(pt.x, pt.y);
break;

case WM_KEYUP:

repeat = 1; /* clears repeat count */
break;

.
.
.

} /* ends switch (message) */

+$#AK Cursor Reference
The following functions and messages are associated with cursors.
+$#A Cursor Functions
Following are the functions used with cursors.
ClipCursor_win32_ClipCursorCopyCursor_win32_CopyCursorCreateCursor_win32_CreateCursorDestroyCursor_win32_DestroyCursorGetClipCursor_win32_GetClipCursorGetCursor_win32_GetCursorGetCursorPos_win32_GetCursorPosLoadCursor_win32_LoadCursorLoadCursorFromFile_win32_LoadCursorFromFileSetCursor_win32_SetCursorSetCursorPos_win32_SetCursorPosSetSystemCursor_win32_SetSystemCursorShowCursor_win32_ShowCursor
+$#A Cursor Messages
The following message is used with cursors.
WM_SETCURSOR_win32_WM_SETCURSOR

+$#AK Extended Menus
A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with the menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.
This Windows 95 overview describes the extended menu functions and explains how to use them in a Win32-based application.
+$#A Menus and Extended Menus Platform Differences
The overviews for menus and extended menus in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.
+$#A About Extended Menus
{ewl msdncd, EWGraphic, group10809 0 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview About Menus_win32_About_Menus_nt.
This overview describes menus and explains how to use them in applications designed for the Microsoft Win32® application programming interface (API).
The extended menu functions described in this section supercede the menu functions described in Menus (Windows NT)_win32_Menus_Windows_NT_. However, the two sets of functions can be used interchangeably insofar as their capabilities overlap.
+$#A Menu Bars and Pop-up Menus
{ewl msdncd, EWGraphic, group10809 1 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Bars and Pop-up Menus_win32_Menu_Bars_and_Pop_up_Menus_nt.
The menus in an application written for Windows are arranged hierarchically. A menu bar (sometimes called a top-level menu) resides at the uppermost level of the hierarchy; the lower levels consist of pop-up menus (also known as submenus).
The items in a menu bar represent the main categories of commands provided by an application. Selecting an item from the menu bar typically activates a pop-up menu whose items correspond to the commands in a given category. For example, a menu bar might contain a File menu item that, when the user selects it, activates a pop-up menu with such menu items as New, Open, Save, and so on.
Only an overlapped or pop-up window can contain a menu bar; a child window cannot contain one. If the window has a title bar, Windows positions the menu bar just below it. A menu bar is always visible. In contrast, a pop-up menu is not visible until the user selects a menu item that activates it. When the menu is displayed, it is attached to the title bar.
Every menu must have an owner window. Windows sends messages to a menu's owner window when the user selects the menu or chooses a command from the menu. These messages are described in Menu Messages_win32_Menu_Messages.
+$#A Floating Pop-up Menus
{ewl msdncd, EWGraphic, group10809 2 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Floating Pop-up Menus_win32_Floating_Pop_up_Menus_nt.
Windows provides a special type of pop-up menu called a floating pop-up menu. A floating pop-up menu is not attached to the menu bar; it can appear anywhere on the screen. An application typically associates a floating pop-up menu with a portion of a window, such as the client area, or with a specific object, such as an icon. For this reason, floating pop-up menus are also called context menus.
A floating pop-up menu remains hidden until the user activates it, typically by clicking the right mouse button. The menu is usually displayed at the position of the caret or mouse cursor.
+$#A The System Menu
{ewl msdncd, EWGraphic, group10809 3 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview The System Menu_win32_The_System_Menu_nt.
A System menu (also known as the Control menu) is a pop-up menu defined and managed almost exclusively by the operating system. It is represented by a small box containing a horizontal line that is displayed at the left end of the title bar. When the user clicks the box, Windows displays the System menu. Windows also displays the System menu when the user clicks the icon of a minimized application.
A System menu provides a standard set of menu items that the user can choose to change a window's size or position, close the application, or activate the Windows Task List. Items in a System menu can be added, deleted, and modified, but most applications just use the standard set of menu items. An overlapped, pop-up, or child window can have a System menu. It is uncommon for an overlapped or pop-up window not to include a System menu.
When the user chooses a command from a System menu, Windows sends a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message to the menu's owner window. In most applications, the window procedure does not process messages from a System menu. Instead, it simply passes the messages to the DefWindowProc_win32_DefWindowProc function for system-default processing of the message. If a command is added to a System menu, the window procedure must process the command.
+$#A Help Identifier
Associated with each menu bar and pop-up menu is a 32-bit help identifier. If the user presses the F1 key while the menu is active, this value is sent to the owner window as part of a WM_HELP_win32_WM_HELP message. For more information about help identifiers, see Help_win32_Help.
+$#A Menu Handles
{ewl msdncd, EWGraphic, group10809 4 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Handles_win32_Menu_Handles_nt.
The system generates a unique handle for each menu. A menu handle is a value of the HMENU type. An application must specify a menu handle in many of the Windows menu functions. You receive the handle of a menu bar when you create the menu or load a menu resource. For more information about creating and loading menus, see Menu Creation_win32_Menu_Creation.
To retrieve the handle of a menu that has been created or loaded, use the GetMenu_win32_GetMenu or GetSubMenu_win32_GetSubMenu function. GetMenu returns the handle of the menu bar assigned to the specified window. GetSubMenu returns the handle of the pop-up menu associated with a pop-up item (the GetMenuItemInfo_win32_GetMenuItemInfo function can also be used for this purpose.) The GetSystemMenu_win32_GetSystemMenu function retrieves the handle of a window's System menu.
+$#A Menu Items
{ewl msdncd, EWGraphic, group10809 5 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Items_win32_Menu_Items_nt.
Each menu contains one or more menu items. By choosing a menu item, a user can carry out a command or open a pop-up menu. The following sections introduce the various types of menu items and the ways in which an application can control a menu item's appearance and functionality.
+$#A Command Items and Pop-up Items
Choosing a menu item may either perform a command or open a pop-up menu. Menu items that perform commands are called command items; those that open pop-up menus are called pop-up items.
When the user chooses a command item, Windows sends a command message to the window that owns the menu. If the command item is on the System menu, Windows sends the WM_SYSCOMMAND_win32_WM_SYSCOMMAND message. Otherwise, it sends the WM_COMMAND_win32_WM_COMMAND message.
Associated with each pop-up item is the handle of a corresponding pop-up menu. When the user selects a pop-up item, Windows opens the pop-up menu. No command message is sent to the owner window. However, Windows sends a WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP message to the owner window before displaying the pop-up menu. You can get the handle of the pop-up menu associated with a pop-up item by using the GetSubMenu_win32_GetSubMenu or GetMenuItemInfo_win32_GetMenuItemInfo function (the handle is NULL for command items).
A menu bar typically contains pop-up items, but it may also contain command items. A pop-up menu typically contains command items, but it may also contain pop-up items. By adding pop-up items to a pop-up menu, you can nest pop-up menus to an arbitrary depth. If you include a pop-up item in a pop-up menu, Windows automatically displays a small arrow to the right of the menu item text as a visual cue to the user that a pop-up menu has been nested.
+$#A Menu Item Identifier
{ewl msdncd, EWGraphic, group10809 6 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Menu-Item Identifier_win32_Menu_Item_Identifier_nt.
Associated with each menu item is a 16-bit integer called an identifier. An application should assign a unique identifier to each menu item. When a command item is chosen, the item's identifier is sent to the owner window as part of a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message. You can also use an identifier to specify a menu item when you call many menu functions.
Pop-up items have identifiers just as command items do. However, Windows does not send a command message when a pop-up item is selected from a menu. Instead, Windows activates the pop-up menu associated with the menu item.
To retrieve the identifier of the menu item at a specified position, use the GetMenuItemID_win32_GetMenuItemID or GetMenuItemInfo_win32_GetMenuItemInfo function.
+$#A Menu Item Position
{ewl msdncd, EWGraphic, group10809 7 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Menu-Item Position_win32_Menu_Item_Position_nt.
In addition to having a unique identifier, each menu item in a menu bar or pop-up menu has a unique position value. The leftmost item in a menu bar, or the topmost item in a pop-up menu, has position zero. The position value is incremented for subsequent menu items. Windows assigns a position value to all items in a menu, including separators. The following illustration shows the position values of items in a menu bar and a pop-up menu.
{ewc msdncd, EWGraphic, group10809 8 /a "SDK_01.bmp"}
When calling a menu function that modifies or retrieves information about a specific menu item, you can specify the item using either its identifier or its position.
+$#A Default Menu Items
A pop-up menu may contain one default menu item. When the user opens a pop-up by double-clicking, Windows sends a command message to the menu's owner window and closes the menu as if the default item had been chosen. If there is no default command item, the pop-up menu remains open. To retrieve and set the default item for a pop-up menu, use the GetMenuDefaultItem_win32_GetMenuDefaultItem and SetMenuDefaultItem_win32_SetMenuDefaultItem functions.
+$#A Checked and Unchecked Menu Items
{ewl msdncd, EWGraphic, group10809 9 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Checked and Unchecked Menu Items_win32_Checked_and_Unchecked_Menu_Items_nt.
A menu item can be either checked or unchecked. Windows displays a bitmap next to checked menu items to indicate their checked state. Windows does not display a bitmap next to unchecked items, unless an application-defined "unchecked" bitmap is specified. Only menu items in a pop-up menu can be checked; items in a menu bar cannot be checked.
Applications typically check or uncheck a menu item to indicate whether an option is in effect. For example, suppose an application has a toolbar that the user can show or hide using a Toolbar command. When the toolbar is hidden, the Toolbar menu item is unchecked. When the user chooses the command, the application checks the menu item and shows the toolbar. To retrieve and set the check state of a menu item, use the GetMenuItemInfo_win32_GetMenuItemInfo and SetMenuItemInfo_win32_SetMenuItemInfo functions.
Sometimes, a group of menu items corresponds to a set of mutually exclusive options. In this case, you can indicate the selected option by using a checked radio menu item (analogous to a radio button control). Checked radio items are displayed with a bullet bitmap instead of a check-mark bitmap. To check a menu item and make it a radio item, use the CheckMenuRadioItem_win32_CheckMenuRadioItem function.
By default, Windows displays a check-mark or bullet bitmap next to checked menu items and no bitmap next to unchecked menu items. However, you can use the SetMenuItemBitmaps_win32_SetMenuItemBitmaps function to associate application-defined "checked" and "unchecked" bitmaps with a menu item. Windows then uses the specified bitmaps to indicate the menu item's checked or unchecked state.
Application-defined bitmaps associated with a menu item must be the same size as the default check-mark bitmap, the dimensions of which may vary depending on screen resolution. To retrieve the correct dimensions, use the GetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensions function. You can create multiple bitmap resources for different screen resolutions; create one bitmap resource and scale it, if necessary; or create a bitmap at run time and draw an image in it. The bitmaps may be either monochrome or color. However, because menu items are inverted when highlighted, the appearance of certain inverted color bitmaps may be undesirable.
+$#A Enabled, Disabled, and Grayed Menu Items
{ewl msdncd, EWGraphic, group10809 10 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Enabled, Disabled, and Grayed Menu Items_win32_Enabled_Disabled_and_Grayed_Menu_Items_nt.
A menu item can be enabled, disabled, or grayed. By default, a menu item is enabled. When the user chooses an enabled menu item, Windows sends a command message to the owner window or displays the corresponding pop-up menu, depending on whether the menu item is a command or a pop-up item.
When menu items are not available to the user, they should be disabled or grayed. Disabled and grayed menu items cannot be chosen. Typically, applications gray an unavailable menu item to provide a visual clue to the user that a command is not available; a disabled menu item does not provide this visual clue. A disabled item can serve as a heading for a group of related menu items. (For example, a disabled Save menu item can serve as a heading for a series of enabled command items, such as Save File, Save All, and Save As.) A grayed item can be used when an action is not appropriate. (For example, the Print command in the File menu can be grayed when the system does not have a printer installed.)
The EnableMenuItem_win32_EnableMenuItem function enables, disables, or grays a menu item. To determine whether a menu item is enabled, disabled, or grayed, use the GetMenuItemInfo_win32_GetMenuItemInfo function.
+$#A Highlighted Menu Items
{ewl msdncd, EWGraphic, group10809 11 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Highlighted Menu Items_win32_Highlighted_Menu_Items_nt.
Windows automatically highlights menu items as the user selects them. However, highlighting can be explicitly added or removed from a menu item on the menu bar by using the HiliteMenuItem_win32_HiliteMenuItem function. This function has no effect on menu items in pop-up menus. When HiliteMenuItem is used to highlight a menu item, though, the menu item only appears to be selected. If the user presses the ENTER key, the highlighted item is not chosen.
+$#A String, Bitmap, and Owner-Drawn Menu Items
An application can display a menu item using a string or a bitmap. In addition, an application can completely control the appearance of a menu item by using an owner-drawn item. The application is responsible for drawing owner-drawn items in any of their possible states (highlighted, checked, and so on).
For example, an application that has a Font menu might draw each menu item using the corresponding font (that is, the item for roman would be drawn using a roman font, the item for italic in an italic font, and so on).
+$#A Menu Item Separators and Line Breaks
{ewl msdncd, EWGraphic, group10809 12 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Menu-Item Separators and Line Breaks_win32_Menu_Item_Separators_and_Line_Breaks_nt.
Windows provides a special type of menu item, called a separator, that appears as a horizontal line. A separator can be used in a pop-up menu to divide a menu into groups of related items. A separator cannot be used in a menu bar, and the user cannot select a separator.
When a menu bar contains more items than will fit on one line, Windows automatically breaks the line into two or more lines. You can cause a line break to occur at a specific item on a menu bar by assigning the MFT_MENUBREAK type flag to the item. Windows places that item and all subsequent item on a new line.
When a pop-up menu contains more items than will fit in one column, Windows automatically breaks the menu into two or more columns. You can cause a column break to occur at a specific item in a pop-up menu by assigning the MFT_MENUBREAK type flag to the item. Windows places that item and all subsequent items in a new column. The MFT_MENUBARBREAK type flag has the same effect except that a vertical line appears between the new column and the old.
+$#A Keyboard Access to Menus
{ewl msdncd, EWGraphic, group10809 13 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Keyboard Access to Menus_win32_Keyboard_Access_to_Menus_nt.
Windows provides a standard keyboard interface for menus. You can enhance this interface by providing mnemonics and accelerator keys for your menu items. The following sections describe the standard keyboard interface, mnemonics, and accelerator keys.
+$#A Standard Keyboard Interface
{ewl msdncd, EWGraphic, group10809 14 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Standard Keyboard Interface_win32_Standard_Keyboard_Interface_nt.
Windows is designed to work with or without a mouse or other pointing device. Because Windows provides a standard keyboard interface, the user can use the keyboard to select menu items. This keyboard interface does not need special code. An application receives a command message whether a menu item is selected through the keyboard or by using a mouse. The standard keyboard interface processes the following keystrokes.
Keystroke Action
Alphabetic character Selects the first menu item with the specified character as its mnemonic. If the selected item invokes a pop-up menu, the menu is displayed and the first item is highlighted. Otherwise, the menu item is chosen.
ALT Toggles in and out of menu bar mode.
ALT+SPACEBAR Displays the System menu.
ENTER Activates a pop-up menu and selects the first menu item if an item has a pop-up menu associated with it. Otherwise, this keystroke chooses the item as if the user released the mouse button while the item was selected.
ESC Exits menu mode.
LEFT ARROW Works like the RIGHT ARROW key, except in the opposite direction. In pop-up menus, this keystroke backs up one column; when the currently selected item is in the far-left column, the previous pop-up menu is selected.
RIGHT ARROW Cycles to the next top-level menu item. Top-level menu items include menu bar items and the System menu. If the selected item is in a pop-up menu, the next column in the pop-up menu is selected or the next top-level menu item is selected.
UP or DOWN ARROWS Activates a pop-up menu when pressed in a top-level menu. When pressed in a pop-up menu, either keystroke selects the next or previous item.

+$#A Menu Mnemonics
{ewl msdncd, EWGraphic, group10809 15 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Mnemonics_win32_Menu_Mnemonics_nt.
The standard keyboard interface for menus can be enhanced by adding mnemonics to menu items. A mnemonic is an underlined letter in the text of a menu item. When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter. The menu bar is active when the user presses and releases the ALT key and the first item on the menu bar is highlighted. A pop-up menu is active when it is displayed.
A mnemonic keystroke for a menu item is created by preceding any character in the item's text string with an ampersand. For example, the text string "&Move" causes Windows to underline the letter "M".
+$#A Menu Keyboard Accelerators
{ewl msdncd, EWGraphic, group10809 16 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Keyboard Accelerators_win32_Menu_Keyboard_Accelerators_nt.
In addition to having a mnemonic, a menu item can have a keyboard accelerator associated with it. A keyboard accelerator is different from a mnemonic, because the menu does not have to be active for the accelerator key to work. Also, a mnemonic is always associated with a menu item, while a keyboard accelerator is usually (but does not have to be) associated with a menu item.
Text that identifies the keyboard accelerator is added to the menu item text string. The accelerator text appears to the right of the menu item name, after a backslash and tab character (\t). For example, "&Close\tAlt+F4" represents a Close item with the ALT+F4 key combination as its keyboard accelerator and with the letter "C" as its mnemonic. For more information about accelerators, see Keyboard Accelerators_win32_Keyboard_Accelerators.
+$#A Menu Creation
{ewl msdncd, EWGraphic, group10809 17 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Creation_win32_Menu_Creation_nt.
You can create a menu using either a menu template or using menu creation functions. Menu templates are typically defined as resources. Menu template resources can be loaded explicitly or assigned as the default menu for a window class. You can also create menu template resources dynamically in memory.
+$#A Menu Template Resources
{ewl msdncd, EWGraphic, group10809 18 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Menu-Template Resources_win32_Menu_Template_Resources_nt.
Most applications create menus using menu template resources. A menu template defines a menu, including the items in the menu bar and all pop-up menus. For information about creating a menu template resource, see the documentation included with your development tools.
Once you have created a menu template resource and have added it to your application's executable (.EXE) file, you can use the LoadMenu_win32_LoadMenu function to load the resource into memory. This function returns the handle of the menu, which you can then assign to a window by using the SetMenu_win32_SetMenu function.
Implementing menus as resources makes an application easier to localize for use in multiple countries. Only the resource-definition file needs to be translated for each language, not the application's source code.
+$#A Menu Template in Memory
{ewl msdncd, EWGraphic, group10809 19 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Template in Memory_win32_Menu_Template_in_Memory_nt.
A menu can be created from a menu template that is built in memory at run time. For example, an application that allows a user to customize its menu might create a menu template in memory based on the user's preferences. The application could then save the template in a file or in the registry for future use. To create a menu from a template in memory, use the LoadMenuIndirect_win32_LoadMenuIndirect function.
There are two types of menu templates. A standard menu template consists of a MENUITEMTEMPLATEHEADER_win32_MENUITEMTEMPLATEHEADER_str structure followed by one or more MENUITEMTEMPLATE_win32_MENUITEMTEMPLATE_str structures. In most cases, an extended menu template is used; it consists of a MENUEX_TEMPLATE_HEADER_win32_MENUEX_TEMPLATE_HEADER_str structure followed by one or more MENUEX_TEMPLATE_ITEM_win32_MENUEX_TEMPLATE_ITEM_str structures.
+$#A Menu Creation Functions
{ewl msdncd, EWGraphic, group10809 20 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Menu-Creation Functions_win32_Menu_Creation_Functions_nt.
Using menu creation functions, you can create menus at run time or add menu items to existing menus. You can use the CreateMenu_win32_CreateMenu function to create an empty menu bar and the CreatePopupMenu_win32_CreatePopupMenu function to create an empty pop-up menu. To add items to a menu, use the InsertMenuItem_win32_InsertMenuItem function.
+$#A Menu Display
{ewl msdncd, EWGraphic, group10809 21 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Display_win32_Menu_Display_nt.
After a menu has been loaded or created, it must be assigned to a window before Windows can display it. This assignment can be done by defining a class menu. For more information about class menus, see Class Menus_win32_Class_Menus. You can also assign a menu to a window by specifying the handle of the menu as the hmenu parameter of the CreateWindowEx_win32_CreateWindowEx function or by calling the SetMenu_win32_SetMenu function.
To display a floating pop-up menu, use the TrackPopupMenu_win32_TrackPopupMenu or TrackPopupMenuEx_win32_TrackPopupMenuEx function. Floating pop-up menus, also called context menus, are typically displayed when the WM_CONTEXTMENU_win32_WM_CONTEXTMENU message is processed.
+$#A Class Menus
{ewl msdncd, EWGraphic, group10809 22 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Class Menus_win32_Class_Menus_nt.
You can specify a default menu, called a class menu, when you register a window class. To do so, you assign the name of the menu template resource to the lpszMenuName member of the WNDCLASS_win32_WNDCLASS_str structure used to register the class.
By default, every window is assigned the class menu for its window class so that you do not have to explicitly load the menu and assign it to each window. You can override the class menu by specifying a different menu handle in a call to the CreateWindowEx_win32_CreateWindowEx function. You can also change a window's menu after it is created by using the SetMenu_win32_SetMenu function. For more information about window classes, see Window Classes_win32_Window_Classes.
+$#A Menu Destruction
{ewl msdncd, EWGraphic, group10809 23 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Destruction_win32_Menu_Destruction_nt.
If a menu is assigned to a window and that window is destroyed, Windows automatically destroys the menu, freeing the menu's handle and the memory occupied by the menu. Windows does not automatically destroy a menu that is not assigned to a window. An application must destroy the unassigned menu by calling the DestroyMenu_win32_DestroyMenu function. Otherwise, the menu continues to exist in memory even after the application closes.
+$#A Menu Messages
{ewl msdncd, EWGraphic, group10809 24 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Messages Overview_win32_Menu_Messages_Overview_nt.
Windows reports menu-related activity by sending messages to the window procedure of the window that owns the menu. Windows sends a series of messages when the user selects items on the menu bar or clicks the right mouse button to display a floating pop-up menu.
When the user activates an item on the menu bar, the owner window first receives a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message. This message includes a flag that indicates whether the user activated the menu using the keyboard (SC_KEYMENU) or mouse (SC_MOUSEMENU). For more information about the keyboard interface for menus, see Keyboard Access to Menus_win32_Keyboard_Access_to_Menus.
Next, before displaying any pop-up menus, Windows sends the WM_INITMENU_win32_WM_INITMENU message to the window procedure so that an application can modify the menus before the user sees them. Windows sends the WM_INITMENU message only once per menu activation.
When the user selects a pop-up item, Windows sends the owner window the WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP message before displaying the pop-up menu. This message gives the application an opportunity to modify the pop-up menu before it is displayed.
Each time the user moves the highlighting from one item to another, Windows sends a WM_MENUSELECT_win32_WM_MENUSELECT message to the window procedure of the menu's owner window. This message identifies the currently selected menu item. Many applications provide an information area at the bottom of their main windows and use this message to display additional information about the selected menu item.
When the user chooses a command item from a menu, Windows sends a WM_COMMAND_win32_WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
Not all menus are accessed through a window's menu bar. Many applications display floating pop-up menus when the user clicks the right mouse button at a specific location. Such applications should process the WM_CONTEXTMENU_win32_WM_CONTEXTMENU message and display a floating pop-up menu, if appropriate. If an application does not display a floating pop-up menu, it should pass the WM_CONTEXTMENU message to the DefWindowProc_win32_DefWindowProc function for default processing.
+$#A Menu Modifications
{ewl msdncd, EWGraphic, group10809 25 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Menu Modifications_win32_Menu_Modifications_nt.
A number of functions enable the developer to change a menu after it has been loaded or created. These changes may include adding or removing menu items and modifying existing menu items.
To add a menu item, use the InsertMenuItem_win32_InsertMenuItem function. You can use the SetMenuItemInfo_win32_SetMenuItemInfo function to change the attributes of an existing menu item. The lpmii parameter points to a MENUITEMINFO_win32_MENUITEMINFO_str structure, which contains the new attributes and specifies which attributes to change. A menu item's attributes include its type, state, identifier, pop-up menu, bitmaps, item data, and text.
To retrieve information about a menu item, use the GetMenuItemInfo_win32_GetMenuItemInfo function. The lpmii parameter points to a MENUITEMINFO structure, which specifies the attributes to retrieve and receives their current values.
The DeleteMenu_win32_DeleteMenu or RemoveMenu_win32_RemoveMenu function is used to delete a menu item from a menu. If the item being deleted is a pop-up item, DeleteMenu deletes the associated pop-up menu, discarding the menu handle and freeing the memory used by the pop-up menu. RemoveMenu deletes a menu item, but if the item activates a pop-up menu, the function does not destroy the pop-up menu or its handle, allowing the menu to be reused.
After a menu bar has been modified, the DrawMenuBar_win32_DrawMenuBar function must be called to redraw the menu bar. Otherwise, the modifications will not appear until Windows redraws the owner window.
+$#A Superseded Functions
The following menu functions have been superseded. The new function should be used instead of the corresponding superseded function.
Superseded function New function
AppendMenu_win32_AppendMenu InsertMenuItem_win32_InsertMenuItem
ChangeMenu_win32_ChangeMenu SetMenuItemInfo_win32_SetMenuItemInfo
CheckMenuItem_win32_CheckMenuItem SetMenuItemInfo_win32_SetMenuItemInfo
GetMenuState_win32_GetMenuState GetMenuItemInfo_win32_GetMenuItemInfo
InsertMenu_win32_InsertMenu InsertMenuItem_win32_InsertMenuItem
ModifyMenu_win32_ModifyMenu SetMenuItemInfo_win32_SetMenuItemInfo

+$#A Using Extended Menus
· Using a menu template resource_win32_Using_a_Menu_Template_Resource.
· Creating a floating pop-up menu_win32_Creating_a_Floating_Pop_up_Menu.
· Using menu item bitmaps_win32_Using_Menu_Item_Bitmaps.
· Creating owner-drawn menu items_win32_Creating_Owner_Drawn_Menu_Items.
· Using custom check mark bitmaps_win32_Using_Custom_Check_Mark_Bitmaps.

+$#A Using a Menu Template Resource
{ewl msdncd, EWGraphic, group10809 26 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Using a Menu-Template Resource_win32_Using_a_Menu_Template_Resource_nt.
You typically include a menu in an application by creating a menu template resource and then loading the menu at run time. This section explains how to load a menu template resource and use it in your application. For information about creating a menu template resource, see the documentation included with your development tools.
+$#A Loading a Menu Template Resource
{ewl msdncd, EWGraphic, group10809 27 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Loading a Menu-Template Resource_win32_Loading_a_Menu_Template_Resource_nt.
You can load a menu template resource by calling the LoadMenu_win32_LoadMenu function, specifying the handle of the module that contains the resource and the menu template's identifier. LoadMenu returns a menu handle that you can use to assign the menu to a window. This window becomes the menu's owner window, receiving all the messages generated by the menu.
You can assign a menu to a window by using the SetMenu_win32_SetMenu function or by specifying the menu's handle in the hmenu parameter of the CreateWindowEx_win32_CreateWindowEx function when creating a window. Another way that you can assign a menu to a window is to specify a menu template when you register a window class; the template identifies the menu as the class menu for the given window class. Whenever you create a window of the given class, Windows automatically assigns the given menu to the window.
You can create a class menu by including the identifier of the menu template resource as the lpszMenuName member of a WNDCLASS_win32_WNDCLASS_str structure and then passing the address of the structure to the RegisterClass_win32_RegisterClass function.
+$#A Creating a Class Menu
{ewl msdncd, EWGraphic, group10809 28 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Creating a Class Menu_win32_Creating_a_Class_Menu_nt.
The following example shows how to create a class menu for an application, create a window that uses the class menu, and process menu commands in the window procedure.
HINSTANCE hinst;

int APIENTRY WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hinstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
MSG msg; // message
WNDCLASS wc; // windowclass data
HWND hwnd; // handle of main window

// Create the window class for the main window. Specify
// the identifier of the menu-template resource as the
// lpszMenuName member of the WNDCLASS structure to create
// the class menu.

wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MyMenuResource";
wc.lpszClassName = "MainWClass";

if (!RegisterClass(&wc))
return FALSE;

hinst = hinstance;

// Create the main window. Set the hmenu parameter to NULL so
// that Windows uses the class menu for the window.

hwnd = CreateWindow("MainWClass", "Sample Application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,
NULL);

if (hwnd == NULL)
return FALSE;

// Make the window visible and send a WM_PAINT message to the
// window procedure.

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Start the main message loop.

while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(hPrevInstance);
}

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

switch (uMsg) {
.
. // Process other window messages.
.

case WM_COMMAND:

// Test for the identifier of a command item.

switch(LOWORD(wParam)) {
case IDM_FI_OPEN:
DoFileOpen(); // application-defined
break;

case IDM_FI_CLOSE:
DoFileClose(); // application-defined
break;
.
. // Process other menu commands.
.

default:
break;

}
return 0;

.
. // Process other window messages.
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

+$#A Creating a Floating Pop-up Menu
{ewl msdncd, EWGraphic, group10809 29 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Creating a Floating Pop-up Menu_win32_Creating_a_Floating_Pop_up_Menu_nt.
To use a floating pop-up menu in an application, pass the handle of a pop-up menu to the TrackPopupMenu_win32_TrackPopupMenu or TrackPopupMenuEx_win32_TrackPopupMenuEx function. An application typically calls these functions in a window procedure when processing the WM_CONTEXTMENU_win32_WM_CONTEXTMENU message. For more information about this message, see Processing the WM_CONTEXTMENU Message_win32_Processing_the_WM_CONTEXTMENU_Message.
In addition to the pop-up menu handle, TrackPopupMenu requires that you specify the handle of the owner window, the position of the floating pop-up menu (in screen coordinates), and the mouse button that the user can use to choose an item. TrackPopupMenuEx requires the same parameters as TrackPopupMenu, but TrackPopupMenuEx also enables you to specify a portion of the screen that the menu should not obscure.
You can specify the position of a menu by providing x- and y-coordinates along with the TPM_CENTERALIGN, TPM_LEFTALIGN, or TPM_RIGHTALIGN flag. The flag specifies the position of the menu relative to the x- and y-coordinates.
You should permit the user to choose an item from a pop-up menu by using the same mouse button used to display the menu. To do this, specify either TPM_LEFTBUTTON or TPM_RIGHTBUTTON flag to indicate which mouse button the user can use to choose a menu item.
+$#A Processing the WM_CONTEXTMENU Message
The WM_CONTEXTMENU_win32_WM_CONTEXTMENU message is generated when an application's window procedure passes the WM_RBUTTONUP_win32_WM_RBUTTONUP or WM_NCRBUTTONUP_win32_WM_NCRBUTTONUP message to the DefWindowProc_win32_DefWindowProc function. The application can process this message to display a floating pop-up menu appropriate to a specific portion of its display. If the application does not display a menu, it should pass the message to DefWindowProc for default handling.
Following is an example of WM_CONTEXTMENU message processing as it might appear in an application's window procedure. The low-order and high-order words of the lParam parameter specify the screen coordinates of the mouse when the right mouse button is released. The application-defined OnContextMenu function returns TRUE if it displays a context menu or FALSE if it does not.
case WM_CONTEXTMENU:
if (!OnContextMenu(hwnd, LOWORD(lParam), HIWORD(lParam)))
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;

The following application-defined OnContextMenu function displays a floating pop-up menu if the specified mouse position is within the window's client area. A more sophisticated function might display one of several different menus depending on which portion of the client area is specified. To actually display the floating pop-up menu, this example calls an application-defined function called DisplayContextMenu. For a description of this function, see Displaying a Floating Pop-up Menu_win32_Displaying_a_Floating_Pop_up_Menu.
BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y)
{
RECT rc; // client area of window
POINT pt = { x, y }; // location of mouse click

// Get the bounding rectangle of the client area.

GetClientRect(hwnd, &rc);

// Convert the mouse position to client coordinates.

ScreenToClient(hwnd, &pt);

// If the position is in the client area, display a floating
// pop-up menu.

if (PtInRect(&rc, pt)) {
ClientToScreen(hwnd, &pt);
DisplayContextMenu(hwnd, pt);
return TRUE;
}

// Return FALSE if no menu is displayed.

return FALSE;
}

+$#A Displaying a Floating Pop-up Menu
The function shown in the following example displays a floating pop-up menu. This function might be called when an application's window procedure receives a WM_CONTEXTMENU_win32_WM_CONTEXTMENU message.
The application includes a menu resource identified by the string "PopupMenu". The top-level menu, or menu bar, simply contains a pop-up menu item. The application uses the TrackPopupMenu_win32_TrackPopupMenu function to display the pop-up menu associated with this menu item. (The top-level menu itself is not displayed because TrackPopupMenu requires the handle of a pop-up menu.)
VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // top-level menu
HMENU hmenuTrackPopup; // pop-up menu

// Load the menu resource.

if ((hmenu = LoadMenu(hinst, "PopupMenu")) == NULL)
return;

// TrackPopupMenu cannot display the top-level menu, so get
// the handle of the first pop-up menu.

hmenuTrackPopup = GetSubMenu(hmenu, 0);

// Display the floating pop-up menu. Track the right mouse
// button on the assumption that this function is called
// during WM_CONTEXTMENU processing.

TrackPopupMenu(hmenuTrackPopup,
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);

// Destroy the menu.

DestroyMenu(hmenu);
}

+$#A Using Menu Item Bitmaps
{ewl msdncd, EWGraphic, group10809 30 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview, Using Menu-Item Bitmaps_win32_Using_Menu_Item_Bitmaps_nt.
Windows can use a bitmap instead of a text string to display a menu item. To use a bitmap, you must set the MF_BITMAP flag for the menu item and specify the handle of the bitmap that Windows should display for the menu item. This section describes how to set the MF_BITMAP flag and retrieve the handle of a bitmap.
+$#A Setting the MFT_BITMAP Type Flag
The MFT_BITMAP type flag tells Windows to use a bitmap rather than a text string to display a menu item. A menu item's MFT_BITMAP flag must be set at run time; it cannot be set in a menu-template resource.
You can use the SetMenuItemInfo_win32_SetMenuItemInfo or InsertMenuItem_win32_InsertMenuItem function to set the MFT_BITMAP type flag. To change a menu item from a text item to a bitmap item, use SetMenuItemInfo. To add a new bitmap item to a menu, use the InsertMenuItem function.
+$#A Creating the Bitmap
{ewl msdncd, EWGraphic, group10809 31 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Creating the Bitmap_win32_Creating_the_Bitmap_nt.
When you set the MFT_BITMAP type flag for a menu item, you must also specify the handle of the bitmap that Windows should display for the menu item. You can provide the bitmap as a bitmap resource or create the bitmap at run time. If you use a bitmap resource, you can use the LoadBitmap_win32_LoadBitmap function to load the bitmap and obtain its handle.
To create the bitmap at run time, use graphics device interface (GDI) functions. GDI provides several ways to create a bitmap at run time, but developers typically use the following method:
1. Use the CreateCompatibleDC_win32_CreateCompatibleDC function to create a device context compatible with the device context used by the application's main window.
2. Use the CreateCompatibleBitmap_win32_CreateCompatibleBitmap function to create a bitmap compatible with the application's main window or use the CreateBitmap_win32_CreateBitmap function to create a monochrome bitmap.
3. Use the SelectObject_win32_SelectObject function to select the bitmap into the compatible device context.
4. Use GDI drawing functions, such as Ellipse_win32_Ellipse and LineTo_win32_LineTo, to draw an image into the bitmap.

For more information about bitmaps, see Bitmaps_win32_Bitmaps.+$#A Example of Menu Item Bitmaps
The example in this section creates two pop-up menus, each containing several bitmap menu items. For each pop-up menu, the application adds a corresponding pop-up item to the main window's menu bar.
The first pop-up menu contains menu items showing each of three chart types: pie, line, and bar. The bitmaps for these menu items are defined as resources and are loaded by using the LoadBitmap_win32_LoadBitmap function. Associated with this menu is a "Chart" pop-up item on the menu bar.
The second pop-up menu contains menu items showing each of the five line styles used with the CreatePen_win32_CreatePen function: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, and PS_DASHDOTDOT. The application creates the bitmaps for these menu items at run time using GDI drawing functions. Associated with this menu is a "Lines" pop-up item on the menu bar.
Defined in the application's window procedure are two static arrays of bitmap handles. One array contains the handles of the three bitmaps used for the Chart menu. The other contains the handles of the five bitmaps used for the Lines menu. When processing the WM_CREATE_win32_WM_CREATE message, the window procedure loads the chart bitmaps, creates the line bitmaps, and then adds the corresponding menu items. When processing the WM_DESTROY_win32_WM_DESTROY message, the window procedure deletes all of the bitmaps.
Following are the relevant portions of the application's header file.
// Menu item identifiers

#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3

#define IDM_SOLID 4
#define IDM_DASH 5
#define IDM_DASHDOT 6
#define IDM_DASHDOTDOT 7

// Number of items on the Chart and Lines menus

#define C_LINES 5
#define C_CHARTS 3

// Bitmap resource identifiers

#define IDB_PIE 1
#define IDB_LINE 2
#define IDB_BAR 3

// Dimensions of the line bitmaps

#define CX_LINEBMP 40
#define CY_LINEBMP 10

Following are the relevant portions of the window procedure. The window procedure performs most of its initialization by calling the application-defined LoadChartBitmaps, CreateLineBitmaps, and AddBitmapMenu functions, described later in this section.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
static HBITMAP aHbmLines[C_LINES];
static HBITMAP aHbmChart[C_CHARTS];
int i;

switch (uMsg) {

case WM_CREATE:

// Call application-defined functions to load the
// bitmaps for the chart menu and create those for
// the lines menu.

LoadChartBitmaps(aHbmChart);
CreateLineBitmaps(aHbmLines);

// Call an application-defined function to create pop-up
// menus containing the bitmap menu items. The function
// also adds a pop-up item to the window's menu bar.

AddBitmapMenu(
hwnd, // menu bar's owner window
"&Chart", // text of pop-up item on menu bar
IDM_PIE, // ID of first item on pop-up menu
aHbmChart, // array of bitmap handles
C_CHARTS // number of items on pop-up menu
);
AddBitmapMenu(hwnd, "&Lines", IDM_SOLID,
aHbmLines, C_LINES);
break;

case WM_DESTROY:
for (i = 0; i < C_LINES; i++)
DeleteObject(aHbmLines[i]);
for (i = 0; i < C_CHARTS; i++)
DeleteObject(aHbmChart[i]);
PostQuitMessage(0);
break;

.
. // Process additional messages here.
.

default:
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
return 0;
}

The application-defined LoadChartBitmaps function loads the bitmap resources for the chart menu by calling the LoadBitmap_win32_LoadBitmap function, as follows.
VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm)
{
paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE));
paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE));
paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR));
}

The application-defined CreateLineBitmaps function creates the bitmaps for the Lines menu by using GDI drawing functions. The function creates a memory DC with the same properties as the desktop window's DC. For each line style, the function creates a bitmap, selects it into the memory DC, and draws in it.
VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm)
{
HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);
COLORREF clrMenu = GetSysColor(COLOR_MENU);
HBRUSH hbrOld;
HPEN hpenOld;
HBITMAP hbmOld;
int fnDrawMode;
int i;

// Create a brush using the menu background color,
// and select it into the memory DC.

hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu));

// Create the bitmaps. Select each one into the memory
// DC that was created and draw in it.

for (i = 0; i < C_LINES; i++) {

// Create the bitmap and select it into the DC.

paHbm[i] = CreateCompatibleBitmap(hdcDesktop,
CX_LINEBMP, CY_LINEBMP);
hbmOld = SelectObject(hdcMem, paHbm[i]);

// Fill the background using the brush.

PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY);

// Create the pen and select it into the DC.

hpenOld = SelectObject(hdcMem,
CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0)));

// Draw the line. To preserve the background color where
// the pen is white use the R2_MASKPEN drawing mode.

fnDrawMode = SetROP2(hdcMem, R2_MASKPEN);
MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL);
LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2);
SetROP2(hdcMem, fnDrawMode);

// Delete the pen, and select the old pen and bitmap.

DeleteObject(SelectObject(hdcMem, hpenOld));
SelectObject(hdcMem, hbmOld);
}

// Delete the brush and select the original brush.

DeleteObject(SelectObject(hdcMem, hbrOld));

// Delete the memory DC and release the desktop DC.

DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
}

The application-defined AddBitmapMenu function creates a pop-up menu and adds the specified number of bitmap menu items to it. Then it adds a corresponding pop-up item to the specified window's menu bar.
VOID WINAPI AddBitmapMenu(
HWND hwnd, // window that owned the menu bar
LPSTR lpszText, // text of pop-up item on menu bar
UINT uID, // ID of first bitmap menu item
HBITMAP *paHbm, // bitmaps for the menu items
int cItems) // number bitmap menu items
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup = CreatePopupMenu();
MENUITEMINFO mii;
int i;

// Add the bitmap menu items to the pop-up menu.

for (i = 0; i < cItems; i++) {
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
mii.fType = MFT_BITMAP;
mii.wID = uID + i;
mii.dwTypeData = (LPSTR) (paHbm[i]);
InsertMenuItem(hmenuPopup, i, TRUE, &mii);
}

// Add a pop-up item to the menu bar.

mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_SUBMENU;
mii.fType = MFT_STRING;
mii.hSubMenu = hmenuPopup;
mii.dwTypeData = lpszText;
InsertMenuItem(hmenuBar,
GetMenuItemCount(hmenuBar), TRUE, &mii);
}

+$#A Creating Owner-Drawn Menu Items
If you need complete control over the appearance of a menu item, you can use an owner-drawn menu item in your application. This section describes the steps involved in creating and using an owner-drawn menu item.
+$#A Setting the MFT_OWNERDRAW Flag
You cannot define an owner-drawn menu item in your application's resource-definition file. Instead, you must create a new menu item or modify an existing one, so it has the MFT_OWNERDRAW type flag.
You can use the InsertMenuItem_win32_InsertMenuItem or SetMenuItemInfo_win32_SetMenuItemInfo function to specify an owner-drawn menu item. To place a new item on a menu, moving the other items down, use InsertMenuItem. To change the content of a menu, use SetMenuItemInfo.
When calling these two functions, you must specify the address of a MENUITEMINFO_win32_MENUITEMINFO_str structure, giving the properties of the new menu item or the properties you want to change for an existing menu item. To make an item an owner-drawn item, specify the MIIM_TYPE value for the fMask member and the MFT_OWNERDRAW value for the fType member.
By setting the appropriate members of the MENUITEMINFO structure, you can associate an application-defined value, which is called item data, with each menu item. To do so, specify the MIIM_DATA value for the fMask member and the application-defined value for the dwItemData member.
You can use item data with any type of menu item, but it is particularly useful for owner-drawn items. For example, suppose a structure contains information used to draw a menu item. An application might use the item data for a menu item to store a pointer to the structure. The item data is sent to the menu's owner window with the WM_MEASUREITEM_win32_WM_MEASUREITEM and WM_DRAWITEM_win32_WM_DRAWITEM messages. To retrieve the item data for a menu at any time, use the GetMenuItemInfo_win32_GetMenuItemInfo function.
+$#A Owner-Drawn Menus and the WM_MEASUREITEM Message
Before Windows displays an owner-drawn menu item for the first time, it sends the WM_MEASUREITEM_win32_WM_MEASUREITEM message to the window procedure of the window that owns the item's menu. This message contains a pointer to a MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure that identifies the item and contains the item data that an application may have assigned to it. The window procedure must fill the itemWidth and itemHeight members of the structure before returning from processing the message. Windows uses the information in these members when creating the bounding rectangle in which an application draws the menu item. It also uses the information to detect when the user chooses the item.
+$#A Owner-Drawn Menus and the WM_DRAWITEM Message
Whenever the item must be drawn (for example, when it is first displayed or when the user selects it), Windows sends the WM_DRAWITEM_win32_WM_DRAWITEM message to the window procedure of the menu's owner window. This message contains a pointer to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure, which contains information about the item, including the item data that an application may have assigned to it. In addition, DRAWITEMSTRUCT contains flags that indicate the state of the item (such as whether it is grayed or checked) as well as a bounding rectangle and a DC that the application uses to draw the item.
An application must do the following while processing the WM_DRAWITEM message:
1. Determine the type of drawing that is necessary. To do so, check the itemAction member of the DRAWITEMSTRUCT structure.
2. Draw the menu item appropriately, using the bounding rectangle and DC obtained from the DRAWITEMSTRUCT structure. The application must draw only within the bounding rectangle. For performance reasons, Windows does not clip portions of the image that are drawn outside the rectangle.
3. Restore all GDI objects selected for the menu item's DC.

If the user selects the menu item, Windows sets the itemAction member of the DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure to the ODA_SELECT value and sets the ODS_SELECTED value in the itemState member. This is an application's cue to redraw the menu item to indicate that it is selected.
+$#A Example of Owner-Drawn Menu Items
The example in this section uses owner-drawn menu items in a pop-up menu. The menu items select specific font attributes, and the application displays each menu item using a font that has the corresponding attribute. For example, the Italic menu item is displayed in an italic font. The Character pop-up item on the menu bar opens the pop-up menu.
The menu bar and pop-up menu are defined initially by an extended menu-template resource. Because a menu template cannot specify owner-drawn items, the pop-up menu initially contains four text menu items with the following strings: "Regular," "Bold," "Italic," and "Underline." The application's window procedure changes these to owner-drawn items when it processes the WM_CREATE_win32_WM_CREATE message. When it receives the WM_CREATE message, the window procedure calls the application-defined OnCreate function, which performs the following steps for each menu item:
1. Allocates an application-defined MYITEM structure.
2. Gets the text of the menu item and saves it in the application-defined MYITEM structure.
3. Creates the font used to display the menu item and saves its handle in the application-defined MYITEM structure.
4. Changes the menu item type to MFT_OWNERDRAW and saves a pointer to the application-defined MYITEM structure as item data.

Because a pointer to each application-defined MYITEM structure is saved as item data, it is passed to the window procedure in conjunction with the WM_MEASUREITEM_win32_WM_MEASUREITEM and WM_DRAWITEM_win32_WM_DRAWITEM messages for the corresponding menu item. The pointer is contained in the itemData member of both the MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str and DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structures.
A WM_MEASUREITEM message is sent for each owner-drawn menu item the first time that it is displayed. The application processes this message by selecting the font for the menu item into a DC and then determining the space required to display the menu item text in that font. The font and menu item text are both specified by the menu item's MYITEM structure (the structure defined by the application). The application determines the size of the text by using the GetTextExtentPoint_win32_GetTextExtentPoint function.
The window procedure processes the WM_DRAWITEM_win32_WM_DRAWITEM message by displaying the menu item text in the appropriate font. The font and menu item text are both specified by the menu item's MYITEM structure. The application selects text and background colors appropriate to the menu item's state.
The window procedure processes the WM_DESTROY_win32_WM_DESTROY message to destroy fonts and free memory. The application deletes the font and frees the application-defined MYITEM structure for each menu item.
Following are the relevant portions of the application's header file.
// Menu item identifiers for the Character menu

#define IDM_CHARACTER 10
#define IDM_REGULAR 11
#define IDM_BOLD 12
#define IDM_ITALIC 13
#define IDM_UNDERLINE 14

// Structure associated with menu items

typedef struct tagMYITEM {
HFONT hfont;
int cchItemText;
char szItemText[1];
} MYITEM;

#define CCH_MAXITEMTEXT 256

Following are the relevant portions of the application's window procedure and its associated functions.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg) {
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;

case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;

case WM_MEASUREITEM:
OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);
return TRUE;

case WM_DRAWITEM:
OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam);
return TRUE;

.
. // Additional message processing goes here.
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;

// Get the handle of the pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Modify each menu item. Assume that the IDs IDM_REGULAR
// through IDM_UNDERLINE are consecutive numbers.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Allocate an item structure, leaving space for a
// string of up to CCH_MAXITEMTEXT characters.

pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,
sizeof(MYITEM) + CCH_MAXITEMTEXT);

// Save the item text in the item structure.

mii.fMask = MIIM_TYPE;
mii.dwTypeData = pMyItem->szItemText;
mii.cch = CCH_MAXITEMTEXT;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem->cchItemText = mii.cch;

// Reallocate the structure to the minimum required size.

pMyItem = (MYITEM *) LocalReAlloc(pMyItem,
sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);

// Create the font used to draw the item.

pMyItem->hfont = CreateMenuItemFont(uID);

// Change the item to an owner-drawn item, and save
// the address of the item structure as item data.

mii.fMask = MIIM_TYPE | MIIM_DATA;
mii.fType = MFT_OWNERDRAW;
mii.dwItemData = (DWORD) pMyItem;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}
return TRUE;
}

HFONT CreateMenuItemFont(UINT uID)
{
LOGFONT lf;

ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = 20;
lstrcpy(lf.lfFaceName, "Times New Roman");

switch (uID) {
case IDM_BOLD:
lf.lfWeight = FW_HEAVY;
break;

case IDM_ITALIC:
lf.lfItalic = TRUE;
break;

case IDM_UNDERLINE:
lf.lfUnderline = TRUE;
break;
}
return CreateFontIndirect(&lf);
}

VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
MYITEM *pMyItem;

// Get the handle of the pop-up menu.

mii.fMask = MIIM_SUBMENU; /* information to get */
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Free resources associated with each menu item.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Get the item data.

mii.fMask = MIIM_DATA;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem = (MYITEM *) mii.dwItemData;

// Destroy the font and free the item structure.

DeleteObject(pMyItem->hfont);
LocalFree(pMyItem);
}
}

VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
{
MYITEM *pMyItem = (MYITEM *) lpmis->itemData;
HDC hdc = GetDC(hwnd);
HFONT hfntOld = SelectObject(hdc, pMyItem->hfont);
SIZE size;

GetTextExtentPoint(hdc, pMyItem->szItemText,
pMyItem->cchItemText, &size);

lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;

SelectObject(hdc, hfntOld);
ReleaseDC(hwnd, hdc);
}

VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis)
{
MYITEM *pMyItem = (MYITEM *) lpdis->itemData;
COLORREF clrPrevText, clrPrevBkgnd;
HFONT hfntPrev;
int x, y;

// Set the appropriate foreground and background colors.

if (lpdis->itemState & ODS_SELECTED) {
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHTTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_HIGHLIGHT));
}
else {
clrPrevText = SetTextColor(lpdis->hDC,
GetSysColor(COLOR_MENUTEXT));
clrPrevBkgnd = SetBkColor(lpdis->hDC,
GetSysColor(COLOR_MENU));
}

// Determine where to draw and leave space for a check mark.

x = lpdis->rcItem.left;
y = lpdis->rcItem.top;
x += LOWORD(GetMenuCheckMarkDimensions());

// Select the font and draw the text.

hfntPrev = SelectObject(lpdis->hDC, pMyItem->hfont);
ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE,
&lpdis->rcItem, pMyItem->szItemText,
pMyItem->cchItemText, NULL);

// Restore the original font and colors.

SelectObject(lpdis->hDC, hfntPrev);
SetTextColor(lpdis->hDC, clrPrevText);
SetBkColor(lpdis->hDC, clrPrevBkgnd);
}

+$#A Using Custom Check Mark Bitmaps
{ewl msdncd, EWGraphic, group10809 32 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Using Custom Check Mark Bitmaps_win32_Using_Custom_Check_Mark_Bitmaps_nt.
Windows provides a default check mark bitmap for displaying next to a menu item that is checked. You can customize an individual menu item by providing a pair of bitmaps to replace the default check mark bitmap. Windows displays one bitmap when the item is checked and the other when it is unchecked. This section describes the steps involved in creating and using custom check mark bitmaps.
+$#A Creating Custom Check Mark Bitmaps
{ewl msdncd, EWGraphic, group10809 33 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Creating Custom Check Mark Bitmaps_win32_Creating_Custom_Check_Mark_Bitmaps_nt.
A custom check mark bitmap must be the same size as the default check mark bitmap. You can retrieve the default check mark size of the bitmap by calling the GetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensions function. The low-order word of this function's return value specifies the width; the high-order word specifies the height.
You can use bitmap resources to provide check mark bitmaps. However, because the required bitmap size varies depending on the display type, you may need to resize the bitmap at run time by using the StretchBlt_win32_StretchBlt function. Depending on the bitmap, the distortion caused by sizing could produce unacceptable results.
Instead of using a bitmap resource, you can create a bitmap at run time by using GDI functions. For the typical method, you follow these steps:
1. Use the CreateCompatibleDC_win32_CreateCompatibleDC function to create a DC compatible with the DC used by the application's main window.
2. Use the CreateCompatibleBitmap_win32_CreateCompatibleBitmap function to create a bitmap compatible with the application's main window, or use the CreateBitmap_win32_CreateBitmap function to create a monochrome bitmap.
3. Use the SelectObject_win32_SelectObject function to select the bitmap into the compatible DC.
4. Use GDI drawing functions, such as Ellipse_win32_Ellipse and LineTo_win32_LineTo, to draw an image into the bitmap.

+$#A Associating Bitmaps with a Menu Item
{ewl msdncd, EWGraphic, group10809 34 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Associating Bitmaps with a Menu Item_win32_Associating_Bitmaps_with_a_Menu_Item_nt.
You associate a pair of check mark bitmaps with a menu item by passing the handles of the bitmaps to the SetMenuItemBitmaps_win32_SetMenuItemBitmaps function. The hbmUnchecked parameter identifies the unchecked bitmap, and the hbmChecked parameter identifies the checked bitmap. If you want to remove one or both check marks from a menu item, you can set the hbmUnchecked or hbmChecked parameter, or both, to NULL.
+$#A Setting the Check Mark Attribute
{ewl msdncd, EWGraphic, group10809 35 /a "SDK.BMP"} Windows 95 Overview Setting the Check Mark AttributeFor more information, see the Windows NT overview Setting the Check Mark Attribute_win32_Setting_the_Check_Mark_Attribute_nt.
The CheckMenuItem_win32_CheckMenuItem function sets a menu item's check mark attribute to either checked or unchecked. You can specify the MF_CHECKED value to set the check mark attribute to checked and the MF_UNCHECKED value to set it to unchecked. You can also set the check state of a menu item by using the SetMenuItemInfo_win32_SetMenuItemInfo function.
Sometimes a group of menu items represents a set of mutually exclusive options. By using the CheckMenuRadioItem_win32_CheckMenuRadioItem function, you can check one menu item while simultaneously removing the check mark from all other menu items in the group.
+$#A Example of Using Custom Check Mark Bitmaps
The example in this section assigns custom check mark bitmaps to menu items in two pop-up menus. The menu items in the first pop-up menu specify character attributes: bold, italic, and underline. Each menu item can be either checked or unchecked. For these menu items, the example uses check mark bitmaps that resemble the checked and unchecked states of a check box control.
The menu items in the second pop-up menu specify paragraph alignment settings: left, centered, and right. Only one of these menu items is checked at any given time. For these menu items, the example uses check mark bitmaps that resemble the checked and unchecked states of a radio button control.
The window procedure processes the WM_CREATE_win32_WM_CREATE message by calling the application-defined OnCreate function. The application-defined OnCreate function creates the four check mark bitmaps and then assigns them to their appropriate menu items by using the SetMenuItemBitmaps_win32_SetMenuItemBitmaps function.
To create each bitmap, the application-defined OnCreate function calls the application-defined CreateMenuBitmaps function, specifying a pointer to a bitmap-specific drawing function. The application-defined CreateMenuBitmaps function creates a monochrome bitmap of the required size, selects it into a memory DC, and erases the background. Then it calls the specified drawing function to fill in the foreground.
The four application-defined drawing functions are DrawCheck, DrawUncheck, DrawRadioCheck, and DrawRadioUncheck. They draw a rectangle with an X, an empty rectangle, an ellipse containing a smaller filled ellipse, and an empty ellipse, respectively.
The window procedure processes the WM_DESTROY_win32_WM_DESTROY message by deleting the check mark bitmaps. It retrieves each bitmap handle by using the GetMenuItemInfo_win32_GetMenuItemInfo function and then passes the handle to the DeleteObject_win32_DeleteObject function.
When the user chooses a menu item, a WM_COMMAND_win32_WM_COMMAND message is sent to the owner window. For menu items on the Character menu, the window procedure calls the application-defined CheckCharacterItem function. For items on the Paragraph menu, the window procedure calls the application-defined CheckParagraphItem function.
Each item on the Character menu can be checked and unchecked independently. Therefore, the application-defined CheckCharacterItem function simply toggles the specified menu item's check state. First, the function calls the GetMenuItemInfo_win32_GetMenuItemInfo function to get the current menu item state. Then it toggles the MFS_CHECKED state flag and sets the new state by calling the SetMenuItemInfo_win32_SetMenuItemInfo function.
Unlike character attributes, only one paragraph alignment can be selected at a time. Therefore, the application-defined CheckParagraphItem function checks the specified menu item and removes the check mark from all other items on the menu. To do so, it calls the CheckMenuRadioItem_win32_CheckMenuRadioItem function.
Following are the relevant portions of the application's header file.
// Menu item identifiers for the Character menu

#define IDM_CHARACTER 10
#define IDM_BOLD 11
#define IDM_ITALIC 12
#define IDM_UNDERLINE 13

// Menu item identifiers for the Paragraph menu

#define IDM_PARAGRAPH 20
#define IDM_LEFT 21
#define IDM_CENTER 22
#define IDM_RIGHT 23

// Function-pointer type for drawing functions

typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size);

Following are the relevant portions of the application's window procedure and related functions.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg) {
case WM_CREATE:
if (!OnCreate(hwnd))
return -1;
break;

case WM_DESTROY:
OnDestroy(hwnd);
PostQuitMessage(0);
break;

case WM_COMMAND:
switch (LOWORD(wParam)) {

case IDM_BOLD:
case IDM_ITALIC:
case IDM_UNDERLINE:
CheckCharacterItem(hwnd, LOWORD(wParam));
break;

case IDM_LEFT:
case IDM_CENTER:
case IDM_RIGHT:
CheckParagraphItem(hwnd, LOWORD(wParam));
break;

.
. // Process other commands here.
.

}
break;

.
. // Process other messages here.
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Get the state of the specified menu item.

mii.fMask = MIIM_STATE; // information to get
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

// Toggle the checked state.

mii.fState ^= MFS_CHECKED;
SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
}

VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Check the specified item and uncheck all the others.

CheckMenuRadioItem(
hmenuPopup, // handle of pop-up menu
IDM_LEFT, // first item in range
IDM_RIGHT, // last item in range
uID, // item to check
MF_BYCOMMAND // IDs, not positions
);
}

BOOL WINAPI OnCreate(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;
UINT uID;
HBITMAP hbmChecked;
HBITMAP hbmUnchecked;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Create the checked and unchecked bitmaps.

hbmChecked = CreateMenuBitmap(DrawCheck);
hbmUnchecked = CreateMenuBitmap(DrawUncheck);

// Set the checkmark bitmaps for each menu item.

for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++) {
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Create the checked and unchecked bitmaps.

hbmChecked = CreateMenuBitmap(DrawRadioCheck);
hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck);

// Set the checkmark bitmaps for each menu item.

for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++) {
SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,
hbmUnchecked, hbmChecked);
}

// Initially check the IDM_LEFT paragraph item.

CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT,
IDM_LEFT, MF_BYCOMMAND);
return TRUE;
}

HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw)
{
// Create a DC compatible with the desktop window's DC.

HWND hwndDesktop = GetDesktopWindow();
HDC hdcDesktop = GetDC(hwndDesktop);
HDC hdcMem = CreateCompatibleDC(hdcDesktop);

// Determine the required bitmap size.

DWORD dwExt = GetMenuCheckMarkDimensions();
SIZE size = { LOWORD(dwExt), HIWORD(dwExt) };

// Create a monochrome bitmap and select it.

HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL);
HBITMAP hbmOld = SelectObject(hdcMem, hbm);

// Erase the background and call the drawing function.

PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS);
(*lpfnDraw)(hdcMem, size);

// Clean up.

SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
ReleaseDC(hwndDesktop, hdcDesktop);
return hbm;
}

VOID WINAPI DrawCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, size.cx, size.cy);
MoveToEx(hdc, 0, size.cy - 1, NULL);
LineTo(hdc, size.cx - 1, 0);
SelectObject(hdc, hbrOld);
}

VOID WINAPI DrawUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}

VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2);
SelectObject(hdc, hbrOld);
}

VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size)
{
HBRUSH hbrOld;
hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));
Ellipse(hdc, 0, 0, size.cx, size.cy);
SelectObject(hdc, hbrOld);
}

VOID WINAPI OnDestroy(HWND hwnd)
{
HMENU hmenuBar = GetMenu(hwnd);
HMENU hmenuPopup;
MENUITEMINFO mii;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Get the check mark bitmaps and delete them.

mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get
GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);
hmenuPopup = mii.hSubMenu;

// Get the check mark bitmaps and delete them.

mii.fMask = MIIM_CHECKMARKS;
GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii);
DeleteObject(mii.hbmpChecked);
DeleteObject(mii.hbmpUnchecked);
}

+$#A Extended Menu Reference
The functions, structures, and messages used with extended menus can be grouped as follows. Some of these application programming interface (API) elements are also used with menus.
Menu Handles
GetMenu_win32_GetMenuGetSubMenu_win32_GetSubMenuGetSystemMenu_win32_GetSystemMenu
Menu-Item Information
GetMenuItemID_win32_GetMenuItemIDGetMenuItemInfo_win32_GetMenuItemInfoSetMenuItemInfo_win32_SetMenuItemInfoMENUITEMINFO_win32_MENUITEMINFO_str
Default Menu Items
GetMenuDefaultItem_win32_GetMenuDefaultItemSetMenuDefaultItem_win32_SetMenuDefaultItem
Menu-Item States
CheckMenuRadioItem_win32_CheckMenuRadioItemEnableMenuItem_win32_EnableMenuItemGetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensionsHiliteMenuItem_win32_HiliteMenuItemSetMenuItemBitmaps_win32_SetMenuItemBitmaps
Menu Templates
LoadMenu_win32_LoadMenuLoadMenuIndirect_win32_LoadMenuIndirectMENUITEMTEMPLATE_win32_MENUITEMTEMPLATE_strMENUITEMTEMPLATEHEADER_win32_MENUITEMTEMPLATEHEADER_strMENUEX_TEMPLATE_HEADER_win32_MENUEX_TEMPLATE_HEADER_strMENUEX_TEMPLATE_ITEM_win32_MENUEX_TEMPLATE_ITEM_str
Menu Creation and Modification
CreateMenu_win32_CreateMenuCreatePopupMenu_win32_CreatePopupMenuDeleteMenu_win32_DeleteMenuDestroyMenu_win32_DestroyMenuDrawMenuBar_win32_DrawMenuBarInsertMenuItem_win32_InsertMenuItemRemoveMenu_win32_RemoveMenu
Menu Display
SetMenu_win32_SetMenuTrackPopupMenu_win32_TrackPopupMenuTrackPopupMenuEx_win32_TrackPopupMenuExWM_CONTEXTMENU_win32_WM_CONTEXTMENU
Menu Messages
WM_COMMAND_win32_WM_COMMANDWM_INITMENU_win32_WM_INITMENUWM_INITMENUPOPUP_win32_WM_INITMENUPOPUPWM_MENUSELECT_win32_WM_MENUSELECTWM_SYSCOMMAND_win32_WM_SYSCOMMAND
+$#A Extended Menu Functions
The following are the extended menu functions.
CheckMenuRadioItem_win32_CheckMenuRadioItemGetMenuDefaultItem_win32_GetMenuDefaultItemGetMenuItemInfo_win32_GetMenuItemInfoGetMenuItemRect_win32_GetMenuItemRectInsertMenuItem_win32_InsertMenuItemMenuItemFromPoint_win32_MenuItemFromPointSetMenuDefaultItem_win32_SetMenuDefaultItemSetMenuItemInfo_win32_SetMenuItemInfoTrackPopupMenuEx_win32_TrackPopupMenuEx
+$#A Extended Menu Structures
The following are the extended menu structures.
MENUEX_TEMPLATE_HEADER_win32_MENUEX_TEMPLATE_HEADER_strMENUEX_TEMPLATE_ITEM_win32_MENUEX_TEMPLATE_ITEM_strMENUITEMINFO_win32_MENUITEMINFO_strTPMPARAMS_win32_TPMPARAMS_str
+$#A Extended Menu Messages
The following are the extended menu messages.
WM_CONTEXTMENU_win32_WM_CONTEXTMENU

+$#AK Menus (Windows NT)
A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with the menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.
This Windows NT overview describes menus and explains how to use them in a Win32-based application.
+$#A About Menus
{ewl msdncd, EWGraphic, group10806 0 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview About Extended Menus_win32_About_Extended_Menus.
A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with that menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.
Win32 also provides extended menu functions that give applications an improved way to create and control menus. The extended functions supersede the menu functions described in this topic and should be used in Win32-based applications whenever possible. For information about extended menu functions, see Extended Menus_win32_Extended_Menus.
+$#A Menu Bars and Pop-up Menus
{ewl msdncd, EWGraphic, group10806 1 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Bars and Pop-up Menus_win32_Menu_Bars_and_Pop_up_Menus.
The menus in an application written for Microsoft Windows are arranged hierarchically. A menu bar (sometimes called a top-level menu) resides at the uppermost level of the hierarchy; the lower levels consist of pop-up menus (also known as submenus).
The items in a menu bar represent the main categories of commands provided by an application. Selecting an item from the menu bar typically activates a pop-up menu whose items correspond to the commands in a given category. For example, a menu bar might contain a File item that, when the user selects it, activates a pop-up menu with menu items such as New, Open, Save, and so on.
Only an overlapped or pop-up window can contain a menu bar; a child window cannot contain one. A menu bar is always visible. If the window has a title bar, Windows positions the menu bar just below it. By contrast, a pop-up menu is not visible until the user selects a menu item that activates it. When this menu is displayed, it is attached to the title bar.
Every menu must have an owner window. Windows sends messages to a menu's owner window when the user selects the menu or chooses a command from the menu. These messages are described in Menu Messages Overview_win32_Menu_Messages_Overview_nt.
+$#A Floating Pop-up Menus
{ewl msdncd, EWGraphic, group10806 2 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Floating Pop-up Menus_win32_Floating_Pop_up_Menus.
Windows provides a special type of pop-up menu called a floating pop-up menu. A floating pop-up menu is not attached to the menu bar; it can appear anywhere on the screen. An application typically associates a floating pop-up menu with a portion of a window such as the client area or with a specific object such as an icon.
A floating pop-up menu remains hidden until the user selects it (for example, by clicking the appropriate screen location with the mouse.) A floating pop-up menu is typically displayed at the position of the caret or mouse cursor.
+$#A The System Menu
{ewl msdncd, EWGraphic, group10806 3 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview The System Menu_win32_The_System_Menu.
A System menu (also known as the Control menu) is a pop-up menu defined and managed almost exclusively by the system. It is represented by a small box containing a horizontal line that is displayed at the left end of the title bar. When the user clicks the box, Windows displays the System menu. Windows also displays the System menu when the user clicks the icon of a minimized application.
A System menu provides a standard set of menu items the user can choose to change a window's size or position, close the application, or activate the Windows Task List. Items in a System menu can be added, deleted, and modified, but most applications just use the standard set of menu items. An overlapped, pop-up, or child window can have a System menu. It is uncommon for an overlapped or pop-up window not to include a System menu.
When the user chooses a command from a System menu, Windows sends a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message to the menu's owner window. In most applications, the window procedure does not process messages from a System menu. Instead, it simply passes the messages to the DefWindowProc_win32_DefWindowProc function for system-default processing of the message. If a command is added to a System menu, the window procedure must process the command.
+$#A Menu Handles
{ewl msdncd, EWGraphic, group10806 4 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Handles_win32_Menu_Handles.
The system generates a unique handle for each menu. A menu handle is a value of the HMENU type. An application must specify a menu handle in many of the Windows menu functions. You receive the handle of a menu bar when you create the menu or loads a menu resource. For information about creating menus and loading menus, see Menu Creation_win32_Menu_Creation.
The GetMenu_win32_GetMenu or GetSubMenu_win32_GetSubMenu function is used to retrieve the handle of a menu that has been created or loaded. GetMenu returns the handle of the menu bar assigned to the specified window. GetSubMenu returns the handle of a pop-up menu within the specified menu bar or pop-up menu. The GetSystemMenu_win32_GetSystemMenu function is used to retrieve the handle of a window's System menu.
+$#A Class Menus
{ewl msdncd, EWGraphic, group10806 5 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Class Menus_win32_Class_Menus.
When registering a window class, the developer can specify a default menu for that class. This default menu is known as the class menu. To specify a class menu, the developer must assign the name of the menu, as given in your application's resource-definition file, to the lpszMenuName member of the WNDCLASS_win32_WNDCLASS_str structure used to register the window class. After the window class has been registered, each window of that class will have the specified class menu. The developer can override the class menu by specifying the handle of a different menu when creating a window of that class. For more information about window classes, see Window Classes_win32_Window_Classes.
+$#A Menu Items
{ewl msdncd, EWGraphic, group10806 6 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Items_win32_Menu_Items.
There are two kinds of menu items in Win32 applications: command items and pop-up items. When the user chooses a command item, Windows sends a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message to the window procedure of the menu's owner window. A command item on a System menu generates a WM_SYSCOMMAND message; command items in other menus generate WM_COMMAND messages. When the user selects a pop-up item, Windows displays a pop-up menu but doesn't send a command message to the window procedure.
A menu bar typically contains pop-up items, but may also contain command items. A pop-up menu typically contains command items, but may also contain pop-up items. By adding pop-up items to a pop-up menu, you can nest pop-up menus to an arbitrary depth. If you include a pop-up item in a pop-up menu, Windows automatically displays a small arrow to the right of the menu item text as a visual cue to the user.
+$#A Menu-Item Identifier
{ewl msdncd, EWGraphic, group10806 7 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Menu Item Identifier_win32_Menu_Item_Identifier.
Associated with each menu item is a unique, application-defined integer, called an identifier. When the user chooses a command item, Windows sends the item's identifier to the owner window as part of a WM_COMMAND_win32_WM_COMMAND message. The window procedure examines the identifier to determine the source of the message, and processes the message accordingly. In addition, you can specify a menu item using its identifier when you call menu functions ¾ for example, to enable or disable a menu item.
A menu identifier must be a value from 0 to 65,535, even though it is a 32-bit integer. This is because the WM_COMMAND message passes a menu identifier as the low-order word of its wParam parameter.
Beginning with the Win32 API, pop-up menu items have identifiers just as other menu items do. However, Windows does not send a WM_COMMAND message when a pop-up menu item is selected. Instead, Windows activates the pop-up menu associated with the pop-up menu item.
+$#A Checked and Unchecked Menu Items
{ewl msdncd, EWGraphic, group10806 8 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Checked and Unchecked Menu Items_win32_Checked_and_Unchecked_Menu_Items.
A menu item can be either checked or unchecked. When a menu item is checked, Windows uses a bitmap to display a check mark to the left of the menu item. When the menu item is unchecked, Windows does not display the check mark. Only menu items in a pop-up menu can be checked; menu items in a menu bar can not be checked.
An application typically uses check marks to indicate menu options that are in effect. For example, when the user selects paragraph formatting options from a menu in a word processing application, the application places check marks next to the selected options. The following illustration shows check marks next to the Left and Single Space options in a word processing application's Paragraph menu.
A check mark attribute controls whether a menu item is checked or not. A menu item's check mark attribute can be set by using the CheckMenuItem_win32_CheckMenuItem function. The GetMenuState_win32_GetMenuState function is used to determine whether a menu item is currently checked or unchecked.
Windows provides a default bitmap for displaying check marks. The developer, however, can substitute other bitmaps and direct Windows to use them instead of the default. Windows requires, however, that these check mark bitmaps have the same dimensions as the default check mark bitmap. The GetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensions function can be used to retrieve the default dimensions.
Two bitmaps can be associated with each menu item: one for when the menu item is checked and another for when it is unchecked. The SetMenuItemBitmaps_win32_SetMenuItemBitmaps function can be used to associate bitmaps with a menu item.
The developer can either provide check mark bitmaps as bitmap resources or create the bitmaps at run time by using graphic device interface (GDI) bitmap functions. A bitmap resource, however, isn't suitable for all video resolutions and aspect ratios, so the bitmap must be sized appropriately. The check mark bitmaps can be either monochrome or color. However, on a color screen the bitmap colors are inverted when the menu item is highlighted, and this result may be undesirable. For more information about bitmaps, see Bitmaps_win32_Bitmaps.
+$#A Enabled, Disabled, and Graye Menu Items
{ewl msdncd, EWGraphic, group10806 9 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Enabled, Disabled, and Grayed Menu Items_win32_Enabled_Disabled_and_Grayed_Menu_Items.
A menu item can be enabled, disabled, or grayed. By default, a menu item is enabled. When the user chooses an enabled menu item, Windows sends a command message to the corresponding window procedure or displays the corresponding pop-up menu, depending on whether the menu item is a command item or a pop-up item.
When menu items are not available to the user, they should be disabled or grayed. Disabled or grayed menu items do not generate a command message or display a pop-up menu when a user chooses them. Typically, applications gray an unavailable menu item to provide a visual clue to the user that a command is not available. A disabled menu item does not provide this visual clue. A disabled item can serve as a heading for group related menu items. (For example, a Save heading can be listed and then a series of commands such as Save File, Save All, and Save As). A grayed item can be used when an action is not appropriate. (For example, the Print command in the File menu can be grayed when the system does not have a printer installed.)
The EnableMenuItem_win32_EnableMenuItem function is used to enable, disable, or gray a menu item, and the GetMenuState_win32_GetMenuState function is used to determine whether a menu item is enabled, disabled, or grayed.
+$#A Menu-Item Separators and Line Breaks
{ewl msdncd, EWGraphic, group10806 10 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Menu Item Separators and Line Breaks_win32_Menu_Item_Separators_and_Line_Breaks.
Windows provides a special type of menu item, called a separator, that consists of a horizontal line. A separator menu item can be used in a pop-up menu to divide a menu into groups of related items. A separator item cannot be used in a menu bar. The user cannot select a separator menu item, and a separator does not send command messages to the window procedure.
When a menu bar contains more items than will fit on one line, Windows automatically breaks it into two or more lines. You can force a line break at a specific item on a menu bar by assigning the MF_MENUBREAK flag to the item. Windows places that item and all remaining items on a new line.
The MF_MENUBREAK and MF_MENUBARBREAK flags are used to divide items in a pop-up menu into multiple columns. When MF_MENUBREAK flag is assigned to an item, Windows places it and all remaining items in a new column. The MF_MENUBARBREAK flag creates a similar effect, except Windows includes a vertical line in the menu to separate the new column from the old one.
+$#A Menu-Item Position
{ewl msdncd, EWGraphic, group10806 11 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Menu-Item Position_win32_Menu_Item_Position.
Windows assigns an integer position value to each item on a menu and uses the value to keep track of each item's position in the menu. Position values are zero-based; that is, the first item on a menu occupies position zero, the next position one, and so on. In a menu bar, the leftmost menu item is considered the first item, so it occupies position zero; in a pop-up menu, the uppermost menu item occupies position zero. Windows assigns position values to menu-item separators. The following illustration shows the position values of both items and separators in a menu bar and a pop-up menu.
{ewc msdncd, EWGraphic, group10806 12 /a "SDK_05.bmp"}
A number of functions are used to add an item to a menu, delete an item from a menu, or modify an existing item on a menu. When one of these functions is called, the item to be modified is identified either by its position or by its identifier. Either the MF_BYPOSITION or MF_BYCOMMAND flag is also specified. For more information about modifying the contents of a menu, see Menu Modifications_win32_Menu_Modifications.
+$#A Highlighted Menu Items
{ewl msdncd, EWGraphic, group10806 13 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Highlighted Menu Items_win32_Highlighted_Menu_Items.
Windows automatically highlights menu items as the user selects them. Highlighting can, however, be explicitly added or removed from a menu item on the menu bar by calling the HiliteMenuItem_win32_HiliteMenuItem function. This function has no effect on menu items in pop-up menus. When you use HiliteMenuItem to highlight a menu item, however, the menu item only appears to be selected; if the user presses the ENTER key, the highlighted item is not chosen.
+$#A Owner-drawn Menu Items
An application can completely control the appearance of a menu item by using an owner-drawn item. Owner-drawn items require an application to take total responsibility for drawing selected (highlighted), checked, and unchecked states. For example, if an application provided a font menu it could draw each menu item by using the corresponding font; the item for roman would be drawn with a roman font, the item for italic would be drawn in italic, and so on.
For information on how to create owner-drawn menu items, see Creating Owner-drawn Menu Items_win32_Creating_Owner_Drawn_Menu_Items.
+$#A Help Item
The developer should provide a standard interface for the user, including a Help item on an application's menu bars. The item should read "Help," and it should be the rightmost item on the menu bar.
+$#A Keyboard Access to Menus
{ewl msdncd, EWGraphic, group10806 14 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Keyboard Access to Menus_win32_Keyboard_Access_to_Menus.
Windows provides a standard keyboard interface for menus. You can enhance this interface by including mnemonics and accelerator keys for your menu items. The following three sections describe the standard keyboard interface, mnemonics, and accelerator keys.
+$#A Standard Keyboard Interface
{ewl msdncd, EWGraphic, group10806 15 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Standard Keyboard Interface_win32_Standard_Keyboard_Interface.
Windows is designed to work with or without a mouse or other pointing device. Because Windows provides a standard keyboard interface the user can use the keyboard to select menu items. Special code is not needed to support this keyboard interface. An application receives a command message whether a menu item is selected through the keyboard or by using a mouse. The standard keyboard interface processes the following keystrokes.
Keystroke Action
ALT Toggles in and out of menu bar mode.
ALT+SPACEBAR Displays the System menu.
ESC Exits menu mode.
RIGHT ARROW Cycles to the next top-level menu item. This keystroke cycles between the application and System menus. If the selected item is in a pop-up menu, the next column in the pop-up menu is selected or the next top-level menu item is selected.
LEFT ARROW Works like the RIGHT ARROW key, except in the opposite direction. In pop-up menus, this keystroke backs up one column; when the currently selected item is in the far-left column, the previous pop-up menu is selected.
UP or DOWN ARROWS Activate a pop-up menu when pressed in a top-level menu. When pressed in a pop-up menu, either keystroke selects the next or previous item.
ENTER Activates a pop-up menu and highlights the first item if an item has a pop-up menu associated with it; otherwise, this keystroke chooses the item as if the user released the mouse button while the item was selected.
Alphabetic character Selects the first menu item with the specified character as its mnemonic. If the selected item onvokes a pop-up menu, the menu is displayed and the first item is highlighted; otherwise, the item is chosen.

+$#A Menu Mnemonics
{ewl msdncd, EWGraphic, group10806 16 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Mnemonics_win32_Menu_Mnemonics.
The standard keyboard interface for menus can be enhanced by adding mnemonics to menu items. A mnemonic is a underlined letter in the text of a menu item. When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter. The menu bar is active when the user presses and releases the ALT key and the first item on the menu bar is highlighted. A pop-up menu is active when it is displayed.
A mnemonic keystroke for a menu item is created by preceding a character in the item's text string with an ampersand. For example, "&Move" causes Windows to underline the "M" character.
+$#A Menu Keyboard Accelerators
{ewl msdncd, EWGraphic, group10806 17 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Keyboard Accelerators_win32_Menu_Keyboard_Accelerators.
In addition to having a mnemonic, a menu item can have a keyboard accelerator associated with it. A keyboard accelerator is different from a mnemonic in that the menu need not be active for the accelerator key to work. Also, a mnemonic is always associated with a menu item, while a keyboard accelerator is usually associated with a menu item, but it does not have to be.
Text to identify the keyboard accelerator is added to the menu-item text string. The accelerator text appear to the right of the menu-item name after a backslash and tab character. For example, "&Close\tAlt+F4" represents a Close item with the ALT+F4 key combination as its keyboard accelerator and with C as its mnemonic. For more information on accelerators, see Keyboard Accelerators_win32_Keyboard_Accelerators.
+$#A Menu Creation
{ewl msdncd, EWGraphic, group10806 18 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Creation_win32_Menu_Creation.
You can use the following methods to create a menu for use in an application:
· Load a menu from a menu-template resource.
· Build a menu from a menu template created at run time.
· Use the menu creation functions to build a menu at run time.

The following sections describe how to use these three methods to create menus.
+$#A Menu-Template Resources
{ewl msdncd, EWGraphic, group10806 19 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Menu Template Resources_win32_Menu_Template_Resources.
Most developers implement menus by creating a menu-template resource that is loaded at run time. A menu template defines a menu, including the items in the menu bar and all pop-up menus. For information about creating menu template resources, see your tools documentation.
Once you have created a menu-template resource and added it to your application's executable file, you can use the LoadMenu_win32_LoadMenu function to load the menu-template resource into memory. This function returns the handle of the menu, which you can then assign to a window by using the SetMenu_win32_SetMenu function.
Implementing menus as resources makes an application easier to localize for use in multiple countries. Only the resource definition needs to be translated for each language, not the application's source code.
+$#A Menu Template in Memory
{ewl msdncd, EWGraphic, group10806 20 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Template in Memory_win32_Menu_Template_in_Memory.
Instead of loading a menu-template resource, an application can create create a menu template in memory at run time. You create a menu from a template in memory by using the LoadMenuIndirect_win32_LoadMenuIndirect function. For descriptions of menu-template formats, see Using a Menu-Template Resource_win32_Using_a_Menu_Template_Resource_nt.
+$#A Menu-Creation Functions
{ewl msdncd, EWGraphic, group10806 21 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Menu Creation Functions_win32_Menu_Creation_Functions.
Another way to create a menu at run time is to use the CreateMenu_win32_CreateMenu and CreatePopupMenu_win32_CreatePopupMenu functions to create empty menus. CreateMenu creates an empty menu bar; CreatePopupMenu creates an empty pop-up menu. Then menu items are added to the menus by using the AppendMenu_win32_AppendMenu or InsertMenu_win32_InsertMenu functions.
+$#A Menu Display
{ewl msdncd, EWGraphic, group10806 22 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Display_win32_Menu_Display.
After a menu has been loaded or created, it must be assigned to a window before Windows can display it. This can be done by creating a class menu (see Class Menus_win32_Class_Menus), by specifying the handle of the menu as the hmenu parameter of the CreateWindowEx_win32_CreateWindowEx function, or by calling the SetMenu_win32_SetMenu function.
The TrackPopupMenu_win32_TrackPopupMenu function can be used to display a pop-up menu. To do so, the handle of a pop-up menu is passed to the function. The menu's owner window, the screen position where Windows should display the menu, and the mouse button the user must use to choose an item must also be specified.
+$#A Menu Destruction
{ewl msdncd, EWGraphic, group10806 23 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Destruction_win32_Menu_Destruction.
If a menu is assigned to a window and that window is destroyed, Windows automatically destroys the menu, freeing the menu's handle and the memory occupied by the menu. Windows does not automatically destroy a menu that is not assigned to a window. An application must destroy the unassigned menu by calling the DestroyMenu_win32_DestroyMenu function; otherwise, the menu continues to exist in memory even after the application closes.
+$#A Menu Messages Overview
{ewl msdncd, EWGraphic, group10806 24 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Messages_win32_Menu_Messages.
Windows reports menu-related activity by sending messages to the window procedure of the window that owns the menu. The first menu-related message the window procedure receives is WM_SYSCOMMAND_win32_WM_SYSCOMMAND. Windows sends this message when the user activates the menu by clicking the menu bar or by pressing a key that activates the menu (for more information, see Keyboard Access to Menus_win32_Keyboard_Access_to_Menus). The WM_SYSCOMMAND message includes a flag, either SC_KEYMENU or SC_MOUSEMENU, that indicates the hardware device used to activate the menu.
Next, Windows sends the WM_INITMENU_win32_WM_INITMENU message to the window procedure before Windows displays any pop-up menus, so an application can modify the menus before the user sees them. Windows sends the WM_INITMENU message only once per menu activation.
When the user selects a pop-up item, Windows sends the owner window the WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP message before displaying the pop-up menu. This message gives the application a second opportunity to modify the pop-up menu before it is displayed.
Each time the user moves the highlighting from one item to another, Windows sends a WM_MENUSELECT_win32_WM_MENUSELECT message to the window procedure of the menu's owner window. This message identifies the currently selected menu item. Many applications provide an information area at the bottom of their main windows and use this message to display additional information about the selected menu item.
Windows sends a WM_COMMAND_win32_WM_COMMAND message to the window procedure when the user chooses a command item from a menu. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
+$#A Menu Modifications
{ewl msdncd, EWGraphic, group10806 25 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Menu Modifications_win32_Menu_Modifications.
A number of functions can be used to modify a menu after it has been loaded or created. The AppendMenu_win32_AppendMenu and InsertMenu_win32_InsertMenu functions are used to add items to a menu. AppendMenu appends a menu item to the end of a menu; InsertMenu inserts a menu item at a specified position in a menu. Both functions permit the attributes of the menu item to be specified, including whether the menu item is enabled, checked, grayed, and so on.
The ModifyMenu_win32_ModifyMenu function is used to change the appearance or attributes of an existing menu item. For example, the text string or bitmap of a menu item can be enabled, disabled, grayed, checked, or unchecked. ModifyMenu replaces the specified menu item with a new item.
The DeleteMenu_win32_DeleteMenu or RemoveMenu_win32_RemoveMenu functions are used to delete a menu item from a menu. If the item being deleted is a pop-up item, DeleteMenu deletes the associated pop-up menu, discarding the menu handle and freeing the memory used by the pop-up menu. The RemoveMenu function deletes a menu item, but if the item activates a pop-up menu, it does not destroy the pop-up menu or its handle, allowing the menu to be reused.
After a menu bar has been modified, the DrawMenuBar_win32_DrawMenuBar function must be called to redraw the menu bar. Otherwise, the modifications will not appear until Windows redraws the owner window.
+$#A Using Menus
· Using a menu template resource_win32_Using_a_Menu_Template_Resource.
· Creating a floating pop-up menu_win32_Creating_a_Floating_Pop_up_Menu.
· Using menu item bitmaps_win32_Using_Menu_Item_Bitmaps.
· Creating owner-drawn menu items_win32_Creating_Owner_Drawn_Menu_Items.
· Using custom check mark bitmaps_win32_Using_Custom_Check_Mark_Bitmaps.

+$#A Using a Menu-Template Resource
{ewl msdncd, EWGraphic, group10806 26 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Using a Menu Template Resource_win32_Using_a_Menu_Template_Resource.
You typically include a menu in an application by defining a menu template in the application's resource-definition file and then loading the menu at run time. Alternatively, you can create a menu template dynamically in memory. This section describes the format of a menu template, and explains how to load a menu-template resource. For information about defining a menu-template resource, see your tools documentation.
+$#A Extended Menu Template Format
The extended menu template format supports the new menu functionality available in the Win32 API. Like old menu template resources, extended menu template resources have the RC_MENU resource type. Windows distinguishes the two resource formats by the version number, which is the first member of the resource header.

Note The structure definitions in this section are for explanation only. They are not defined in any standard header file.

An extended menu template consists of a header followed by one more more item definitions. The header has the following form:
typedef struct {
WORD wVersion; // must be 1 for extended menu resources
WORD wOffset; // offset of first item; typically 4
DWORD dwHelpId; // Help identifier for menu
} MENUEX_HDR;

Following is a description of each member:
wVersion
Specifies the template version number. This value must be 1 for extended menu templates.
wOffset
Specifies the offset of the first menu-item definition, relative to the end of this member. If the first menu-item definition immediately follows the dwHelpId member, this value should be 4.
dwHelpId
Specifies the Help identifier for the menu.

Following the header are one or more item definitions. Item definitions vary in size depending on the length of the menu text, and are packed on byte boundaries. Each item definition has the following form:
typedef struct {
DWORD dwType; // menu-item type (MFT_* flags)
DWORD dwState; // menu-item state (MFS_* flags)
WORD wId; // application-defined menu-item identifier
BYTE bResInfo; // resource info (MFR_* flags)
char szText[1]; // null-terminated menu text

// dwHelpId - In pop-up menu items, a 32-bit Help identifier
// follows the variable-length szText member.

} MENUEX_ITEM;

Following is a description of each member:
dwType
Specifies the type of menu item. Can be a combination of the MFT_* flags defined in WINDOWS.H. For a list of menu-item types, see the description of the MENUITEMINFO structure.
dwState
Specifies the menu-item state. Can be a combination of the MFS_* flags defined in WINDOWS.H. For a list of menu-item states, see the description of the MENUITEMINFO structure.
wId
Specifies the menu-item identifier. This is an application-defined 16-bit value used to identify the menu item. Pop-up menu items can now have identifiers; previously, only command menu-items could have identifiers.
bResInfo
Specifies the type of menu-item definition. Can be zero or more of these values:
MFR_POPUP The item definition is for a pop-up menu item.
MFR_END The item is the last menu item in the menu or submenu.

szText
Specifies the menu-item text. This member is a null-terminated ANSI string. The size of the menu-item definition varies depending on the length of the string.
dwHelpId
Specifies the Help identifier for a submenu. This member follows the menu-item text for pop-up menu items. It is omitted for other menu items.

The following assembly-language example defines an extended menu template:
My_Menu_Template LABEL DWORD
dw 0001h ; template version number
dw 0004h ; Windows version number
dd 0 ; Help id for main menu

; The following item definition specifies a pop-up menu
; item for a File submenu.

dd MFT_STRING ; type
dd 0 ; state
dw 1 ; identifier
db MFR_POPUP ; specifies a pop-up item
db "&File", 0 ; text, including null character
dd 0 ; Help id for the File submenu

; The following definition specifies an Exit command item,
; which appears in the File submenu. Because it is the last
; item in the submenu, the MFR_END flag is specified.

dd MFT_STRING ; type
dd MFS_DEFAULT ; state
dw 2 ; identifier
db MFR_END ; last menu-item in submenu
db "E&xit", 0 ; text, including null character

; The following definition specifies a Help command item.
; Because it is the last item in the menu, the MFR_END flag
; is specified.

dd MFT_STRING ; type
dd 0 ; state
dw 3 ; id
db MFR_END ; last menu-item in main menu
db "&Help", 0 ; text, including null character

+$#A Old Menu Template Format
An old menu template defines a menu, but does not support the new menu functionality available in the Win32 API. An old menu template resource has the RT_MENU resource type. An old menu template consists of a MENUITEMTEMPLATEHEADER_win32_MENUITEMTEMPLATEHEADER_str structure followed by one or more MENUITEMTEMPLATE_win32_MENUITEMTEMPLATE_str structures.
+$#A Loading a Menu-Template Resource
{ewl msdncd, EWGraphic, group10806 27 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Loading a Menu Template Resource_win32_Loading_a_Menu_Template_Resource.
You can load a menu-template resource by calling the LoadMenu_win32_LoadMenu function, specifying the handle of the module that contains the resource and the menu-template's identifier. LoadMenu returns a menu handle that you can use to assign the menu to a window. This window becomes the menu's owner window, receiving all the messages generated by the menu.
You can create a menu from a menu template that is already in memory by using the LoadMenuIndirect_win32_LoadMenuIndirect function. This is useful if your application generates menu templates dynamically.
Use the SetMenu_win32_SetMenu function to assign a menu to a window, or specify the menu's handle in the hmenu parameter of the CreateWindowEx_win32_CreateWindowEx function when you create a window. Another way that you can assign a menu to a window is to specify a menu template when you register a window class. This identifies the menu as the class menu for the given window class. Whenever you create a window of the given class, Windows automatically assigns the given menu to the window.
You can create a class menu by including the identifier of the menu-template resource as the lpszMenuName member of a WNDCLASS_win32_WNDCLASS_str structure and then passing the address of the structure to the RegisterClass_win32_RegisterClass function.
+$#A Creating a Class Menu
{ewl msdncd, EWGraphic, group10806 28 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Creating a Class Menu_win32_Creating_a_Class_Menu.
The following example shows how to create a class menu for an application, how to create a window that uses the class menu, and how to process menu commands in the window procedure.
HINSTANCE hinst;

int APIENTRY WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hinstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
MSG msg; /* message */
WNDCLASS wc; /* window-class data */
HWND hwnd; /* handle of main window */

/*
* Create the window class for the main window. Specify
* the identifier of the menu-template resource as the
* lpszMenuName member of the WNDCLASS structure to create
* the class menu.
*/

wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MyMenuResource";
wc.lpszClassName = "MainWClass";

if (!RegisterClass(&wc))
return FALSE;

hinst = hinstance;

/*
* Create the main window. Set the hmenu parameter to NULL so
* that Windows uses the class menu for the window.
*/

hwnd = CreateWindow("MainWClass", "Sample Application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,
NULL);

if (hwnd == NULL)
return FALSE;

/*
* Make the window visible and send a WM_PAINT message to the
* window procedure.
*/

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

/* Start the main message loop. */

while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(hPrevInstance);
}

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

switch (uMsg) {
.
. /* Process other window messages. */
.

case WM_COMMAND:

/* Test for the identifier of a command item. */

switch(LOWORD(wParam)) {
case IDM_FI_OPEN:
DoFileOpen(); /* application-defined */
break;

case IDM_FI_CLOSE:
DoFileClose(); /* application-defined */
break;
.
. /* Process other menu commands. */
.

default:
break;

}
return 0;

.
. /* Process other window messages. */
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

+$#A Creating a Floating Pop-up Menu
{ewl msdncd, EWGraphic, group10806 29 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Creating a Floating Pop-up Menu_win32_Creating_a_Floating_Pop_up_Menu.
To use a floating pop-up menu in an application, pass the handle of a pop-up menu to the TrackPopupMenu_win32_TrackPopupMenu function. An application typically calls TrackPopupMenu in a window procedure in response to a user-generated message, such as WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN or WM_KEYDOWN_win32_WM_KEYDOWN.
In addition to the pop-up menu handle, TrackPopupMenu requires that you specify the handle of the owner window, the position of the floating pop-up menu (in screen coordinates), and the mouse button the user can use to choose an item.
Specify the position by providing x- and y-coordinates along with the TPM_CENTERALIGN, TPM_LEFTALIGN, or TPM_RIGHTALIGN flag. The flag specifies the position of the menu relative to the x- and y-coordinates.
You should permit the user to choose an item from a pop-up menu by using the same mouse button used to display the menu. To do this, specify either TPM_LEFTBUTTON or TPM_RIGHTBUTTON flags to indicate which mouse button the user can use to select a menu item.
+$#A Creating a Floating Font-Attributes Menu
The example in this section contains portions of code from an application that creates and displays a floating pop-up menu that enables the user to set fonts and font attributes. The application displays the menu in the client area of its main window whenever the user clicks the left mouse button.
Here is the menu template for the pop-up menu that is provided in the application's resource-definition file.
PopupMenu MENU
BEGIN
POPUP "Dummy Popup"
BEGIN
POPUP "Fonts"
BEGIN
MENUITEM "Courier", IDM_FONT_COURIER
MENUITEM "Times Roman", IDM_FONT_TMSRMN
MENUITEM "Swiss", IDM_FONT_SWISS
MENUITEM "Helvetica", IDM_FONT_HELV
MENUITEM "Old English", IDM_FONT_OLDENG
END
POPUP "Sizes"
BEGIN
MENUITEM "7", IDM_SIZE_7
MENUITEM "8", IDM_SIZE_8
MENUITEM "9", IDM_SIZE_9
MENUITEM "10", IDM_SIZE_10
MENUITEM "11", IDM_SIZE_11
MENUITEM "12", IDM_SIZE_12
MENUITEM "14", IDM_SIZE_14
END
POPUP "Styles"
BEGIN
MENUITEM "Bold", IDM_STYLE_BOLD
MENUITEM "Italic", IDM_STYLE_ITALIC
MENUITEM "Strike Out", IDM_STYLE_SO
MENUITEM "Superscript", IDM_STYLE_SUPER
MENUITEM "Subscript", IDM_STYLE_SUB
END
END

END

The following example gives the window procedure and supporting functions used to create and display the floating pop-up menu.
LRESULT APIENTRY MenuWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
RECT rc; /* client area */
POINT pt; /* location of mouse click */

switch (uMsg) {
case WM_LBUTTONDOWN:

/* Get the bounding rectangle of the client area. */

GetClientRect(hwnd, (LPRECT) &rc);

/* Get the client coordinates for the mouse click. */

pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);

/*
* If the mouse click took place inside the client
* area, execute the application-defined function
* that displays the floating pop-up menu.
*/

if (PtInRect((LPRECT) &rc, pt))
HandlePopupMenu(hwnd, pt);
break;
.
. /* Process other window messages. */
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

VOID APIENTRY HandlePopupMenu(hwnd, pt)
HWND hwnd;
POINT pt;
{
HMENU hmenu; /* menu template */
HMENU hmenuTrackPopup; /* floating pop-up menu */

/*
* Load the menu template containing the pop-up menu from the
* application's resources.
*/

hmenu = LoadMenu(hinst, "PopupMenu");
if (hmenu == NULL)
return;

/*
* Get the first pop-up menu in the menu template. This is the
* menu that TrackPopupMenu displays.
*/

hmenuTrackPopup = GetSubMenu(hmenu, 0);

/*
* TrackPopup uses screen coordinates, so convert the
* coordinates of the mouse click to screen coordinates.
*/

ClientToScreen(hwnd, (LPPOINT) &pt);

/* Draw and track the floating pop-up menu. */

TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);

/* Destroy the menu.*/

DestroyMenu(hmenu);
}

+$#A Using Menu-Item Bitmaps
{ewl msdncd, EWGraphic, group10806 30 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview, Using Menu Item Bitmaps_win32_Using_Menu_Item_Bitmaps.
Windows can use a bitmap instead of a text string to display a menu item. To use a bitmap, you must set the MF_BITMAP flag for the menu item and specify the handle of the bitmap that Windows should display for the menu item. This section describes how to set the MF_BITMAP flag and retrieve the handle of a bitmap.
+$#A Setting the MF_BITMAP Flag
The MF_BITMAP flag tells Windows to use a bitmap rather than a text string to display a menu item. A menu item's MF_BITMAP flag must be set at run time; you cannot set it in the resource-definition file.
You can use the ModifyMenu_win32_ModifyMenu, InsertMenu_win32_InsertMenu, or AppendMenu_win32_AppendMenu function to set the MF_BITMAP flag. To convert a menu item from a text string item to a bitmap item, use ModifyMenu. To add a new bitmap item to a menu, use the MF_BITMAP flag with the InsertMenu or AppendMenu function.
+$#A Creating the Bitmap
{ewl msdncd, EWGraphic, group10806 31 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Creating the Bitmap_win32_Creating_the_Bitmap.
When you set the MF_BITMAP flag for a menu item, you must also specify the handle of the bitmap that Windows should display for the menu item. You can provide the bitmap as a bitmap resource or create the bitmap at run time.
To provide a bitmap resource, use a resource editor, such as ImageEdit, to create a bitmap resource, and then use a resource compiler to add the bitmap to your application's executable file. At run time, use the LoadBitmap_win32_LoadBitmap function to load the bitmap and obtain its handle.
To create the bitmap at run time, use GDI functions. GDI provides several ways to create a bitmap at run time, but developers typically use the following method:
· Use the CreateCompatibleDC_win32_CreateCompatibleDC function to create a device context compatible with the device context used by the application's main window.
· Use the CreateCompatibleBitmap_win32_CreateCompatibleBitmap function to create a bitmap compatible with the application's main window.
· Use the SelectObject_win32_SelectObject function to select the bitmap into the compatible device context.
· Use GDI drawing functions, such as Ellipse_win32_Ellipse and LineTo_win32_LineTo, to draw an image into the bitmap.
· Pass the handle of the bitmap to the ModifyMenu_win32_ModifyMenu, InsertMenu_win32_InsertMenu, or AppendMenu_win32_AppendMenu function.

For more information about bitmaps, see Bitmaps_win32_Bitmaps.
+$#A Adding Lines and Graphs to a Menu
The following code sample shows how to create a menu that contains menu-item bitmaps. It creates two pop-up menus shown in the following illustration. The first is a Chart menu that contains three menu-item bitmaps: a pie chart, a line chart, and a bar chart. The example demonstrates how to load these bitmaps from the application's resource file, and then use the CreatePopupMenu_win32_CreatePopupMenu and AppendMenu_win32_AppendMenu functions to create the menu and menu items.
The second pop-up menu is a Lines menu. It contains bitmaps showing the line styles provided by the predefined pen in Windows. The line-style bitmaps are created at run time by using GDI functions.
Here are the definitions of the bitmap resources in the application's resource-definition file.
PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp

Here are the relevant portions of the application's header file.
/* Menu-item identifiers */

#define IDM_SOLID PS_SOLID
#define IDM_DASH PS_DASH
#define IDM_DASHDOT PS_DASHDOT
#define IDM_DASHDOTDOT PS_DASHDOTDOT

#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3

/* Line-type flags */

#define SOLID 0
#define DOT 1
#define DASH 2
#define DASHDOT 3
#define DASHDOTDOT 4

/* Count of pens */

#define CPENS 5

/* Chart-type flags */

#define PIE 1
#define LINE 2
#define BAR 3

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
VOID MakeChartMenu(HWND);
VOID MakeLineMenu(HWND, HPEN, HBITMAP);

The following example shows how menus and menu-item bitmaps are created in an application.
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

static HPEN hpen[CPENS];
static HBITMAP hbmp[CPENS];
int i;

switch (uMsg) {
case WM_CREATE:

/* Create the Chart and Line menus. */

MakeChartMenu(hwnd);
MakeLineMenu(hwnd, hpen, hbmp);
return 0;

.
. /* Process other window messages. */
.

case WM_DESTROY:

for (i = 0; i < CPENS; i++) {
DeleteObject(hbmp[i]);
DeleteObject(hpen[i]);
}

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

VOID MakeChartMenu(hwnd)
HWND hwnd; /* handle of owner window */
{
HBITMAP hbmpPie; /* handle of pie chart bitmap */
HBITMAP hbmpLine; /* handle of line chart bitmap */
HBITMAP hbmpBar; /* handle of bar chart bitmap */
HMENU hmenuMain; /* handle of main menu */
HMENU hmenuChart; /* handle of chart pop-up menu */

/*
* Load the pie, line, and bar chart bitmaps from the
* resource-definition file.
*/

hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE));
hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE));
hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR));

/*
* Create the Chart pop-up menu and add it to the menu bar.
* Append the Pie, Line, and Bar menu items to the Chart
* pop-up menu.
*/

hmenuMain = GetMenu(hwnd);
hmenuChart = CreatePopupMenu();
AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart,
"Chart");
AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie);
AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE,
(LPCTSTR) hbmpLine);
AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar);

return;
}

VOID MakeLineMenu(hwnd, phpen, phbmp)
HWND hwnd;
HPEN *phpen;
HBITMAP *phbmp;
{
HMENU hmenuLines; /* handle of Lines pop-up menu */
HMENU hmenu; /* handle of main menu */
COLORREF crMenuClr; /* menu-item background color */
HBRUSH hbrBackground; /* handle of background brush */
HBRUSH hbrOld; /* handle of previous brush */
LONG lCheckXY; /* dimensions of check mark bitmap */
WORD wLineX; /* width of line bitmaps */
WORD wLineY; /* height of line bitmaps */
HDC hdcMain; /* handle of main window's DC */
HDC hdcLines; /* handle of compatible DC */
HBITMAP hbmpOld; /* handle of previous bitmap */
int i; /* loop counter */

/* Create the Lines pop-up menu. Add it to the menu bar. */

hmenu = GetMenu(hwnd);
hmenuLines = CreatePopupMenu();
AppendMenu(hmenu, MF_STRING | MF_POPUP,
(UINT) hmenuLines, "&Lines");

/* Create a brush for the menu-item background color. */

crMenuClr = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crMenuClr);

/*
* Create a compatible device context for the line bitmaps,
* and then select the background brush into it.
*/

hdcMain = GetDC(hwnd);
hdcLines = CreateCompatibleDC(hdcMain);
hbrOld = SelectObject(hdcLines, hbrBackground);

/*
* Get the dimensions of the check mark bitmap. The width of
* the line bitmaps will be five times the width of the
* check mark bitmap.
*/

lCheckXY = GetMenuCheckMarkDimensions();
wLineX = LOWORD(lCheckXY) * (WORD) 5;
wLineY = HIWORD(lCheckXY);

/*
* Create the bitmaps and select them, one at a time, into the
* compatible device context. Initialize each bitmap by
* filling it with the menu-item background color.
*/

for (i = 0; i < CPENS; i++) {
phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY);
if (i == 0)
hbmpOld = SelectObject(hdcLines, phbmp[i]);
else
SelectObject(hdcLines, phbmp[i]);
ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER);
}

/* Create the pens. */

phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));

/*
* Select a pen and a bitmap into the compatible device
* context, draw a line into the bitmap, and then append
* the bitmap as an item in the Lines menu.
*/

for (i = 0; i < CPENS; i++) {
SelectObject(hdcLines, phbmp[i]);
SelectObject(hdcLines, phpen[i]);
MoveToEx(hdcLines, 0, wLineY / 2, NULL);
LineTo(hdcLines, wLineX, wLineY / 2);
AppendMenu(hmenuLines, MF_BITMAP, i + 1,
(LPCTSTR) phbmp[i]);
}

/*
* Release the main window's device context and destroy the
* compatible device context. Also, destroy the background
* brush.
*/

ReleaseDC(hwnd, hdcMain);
SelectObject(hdcLines, hbrOld);
DeleteObject(hbrBackground);
SelectObject(hdcLines, hbmpOld);
DeleteDC(hdcLines);

return;
}

+$#A Creating Owner-Drawn Menu Items
If you need complete control over the appearance of a menu item, you can use an owner-drawn menu item on your application. This section describes the steps involved in creating and using an owner-drawn menu item.
+$#A Setting the MF_OWNERDRAW Flag
You cannot define an owner-drawn menu item on your application's resource-definition file. Instead, you must create a new menu item or modify an existing one by using the MF_OWNERDRAW menu flag.
You can use the AppendMenu_win32_AppendMenu, InsertMenu_win32_InsertMenu, or ModifyMenu_win32_ModifyMenu function to specify an owner-drawn menu item. Use AppendMenu to add a new item to the end of the given menu. Use InsertMenu to place a new item onto a menu, moving the other items down. Use ModifyMenu to change the contents of a menu.
When you call any of these three functions, you can pass a 32-bit value as the lpNewItem parameter. This value can represent any information that is meaningful to your application, and that will be available to your application when the item is to be displayed. For example, the value could contain a pointer to a structure; the structure, in turn, might contain a text string and the handle of a logical font that your application will use to draw the string.
+$#A Responding to the WM_MEASUREITEM Message
Before Windows displays an owner-drawn menu item for the first time, it sends the WM_MEASUREITEM_win32_WM_MEASUREITEM message to the window procedure of the window that owns the item's menu. This message contains a pointer to a MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure that identifies the item and contains an optional 32-bit value for the item. When the window procedure receives the WM_MEASUREITEM message, it must fill the itemWidth and itemHeight members of the structure before returning from processing the message. Windows uses the information in these members when creating the bounding rectangle in which your application draws the menu item. It also uses the information to detect when the user chooses the item.
+$#A Responding to the WM_DRAWITEM Message
Whenever the item must be drawn (for example, when it is first displayed or when the user chooses it as a command), Windows sends the WM_DRAWITEM_win32_WM_DRAWITEM message to the window procedure of the menu's owner window. This message contains a pointer to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure. Like the MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure, DRAWITEMSTRUCT contains information about the item and its optional 32-bit data. In addition, DRAWITEMSTRUCT contains flags that indicate the state of the item (such as whether it is grayed or checked) as well as a bounding rectangle and a device context that the application uses to draw the item.
In response to the WM_DRAWITEM message, your application must perform the following actions before returning from processing the message:
1. Determine the type of drawing that is necessary. To do so, check the itemAction member of the DRAWITEMSTRUCT structure.
2. Draw the menu item appropriately, using the bounding rectangle and device context obtained from the DRAWITEMSTRUCT structure. Your application must draw only within the bounding rectangle. For performance reasons, Windows doesn't clip portions of the image that are drawn outside the rectangle.
3. Restore all GDI objects selected for the menu item's device context.

If the menu item is selected, Windows sets the itemAction member of the DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure to ODA_SELECT and sets the ODS_SELECTED flag in the itemState member. This is an application's cue to redraw the menu item to indicate that it is selected.
+$#A Setting Fonts for Menu-Item Text Strings
This section contains an example from an application that uses owner-drawn menu items in a pop-up menu. The pop-up menu contains items that set the attributes of the current font, and the items are displayed using the appropriate font attribute.
Here is how the menu is defined in the resource-definition file. Note that the strings for the Regular, Bold, Italic, and Underline items are assigned at run time, so their strings are empty in the resource-definition file.
MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "", IDM_REGULAR
MENUITEM SEPARATOR
MENUITEM "", IDM_BOLD
MENUITEM "", IDM_ITALIC
MENUITEM "", IDM_ULINE
END
END

The application's window procedure processes the messages involved in using owner-drawn menu items. The application uses the WM_CREATE_win32_WM_CREATE message to do the following:
· Set the MF_OWNERDRAW flag for the menu items.
· Set the text strings for the menu items.
· Obtain handles of the fonts used to draw the items.
· Obtain the text and background color values for selected menu items.

The text strings and font handles are stored in an array of application-defined MYITEM structures. The application-defined GetAFont function creates a font that corresponds to the given font attribute and returns the handle of the font. The handles are destroyed during the processing of the WM_DESTROY_win32_WM_DESTROY message.
During the processing of the WM_MEASUREITEM_win32_WM_MEASUREITEM message, the example gets the width and height of a menu-item string and copies these values into the MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure. Windows uses the width and height values to calculate the size of the pop-up menu.
During the processing of the WM_DRAWITEM_win32_WM_DRAWITEM message, the menu item's string is drawn with room left next to the string for the check mark bitmap. If the user selects the item, the selected text and background colors are used to draw the item.
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

typedef struct _MYITEM {
HFONT hfont;
LPSTR psz;
} MYITEM; /* structure for item font and string */

MYITEM *pmyitem; /* pointer to item's font and string */
static MYITEM myitem[CITEMS]; /* array of MYITEMS */
static HMENU hmenu; /* handle of main menu */
static COLORREF crSelText; /* text color of selected item */
static COLORREF crSelBkgnd; /* background color of selected item */
COLORREF crText; /* text color of unselected item */
COLORREF crBkgnd; /* background color unselected item */
LPMEASUREITEMSTRUCT lpmis; /* points to item of data */
LPDRAWITEMSTRUCT lpdis; /* points to item drawing data */
HDC hdc; /* handle of screen DC */
SIZE size; /* menu-item text extents */
DWORD dwCheckXY; /* check mark dimensions */
WORD wCheckX; /* check mark width */
int nTextX; /* width of menu item */
int nTextY; /* height of menu item */
int i; /* loop counter */
HFONT hfontOld; /* handle of old font */
BOOL fSelected = FALSE; /* menu-item selection flag */

switch (uMsg) {
case WM_CREATE:

/*
* Modify the Regular, Bold, Italic, and Underline
* menu items to make them owner-drawn items. Associate
* a MYITEM structure with each item to contain the
* string and font handle for each item.
*/

hmenu = GetMenu(hwnd);
ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR,
(LPTSTR) &myitem[REGULAR]);
ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]);
ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ITALIC,
(LPTSTR) &myitem[ITALIC]);
ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ULINE, (LPTSTR) &myitem[ULINE]);

/*
* Retrieve each item's font handle and copy it into
* the hfont member of each item's MYITEM structure.
* Also, copy each item's string into the structures.
*/

myitem[REGULAR].hfont = GetAFont(REGULAR);
myitem[REGULAR].psz = "Regular";
myitem[BOLD].hfont = GetAFont(BOLD);
myitem[BOLD].psz = "Bold";
myitem[ITALIC].hfont = GetAFont(ITALIC);
myitem[ITALIC].psz = "Italic";
myitem[ULINE].hfont = GetAFont(ULINE);
myitem[ULINE].psz = "Underline";

/*
* Retrieve the text and background colors of the
* selected menu text.
*/

crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);

return 0;

case WM_MEASUREITEM:

/* Retrieve a device context for the main window. */

hdc = GetDC(hwnd);

/*
* Retrieve pointers to the menu item's
* MEASUREITEMSTRUCT structure and MYITEM structure.
*/

lpmis = (LPMEASUREITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpmis->itemData;

/*
* Select the font associated with the item into
* the main window's device context.
*/

hfontOld = SelectObject(hdc, pmyitem->hfont);

/*
* Retrieve the width and height of the item's string,
* and then copy the width and height into the
* MEASUREITEMSTRUCT structure's itemWidth and
* itemHeight members.
*/

GetTextExtentPoint32(hdc, pmyitem->psz,
lstrlen(pmyitem->psz), &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;

/*
* Select the old font back into the device context,
* and then release the device context.
*/

SelectObject(hdc, hfontOld);
ReleaseDC(hwnd, hdc);

return TRUE;

break;

case WM_DRAWITEM:

/*
* Get pointers to the menu item's DRAWITEMSTRUCT
* structure and MYITEM structure.
*/

lpdis = (LPDRAWITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpdis->itemData;

/*
* If the user has selected the item, use the selected
* text and background colors to display the item.
*/

if (lpdis->itemState & ODS_SELECTED) {
crText = SetTextColor(lpdis->hDC, crSelText);
crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);
fSelected = TRUE;
}

/*
* Remember to leave space in the menu item for the
* check mark bitmap. Retrieve the width of the bitmap
* and add it to the width of the menu item.
*/

dwCheckXY = GetMenuCheckMarkDimensions();
wCheckX = LOWORD(dwCheckXY);
nTextX = wCheckX + lpdis->rcItem.left;
nTextY = lpdis->rcItem.top;

/*
* Select the font associated with the item into the
* item's device context, and then draw the string.
*/

hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,
&lpdis->rcItem, pmyitem->psz,
lstrlen(pmyitem->psz), NULL);

/*
* Select the previous font back into the device
* context.
*/

SelectObject(lpdis->hDC, hfontOld);

/*
* Return the text and background colors to their
* normal state (not selected).
*/

if (fSelected) {
SetTextColor(lpdis->hDC, crText);
SetBkColor(lpdis->hDC, crBkgnd);
}

return TRUE;

.
. /* Process other messages. */
.

case WM_DESTROY:

/* Destroy the menu items' font handles. */

for (i = 0; i < CITEMS; i++)
DeleteObject(myitem[i].hfont);

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

HFONT GetAFont(fnFont)
int fnFont; /* font-attribute flag */
{
static LOGFONT lf; /* structure for font information */

/*
* Get a handle to the ANSI fixed-pitch font, and copy
* information about the font to a LOGFONT structure.
*/

GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT),
&lf);

/* Set the font attributes, as appropriate. */

if (fnFont == BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;

lf.lfItalic = (fnFont == ITALIC);
lf.lfItalic = (fnFont == ULINE);

/* Create the font, and then return its handle. */

return CreateFont(lf.lfHeight, lf.lfWidth,
lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
lf.lfPitchAndFamily, lf.lfFaceName);
}

+$#A Using Custom Check Mark Bitmaps
{ewl msdncd, EWGraphic, group10806 32 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Using Custom Check Mark Bitmaps_win32_Using_Custom_Check_Mark_Bitmaps.
A Windows video device driver provides a default check mark bitmap that Windows displays next to a menu item that is checked. You can customize an individual menu item by providing a pair of bitmaps to replace the default check mark bitmap. Windows displays one bitmap when the item is checked and the other when it is unchecked. This section describes the steps involved in creating and using custom check mark bitmaps.
+$#A Creating Custom Check Mark Bitmaps
{ewl msdncd, EWGraphic, group10806 33 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Creating Custom Check Mark Bitmaps_win32_Creating_Custom_Check_Mark_Bitmaps.
A custom check mark bitmap must be the same size as the default check mark bitmap. You can retrieve the default check mark bitmap's size by calling the GetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensions function. The low-order word of this function's return value specifies the width; the high-order word specifies the height.
Bitmap resources can be used to provide check mark bitmaps. However, a bitmap resource is not suitable for all screen resolutions and aspect ratios, so you'll need to use the StretchBlt_win32_StretchBlt function to size the bitmap, as appropriate. Depending on the bitmap being sized, the result of the sizing may not be acceptable, because stretching may distort the bitmap.
Instead of using a bitmap resource, GDI functions can be used to provide check mark bitmaps at run time. This involves the following steps, which an application typically performs during the WM_CREATE_win32_WM_CREATE message:
1. Create a device context in memory that is compatible with the screen.
2. Create a bitmap that is compatible with the display.
3. Select the bitmap into the device context.
4. Draw or copy an image into the bitmap.

Call the CreateCompatibleDC_win32_CreateCompatibleDC function to create a device context that is compatible with the screen. The function's hdc parameter can specify either NULL or the return value from the GetDC_win32_GetDC function. CreateCompatibleDC returns the handle of the compatible device context.
Call the CreateCompatibleBitmap_win32_CreateCompatibleBitmap function to create an "empty" bitmap that is compatible with the screen. This function's nWidth and nHeight parameters set the size of the bitmap; they should specify the width and height information returned by the GetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensions function.
The CreateCompatibleBitmap function returns the handle of the bitmap. Pass this handle of the SelectObject_win32_SelectObject function to select the bitmap into the compatible device context. Afterward, you can use the GDI drawing functions such as Rectangle_win32_Rectangle and Ellipse_win32_Ellipse to draw into the bitmap, or you can use functions such as BitBlt_win32_BitBlt and StretchBlt_win32_StretchBlt to copy an image into the bitmap.
GDI provides several other ways to create bitmaps. For more information about bitmaps, see Bitmaps_win32_Bitmaps.
+$#A Associating Bitmaps with a Menu Item
{ewl msdncd, EWGraphic, group10806 34 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Associating Bitmaps with a Menu Item_win32_Associating_Bitmaps_with_a_Menu_Item.
You associate a pair of check mark bitmaps with a menu item by passing the handles of the bitmaps to the SetMenuItemBitmaps_win32_SetMenuItemBitmaps function. The hbmUnchecked parameter identifies the unchecked bitmap; the hbmChecked parameter identifies the checked bitmap. If you want to remove one or both check marks from a menu item, you can set the hbmUnchecked or hbmChecked parameter, or both, to NULL.
+$#A Setting the Check Mark Attribute
{ewl msdncd, EWGraphic, group10806 35 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Setting the Check Mark Attribute_win32_Setting_the_Check_Mark_Attribute.
The CheckMenuItem_win32_CheckMenuItem function sets a menu item's check mark attribute to either checked or unchecked. Specify the MF_CHECKED flag to set the check mark attribute to checked, or specify MF_UNCHECKED to set it to unchecked.
+$#A Simulating Check Boxes in a Menu
This section contains an example that shows how to simulate check boxes in a pop-up menu. The example contains a Character menu whose items allow the user to set the bold, italic, and underline attributes of the current font. When a font attribute is in effect, a check mark is displayed in the check box next to the corresponding menu item; otherwise, it is displayed in an empty check box next to the item.
The example in this section replaces the default check mark bitmap with two bitmaps: a bitmap with a checked box and the bitmap with an empty box. The checked check box bitmap is displayed next to the Bold, Italic, or Underline menu item when the item's check mark attribute is set to MF_CHECKED. The unchecked or empty check box bitmap is displayed when the check mark attribute is set to MF_UNCHECKED.
Windows provides a predefined bitmap that contains the images used for check boxes and radio buttons. The example in this section isolates the checked and empty check boxes, copies them to two separate bitmaps, and then uses them as the checked and unchecked bitmaps for items in the Character menu.
To retrieve the handle of the system-defined check box bitmap, the example in this section calls the LoadBitmap_win32_LoadBitmap function, specifying NULL as the hinst parameter and OBM_CHECKBOXES as the lpBitmapName parameter. Because the images in the bitmap are all the same size, the example can isolate them by dividing the bitmap's width and height by the number of images in its rows and columns.
The following portion of a resource-definition file shows how the menu items in the Character menu are defined. Note that no font attributes are in effect initially, so the check mark attribute for the Regular item is set to checked and, by default, the check mark attribute of the remaining items is set to unchecked.
#include "men3.h"

MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "&Regular", IDM_REGULAR, CHECKED
MENUITEM SEPARATOR
MENUITEM "&Bold", IDM_BOLD
MENUITEM "&Italic", IDM_ITALIC
MENUITEM "&Underline", IDM_ULINE
END
END

Here is the relevant contents of the application's header file.
/* Menu-item identifiers */

#define IDM_REGULAR 0x1
#define IDM_BOLD 0x2
#define IDM_ITALIC 0x4
#define IDM_ULINE 0x8

/* Check mark flags */

#define CHECK 1
#define UNCHECK 2

/* Font-attribute mask */

#define ATTRIBMASK 0xe

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP GetMyCheckBitmaps(UINT);
BYTE CheckOrUncheckMenuItem(BYTE, HMENU);

The following example shows the portions of the window procedure that create the check mark bitmaps, set the check mark attribute of the Bold, Italic, and Underline menu items, and destroy check mark bitmaps.
LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

static HBITMAP hbmpCheck; /* handle of checked bitmap */
static HBITMAP hbmpUncheck; /* handle of unchecked bitmap */
static HMENU hmenu; /* handle of main menu */
BYTE fbFontAttrib; /* font-attribute flags */

switch (uMsg) {
case WM_CREATE:

/*
* Call the application-defined GetMyCheckBitmaps
* function to get the predefined checked and
* unchecked check box bitmaps.
*/

hbmpCheck = GetMyCheckBitmaps(CHECK);
hbmpUncheck = GetMyCheckBitmaps(UNCHECK);

/*
* Set the checked and unchecked bitmaps for the menu
* items.
*/

hmenu = GetMenu(hwndMain);
SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);

return 0;

case WM_COMMAND:
switch (LOWORD(wParam)) {

/* Process the menu commands. */

case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:

/*
* CheckOrUncheckMenuItem is an application-
* defined function that sets the menu item
* check marks and returns the user-selected
* font attributes.
*/

fbFontAttrib = CheckOrUncheckMenuItem(
(BYTE) LOWORD(wParam), hmenu);

.
. /* Set the font attributes. */
.

return 0;

.
. /* Process other command messages. */
.

default:
break;
}

break;

.
. /* Process other window messages. */
.

case WM_DESTROY:

/* Destroy the checked and unchecked bitmaps. */

DeleteObject(hbmpCheck);
DeleteObject(hbmpUncheck);

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

HBITMAP GetMyCheckBitmaps(fuCheck)
UINT fuCheck; /* CHECK or UNCHECK flag */
{
COLORREF crBackground; /* background color */
HBRUSH hbrBackground; /* background brush */
HBRUSH hbrTargetOld; /* original background brush */
HDC hdcSource; /* source device context */
HDC hdcTarget; /* target device context */
HBITMAP hbmpCheckboxes; /* handle of check box bitmap */
BITMAP bmCheckbox; /* structure for bitmap data */
HBITMAP hbmpSourceOld; /* handle of original source bitmap */
HBITMAP hbmpTargetOld; /* handle of original target bitmap */
HBITMAP hbmpCheck; /* handle of check mark bitmap */
RECT rc; /* rectangle for check box bitmap */
DWORD dwCheckXY; /* dimensions of check mark bitmap */
WORD wBitmapX; /* width of check mark bitmap */
WORD wBitmapY; /* height of check mark bitmap */

/*
* Get the menu background color and create a solid brush
* with that color.
*/

crBackground = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crBackground);

/*
* Create memory device contexts for the source and
* destination bitmaps.
*/

hdcSource = CreateCompatibleDC((HDC) NULL);
hdcTarget = CreateCompatibleDC(hdcSource);

/*
* Get the size of the Windows default check mark bitmap and
* create a compatible bitmap of the same size.
*/

dwCheckXY = GetMenuCheckMarkDimensions();
wBitmapX = LOWORD(dwCheckXY);
wBitmapY = LOWORD(dwCheckXY);

hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX,
wBitmapY);

/*
* Select the background brush and bitmap into the target DC.
*/

hbrTargetOld = SelectObject(hdcTarget, hbrBackground);
hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck);

/*
* Use the selected brush to initialize the background color
* of the bitmap in the target device context.
*/

PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY);

/*
* Load the predefined check box bitmaps and select it
* into the source DC.
*/

hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL,
(LPTSTR) OBM_CHECKBOXES);

hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes);

/*
* Fill a BITMAP structure with information about the
* check box bitmaps, and then find the upper-left corner of
* the unchecked check box or the checked check box.
*/

GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox);

if (fuCheck == UNCHECK) {
rc.left = 0;
rc.right = (bmCheckbox.bmWidth / 4);
}
else {
rc.left = (bmCheckbox.bmWidth / 4);
rc.right = (bmCheckbox.bmWidth / 4) * 2;
}

rc.top = 0;
rc.bottom = (bmCheckbox.bmHeight / 3);

/*
* Copy the appropriate bitmap into the target DC. If the
* check box bitmap is larger than the default check mark
* bitmap, use StretchBlt to make it fit; otherwise, just
* copy it.
*/

if (((rc.right - rc.left) > (int) wBitmapX) ||
((rc.bottom - rc.top) > (int) wBitmapY))
StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY,
hdcSource, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SRCCOPY);

else
BitBlt(hdcTarget, 0, 0, rc.right - rc.left,
rc.bottom - rc.top,
hdcSource, rc.left, rc.top, SRCCOPY);

/*
* Select the old source and destination bitmaps into the
* source and destination DCs, and then delete the DCs and
* the background brush.
*/

SelectObject(hdcSource, hbmpSourceOld);
SelectObject(hdcTarget, hbrTargetOld);
hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld);

DeleteObject(hbrBackground);
DeleteObject(hdcSource);
DeleteObject(hdcTarget);

/* Return the handle of the new check mark bitmap. */

return hbmpCheck;
}

BYTE CheckOrUncheckMenuItem(bMenuItemID, hmenu)
BYTE bMenuItemID;
HMENU hmenu;
{
DWORD fdwMenu;
static BYTE fbAttributes;

switch (bMenuItemID) {
case IDM_REGULAR:

/*
* Whenever the Regular menu item is selected, add a
* check mark to it and then remove check marks from
* any font-attribute menu items.
*/

CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED);

if (fbAttributes & ATTRIBMASK) {
CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_UNCHECKED);
}
fbAttributes = IDM_REGULAR;
return fbAttributes;

case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:

/*
* Toggle the check mark for the selected menu item and
* set the font attribute flags appropriately.
*/

fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND);
if (!(fdwMenu & MF_CHECKED)) {
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes |= bMenuItemID;

} else {
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes ^= bMenuItemID;
}

/*
* If any font attributes are currently selected,
* remove the check mark from the Regular menu item;
* if no attributes are selected, add a check mark
* to the Regular menu item.
*/

if (fbAttributes & ATTRIBMASK) {
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes &= (BYTE) ~IDM_REGULAR;

} else {
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes = IDM_REGULAR;
}

return fbAttributes;
}
}

+$#AK Menu Reference
The following functions and messages are associated with menus.
+$#A Menu Functions
Following are the Windows functions used with menus.
AppendMenu_win32_AppendMenuCheckMenuItem_win32_CheckMenuItemCheckMenuRadioItem_win32_CheckMenuRadioItemCreateMenu_win32_CreateMenuCreatePopupMenu_win32_CreatePopupMenuDeleteMenu_win32_DeleteMenuDestroyMenu_win32_DestroyMenuDrawMenuBar_win32_DrawMenuBarEnableMenuItem_win32_EnableMenuItemGetMenu_win32_GetMenuGetMenuCheckMarkDimensions_win32_GetMenuCheckMarkDimensionsGetMenuDefaultItem_win32_GetMenuDefaultItemGetMenuItemCount_win32_GetMenuItemCountGetMenuItemID_win32_GetMenuItemIDGetMenuItemInfo_win32_GetMenuItemInfoGetMenuItemRect_win32_GetMenuItemRectGetMenuState_win32_GetMenuStateGetMenuString_win32_GetMenuStringGetSubMenu_win32_GetSubMenuGetSystemMenu_win32_GetSystemMenuHiliteMenuItem_win32_HiliteMenuItemInsertMenu_win32_InsertMenuInsertMenuItem_win32_InsertMenuItemIsMenu_win32_IsMenuLoadMenu_win32_LoadMenuLoadMenuIndirect_win32_LoadMenuIndirectMenuItemFromPoint_win32_MenuItemFromPointModifyMenu_win32_ModifyMenuRemoveMenu_win32_RemoveMenuSetMenu_win32_SetMenuSetMenuDefaultItem_win32_SetMenuDefaultItemSetMenuItemBitmaps_win32_SetMenuItemBitmapsSetMenuItemInfo_win32_SetMenuItemInfoTrackPopupMenu_win32_TrackPopupMenu TrackPopupMenuEx_win32_TrackPopupMenuEx

Obsolete or Deleted Functions
ChangeMenu_win32_ChangeMenu
+$#A Menu Messages
Following are the Windows messages used with menus.
WM_CONTEXTMENU_win32_WM_CONTEXTMENUWM_DRAWITEM_win32_WM_DRAWITEMWM_ENTERMENULOOP_win32_WM_ENTERMENULOOPWM_EXITMENULOOP_win32_WM_EXITMENULOOPWM_INITMENU_win32_WM_INITMENUWM_INITMENUPOPUP_win32_WM_INITMENUPOPUPWM_MEASUREITEM_win32_WM_MEASUREITEMWM_MENUCHAR_win32_WM_MENUCHARWM_MENUSELECT_win32_WM_MENUSELECTWM_SYSCOMMAND_win32_WM_SYSCOMMAND

+$#AK Hooks
A hook is a point in the Microsoft® Windows® message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.
This overview describes Windows hooks and explains how to use them in a Windows-based application.
+$#A About Hooks
Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.
+$#A Hook Chains
Windows contains many different types of hooks; each type provides access to a different aspect of the Windows message-handling mechanism. For example, an application can use the WH_MOUSE hook to monitor the message traffic for mouse messages.
Windows maintains a separate hook chain for each type of hook. A hook chain is a list of pointers to special, application-defined callback functions called hook procedures. When a message occurs that is associated with a particular type of hook, Windows passes the message to each hook procedure referenced in the hook chain, one after the other. The action a hook procedure can take depends on the type of hook involved. The hook procedures for some types of hooks can only monitor messages; others can modify messages or stop their progress though the chain, preventing them from reaching the next hook procedure or the destination window.
+$#A Hook Procedures
To take advantage of a particular type of hook, the developer provides a hook procedure and uses the SetWindowsHookEx_win32_SetWindowsHookEx function to install it into the chain associated with the hook. A hook procedure must have the following syntax:
LRESULT CALLBACK HookProc(nCode, wParam, lParam)int nCode;WPARAM wParam;LPARAM lParam;
HookProc is a placeholder for an application-defined name. Because Windows must call the hook procedure, the actual name must be exported by listing it and, optionally, its ordinal value, in the EXPORTS statement of the application's module-definition file.
The nCode parameter is a hook code that the hook procedure uses to determine the action to perform. The value of the hook code depends on the type of the hook; each type has its own characteristic set of hook codes. The values of the wParam and lParam parameters depend on the hook code, but they typically contain information about a message that was sent or posted.
SetWindowsHookEx_win32_SetWindowsHookEx always installs a hook procedure at the beginning of a hook chain. When an event o ccurs that is monit ored by a parti cular type of h o ok, Windows calls the procedure at the beginning of the hook chain associated with the hook. Each hook procedure in the chain determines whether to pass the event to the next procedure. A hook procedure passes an event to the next procedure by calling the CallNextHookEx_win32_CallNextHookEx function.
Note that the hook procedures for some types of hooks can only monitor messages. Windows passes messages to each hook procedure, regardless of whether a particular procedure calls CallNextHookEx.
A hook procedure can be global, monitoring messages for all threads in the system, or it can be thread specific, monitoring messages for only an individual thread. A global hook procedure can be called in the context of any application, so the procedure must be in a separate dynamic-link library (DLL) module. A thread specific hook procedure is called only in the context of the associated thread. If an application installs a hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the application's code or in a DLL. If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL. For information about DLLs, see Dynamic-Link Libraries_win32_Dynamic_Link_Libraries.

Note You should use global hooks only for debugging purposes; otherwise, you should avoid them. Global hooks hurt system performance and cause conflicts with other applications that implement the same type of global hook.

+$#A Hook Types
Each type of hook enables an application to monitor a different aspect of the Windows message-handling mechanism. The following sections describe the types of hooks available in Windows.
+$#A WH_CALLWNDPROC and WH_CALLWNDPROCRET Hooks
The WH_CALLWNDPROC and WH_CALLWNDPROCRET hooks enable an application to monitor messages sent to window procedures by the SendMessage_win32_SendMessage function. Windows calls a WH_CALLWNDPROC hook procedure before passing the message to the receiving window procedure, and calls the WH_CALLWNDPROCRET hook procedure after the window procedure has processed the message.
The WH_CALLWNDPROCRET hook passes the address of a CWPRETSTRUCT_win32_CWPRETSTRUCT_str structure to the hook procedure. The structure contains the return value from the window procedure that processed the message, as well as the message parameters associated with the message. In previous versions of Windows, an application that needed the return value had to set a WH_CALLWNDPROC hook and subclass the window when the hook was called. However, subclassing does not work for messages set between processes.
+$#A WH_CBT Hook
Windows calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the input focus; or before synchronizing with the system message queue. The value the hook procedure returns determines whether Windows allows or prevents one of these operations. The WH_CBT hook is intended primarily for Windows computer-based training (CBT) applications.
+$#A WH_DEBUG Hook
Windows calls a WH_DEBUG hook procedure before calling hook procedures associated with any other hook in the system. An application can use this hook to determine whether to allow the system to call hook procedures associated with other types of hooks.
+$#A WH_FOREGROUNDIDLE Hook
The WH_FOREGROUNDIDLE hook enables an application to peform low priority tasks during times when its foreground thread is idle. Windows calls a WH_FOREGROUNDIDLE hook procedure when the application's foreground thread is about to become idle.
+$#A WH_GETMESSAGE Hook
The WH_GETMESSAGE hook enables an application to monitor messages about to be returned by the GetMessage_win32_GetMessage or PeekMessage_win32_PeekMessage function. An application uses the WH_GETMESSAGE hook to monitor mouse and keyboard input and other messages posted to a queue.
+$#A WH_JOURNALRECORD Hook
The WH_JOURNALRECORD hook enables an application to monitor and record input events. Typically, an application uses this hook to record a sequence of mouse and keyboard events it can play back later by using the WH_JOURNALPLAYBACK hook. The WH_JOURNALRECORD hook is a global hook ¾ it cannot be used as a thread-specific hook.
+$#A WH_JOURNALPLAYBACK Hook
The WH_JOURNALPLAYBACK hook enables an application to insert messages into the system message queue. An application might use this hook to play back a series of mouse and keyboard events recorded earlier by using the WH_JOURNALRECORD hook. Regular mouse and keyboard input is disabled as long as a WH_JOURNALPLAYBACK hook is installed. A WH_JOURNALPLAYBACK hook is a global hook ¾ it cannot be used as a thread-specific hook.
The WH_JOURNALPLAYBACK hook returns a time-out value. This value tells the system how many milliseconds to wait before processing the current message from the playback hook. This enables the hook to control the timing of the events it plays back.
+$#A WH_KEYBOARD Hook
The WH_KEYBOARD hook enables an application to monitor message traffic for WM_KEYDOWN_win32_WM_KEYDOWN and WM_KEYUP_win32_WM_KEYUP messages about to be returned by the GetMessage_win32_GetMessage or PeekMessage_win32_PeekMessage function. An application uses the WH_KEYBOARD hook to monitor keyboard input posted to a message queue.
+$#A WH_MOUSE Hook
The WH_MOUSE hook enables an application to monitor mouse messages about to be returned by the GetMessage_win32_GetMessage or PeekMessage_win32_PeekMessage function. An application uses the WH_MOUSE hook to monitor mouse input posted to a message queue.
+$#A WH_MSGFILTER and WH_SYSMSGFILTER Hooks
The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable an application to monitor messages about to be processed by a menu, scroll bar, message box, or dialog box, and to detect when a different window is about to be activated as a result of the user's pressing the ALT+TAB or ALT+ESC key combination. The WH_MSGFILTER hook can monitor only messages passed to a menu, scroll bar, message box, or dialog box created by the application that installed the hook procedure. The WH_SYSMSGFILTER hook monitors such messages for all applications.
The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable an application to perform message filtering during modal loops that is equivalent to the filtering done in the main message loop. For example, an application often examines a new message in the main loop between the time it retrieves the message from the queue and the time it dispatches the message, performing special processing as appropriate. However, during a modal loop, the system retrieves and dispatches messages without allowing an application the chance to filter the messages in its main message loop. If an application installs a WH_MSGFILTER or WH_SYSMSGFILTER hook procedure, the system calls the procedure during the modal loop. For more information about modal loops, see Windows_win32_Windows.
An application can call the WH_MSGFILTER hook directly by calling the CallMsgFilter_win32_CallMsgFilter function. By using this function, the application can use the same code to filter messages during modal loops as it uses in the main message loop. To do so, the application encapsulates the filtering operations in a WH_MSGFILTER hook procedure and calls CallMsgFilter between the calls to the GetMessage_win32_GetMessage and DispatchMessage_win32_DispatchMessage functions.
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
if (!CallMsgFilter(&qmsg, 0))
DispatchMessage(&qmsg);
}

The last argument of CallMsgFilter_win32_CallMsgFilter is simply passed to the hook procedure; the application can enter any value. The hook procedure, by defining a constant such as MSGF_MAINLOOP, can use this value to determine where the procedure was called from.
+$#A WH_SHELL Hook
A Windows shell application can use the WH_SHELL hook to receive important notifications. Windows calls a WH_SHELL hook procedure when the shell application is about to be activated and when a top-level window is created or destroyed.
+$#A Using Hooks
· Installing and Releasing Hook Procedures_win32_Installing_and_Releasing_Hook_Procedures
· Monitoring System Events_win32_Monitoring_System_Events

+$#A Installing and Releasing Hook Procedures
You can install a hook procedure by calling the SetWindowsHookEx_win32_SetWindowsHookEx function and specifying the type of hook calling the procedure, whether the procedure should be associated with all threads or with a particular thread, and a pointer to a procedure entry point.
You must place a global hook procedure in a DLL separate from the application installing the hook procedure. The installing application must have the handle of the DLL module before it can install the hook procedure. The LoadLibrary_win32_LoadLibrary function, when given the name of the DLL, returns the handle of the DLL module. After you have the handle, you can call the GetProcAddress_win32_GetProcAddress function to retrieve the address of the hook procedure. Finally, you use SetWindowsHookEx to install the hook procedure address in the appropriate hook chain. SetWindowsHookEx passes the module handle, a pointer to the hook-procedure entry point, and 0 for the thread identifier, indicating that the hook procedure should be associated with all threads in the system. This sequence is shown in the following example.
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
.
.
.

hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll");
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");
hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,
hkprcSysMsg, hinstDLL, 0);
.
.
.

You can release a thread-specific hook procedure (remove its address from the hook chain) by calling the UnhookWindowsHookEx_win32_UnhookWindowsHookEx function, specifying the handle of the hook procedure to release. Release a hook procedure as soon as your application no longer needs it.
You can release a global hook procedure by using UnhookWindowsHookEx, but this function does not free the DLL containing the hook procedure. This is because global hook procedures are called in the process context of every Windows-based application in the system, causing an implicit call to the LoadLibrary_win32_LoadLibrary function for all of those processes. Because a call to the FreeLibrary_win32_FreeLibrary function cannot be made for another process, there is then no way to free the DLL. Windows eventually frees the DLL after all processes explicitly linked to the DLL have either terminated or called FreeLibrary and all processes that called the hook procedure have resumed processing outside the DLL.
An alternative method for installing a global hook procedure is to provide an installation function in the DLL, along with the hook procedure. With this method, the installing application does not need the handle of the DLL module. By linking with the DLL, the application gains access to the installation function. The installation function can supply the DLL module handle and other details in the call to SetWindowsHookEx_win32_SetWindowsHookEx. The DLL can also contain a function that releases the global hook procedure; the application can call this hook-releasing function when terminating.
+$#A Monitoring System Events
The example in this section uses a variety of thread-specific hook procedures to monitor the system for events affecting a thread. It demonstrates how to process events for the following types of hook procedures:
WH_CALLWNDPROCWH_CBTWH_DEBUGWH_GETMESSAGEWH_KEYBOARDWH_MOUSEWH_MSGFILTER
In this example, the user can install and remove a hook procedure by using the menu. When a hook procedure is installed and an event that is monitored by the procedure occurs, the procedure writes information about the event to the client area of the application's main window.
#define NUMHOOKS 7

/* Global variables */

typedef struct _MYHOOKDATA {
int nType;
HOOKPROC hkprc;
HHOOK hhook;
} MYHOOKDATA;

MYHOOKDATA myhookdata[NUMHOOKS];

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
UINT wParam;
LONG lParam;
{
static BOOL afHooks[NUMHOOKS];
int index;
static HMENU hmenu;

switch (uMsg) {
case WM_CREATE:

/* Save the menu handle. */

hmenu = GetMenu(hwndMain);

/*
* Initialize structures with hook data. The menu-item
* identifiers are defined as 0 through 6 in the
* header file. They can be used to identify array
* elements both here and during the WM_COMMAND
* message.
*/

myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC;
myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc;
myhookdata[IDM_CBT].nType = WH_CBT;
myhookdata[IDM_CBT].hkprc = CBTProc;
myhookdata[IDM_DEBUG].nType = WH_DEBUG;
myhookdata[IDM_DEBUG].hkprc = DebugProc;
myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE;
myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc;
myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD;
myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc;
myhookdata[IDM_MOUSE].nType = WH_MOUSE;
myhookdata[IDM_MOUSE].hkprc = MouseProc;
myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER;
myhookdata[IDM_MSGFILTER].hkprc = MessageProc;

/* Initialize all flags in the array to FALSE. */

memset(afHooks, FALSE, sizeof(afHooks));

return 0;

case WM_COMMAND:
switch (LOWORD(wParam)) {

/*
* The user selected a hook command from the menu.
*/

case IDM_CALLWNDPROC:
case IDM_CBT:
case IDM_DEBUG:
case IDM_GETMESSAGE:
case IDM_KEYBOARD:
case IDM_MOUSE:
case IDM_MSGFILTER:

/*
* Use the menu-item identifier as an index
* into the array of structures with hook data.
*/

index = LOWORD(wParam);

/*
* If the selected type of hook procedure isn't
* installed yet, install it and check the
* associated menu item.
*/

if (!afHooks[index]) {
myhookdata[index].hhook = SetWindowsHookEx(
myhookdata[index].nType,
myhookdata[index].hkprc,
(HINSTANCE) NULL, GetCurrentThreadId());
CheckMenuItem(hmenu, index,
MF_BYCOMMAND | MF_CHECKED);
afHooks[index] = TRUE;
}

/*
* If the selected type of hook procedure is
* already installed, remove it and remove the
* check mark from the associated menu item.
*/

else {
UnhookWindowsHookEx(myhookdata[index].hhook);
CheckMenuItem(hmenu, index,
MF_BYCOMMAND | MF_UNCHECKED);
afHooks[index] = FALSE;
}

default:
return (DefWindowProc(hwndMain, uMsg, wParam,
lParam));
}
break;

.
. /* Process other messages. */
.

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

/****************************************************************
WH_CALLWNDPROC hook procedure
****************************************************************/

LRESULT CALLBACK CallWndProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szCWPBuf[256];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
wParam, lParam);

/*
* Call an application-defined function that converts a message
* constant to a string and copies it to a buffer.
*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);

switch (nCode) {
case HC_ACTION:
cch = wsprintf(szCWPBuf,
"CALLWNDPROC - tsk: %ld, msg: %s, %d times ",
wParam, szMsg, c++);
TextOut(hdc, 2, 15, szCWPBuf, cch);
break;

default:
break;
}

ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
wParam, lParam);
}

/****************************************************************
WH_GETMESSAGE hook procedure
****************************************************************/

LRESULT CALLBACK GetMsgProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szMSGBuf[256];
CHAR szRem[16];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
wParam, lParam);

switch (nCode) {
case HC_ACTION:
switch (wParam) {
case PM_REMOVE:
lstrcpy(szRem, "PM_REMOVE");
break;

case PM_NOREMOVE:
lstrcpy(szRem, "PM_NOREMOVE");
break;

default:
lstrcpy(szRem, "Unknown");
break;
}

/*
* Call an application-defined function that converts a
* message constant to a string and copies it to a
* buffer.
*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);
cch = wsprintf(szMSGBuf,
"GETMESSAGE - wParam: %s, msg: %s, %d times ",
szRem, szMsg, c++);
TextOut(hdc, 2, 35, szMSGBuf, cch);
break;

default:
break;
}

ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
wParam, lParam);
}

/****************************************************************
WH_DEBUG hook procedure
****************************************************************/

LRESULT CALLBACK DebugProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[DEBUG].hhook, nCode,
wParam, lParam);

hdc = GetDC(hwndMain);

switch (nCode) {
case HC_ACTION:
cch = wsprintf(szBuf,
"DEBUG - nCode: %d, tsk: %ld, %d times ",
nCode,wParam, c++);
TextOut(hdc, 2, 55, szBuf, cch);
break;

default:
break;
}

ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam,
lParam);
}

/****************************************************************
WH_CBT hook procedure
****************************************************************/

LRESULT CALLBACK CBTProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szBuf[128];
CHAR szCode[128];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,
lParam);

hdc = GetDC(hwndMain);

switch (nCode) {
case HCBT_ACTIVATE:
lstrcpy(szCode, "HCBT_ACTIVATE");
break;

case HCBT_CLICKSKIPPED:
lstrcpy(szCode, "HCBT_CLICKSKIPPED");
break;

case HCBT_CREATEWND:
lstrcpy(szCode, "HCBT_CREATEWND");
break;

case HCBT_DESTROYWND:
lstrcpy(szCode, "HCBT_DESTROYWND");
break;

case HCBT_KEYSKIPPED:
lstrcpy(szCode, "HCBT_KEYSKIPPED");
break;

case HCBT_MINMAX:
lstrcpy(szCode, "HCBT_MINMAX");
break;

case HCBT_MOVESIZE:
lstrcpy(szCode, "HCBT_MOVESIZE");
break;

case HCBT_QS:
lstrcpy(szCode, "HCBT_QS");
break;

case HCBT_SETFOCUS:
lstrcpy(szCode, "HCBT_SETFOCUS");
break;

case HCBT_SYSCOMMAND:
lstrcpy(szCode, "HCBT_SYSCOMMAND");
break;

default:
lstrcpy(szCode, "Unknown");
break;
}

cch = wsprintf(szBuf, "CBT - nCode: %s, tsk: %ld, %d times ",
szCode, wParam, c++);
TextOut(hdc, 2, 75, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,
lParam);
}

/****************************************************************
WH_MOUSE hook procedure
****************************************************************/

LRESULT CALLBACK MouseProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szBuf[128];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process the message */
return CallNextHookEx(myhookdata[MOUSE].hhook, nCode,
wParam, lParam);

/*
* Call an application-defined function that converts a message
* constant to a string and copies it to a buffer.
*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);
cch = wsprintf(szBuf,
"MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times ",
nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++);
TextOut(hdc, 2, 95, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam,
lParam);
}

/****************************************************************
WH_KEYBOARD hook procedure
****************************************************************/

LRESULT CALLBACK KeyboardProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode,
wParam, lParam);

hdc = GetDC(hwndMain);
cch = wsprintf(szBuf, "KEYBOARD - nCode: %d, vk: %d, %d times ",
nCode, wParam, c++);
TextOut(hdc, 2, 115, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam,
lParam);
}

/****************************************************************
WH_MSGFILTER hook procedure
****************************************************************/

LRESULT CALLBACK MessageProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;
{
CHAR szBuf[128];
CHAR szMsg[16];
CHAR szCode[32];
HDC hdc;
static int c = 0;
int cch;

if (nCode < 0) /* do not process message */
return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
wParam, lParam);

switch (nCode) {
case MSGF_DIALOGBOX:
lstrcpy(szCode, "MSGF_DIALOGBOX");
break;

case MSGF_MENU:
lstrcpy(szCode, "MSGF_MENU");
break;

case MSGF_SCROLLBAR:
lstrcpy(szCode, "MSGF_SCROLLBAR");
break;

case MSGF_NEXTWINDOW:
lstrcpy(szCode, "MSGF_NEXTWINDOW");
break;

default:
wsprintf(szCode, "Unknown: %d", nCode);
break;
}

/*
* Call an application-defined function that converts a message
* constant to a string and copies it to a buffer.
*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);
cch = wsprintf(szBuf,
"MSGFILTER nCode: %s, msg: %s, %d times ",
szCode, szMsg, c++);
TextOut(hdc, 2, 135, szBuf, cch);
ReleaseDC(hwndMain, hdc);
return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
wParam, lParam);
}

+$#AK Hook Reference
The following functions and messages are associated with hooks.
+$#A Hook Functions
Following are the functions used with hooks.
CallMsgFilter_win32_CallMsgFilterCallNextHookEx_win32_CallNextHookExCallWndProc_win32_CallWndProcCBTProc_win32_CBTProcDebugProc_win32_DebugProcGetMsgProc_win32_GetMsgProcJournalPlaybackProc_win32_JournalPlaybackProcJournalRecordProc_win32_JournalRecordProcKeyboardProc_win32_KeyboardProcMessageProc_win32_MessageProcMouseProc_win32_MouseProcSetWindowsHookEx_win32_SetWindowsHookExShellProc_win32_ShellProcSysMsgProc_win32_SysMsgProcUnhookWindowsHookEx_win32_UnhookWindowsHookEx
Obsolete or Deleted Functions
DefHookProc_win32_DefHookProcSetWindowsHook_win32_SetWindowsHookUnhookWindowsHook_win32_UnhookWindowsHook
+$#A Hook Messages
Following are the messages used with hooks.
WM_QUEUESYNC_win32_WM_QUEUESYNC

+$#AK Icons
An icon is a picture that consists of a bitmapped image combined with a mask to create transparent areas in the picture. This overview describes creating, displaying, destroying, and duplicating icons.
+$#A About Icons
Windows uses icons throughout the user interface to represent objects such as files, printers, applications, and windows. The icon functions in the Microsoft® Win32® application programming interface (API) enable applications to create, load, display, arrange, and destroy icons.
The operating system provides a set of standard icons that are available for any application to use at any time. The Windows header files contain identifiers for the standard icons ¾ the identifiers begin with the "IDI_" prefix.
Each standard icon has a corresponding default image associated with it. The user can replace the default image associated with any standard cursor at any time.
An application can retrieve the image for a cursor by using the GetIconInfo_win32_GetIconInfo function, and can draw it by using the DrawIconEx_win32_DrawIconEx function. To draw the default image for a cursor, specify the DI_COMPAT flag in the call to DrawIconEx. If you do not specify the DI_COMPAT flag, DrawIconEx draws the cursor using the image that the user specified.
Custom icons are designed for use in a particular application and can be any design the developer defines. Following are several custom icons.
{ewc msdncd, EWGraphic, group10802 0 /a "SDK_02.bmp"}
+$#A The Iconic Hot Spot
One of the pixels in an icon is designated as the hot spot, which is the point the system tracks and recognizes as the position of the icon. An icon's hot spot is typically the pixel located at the center of the icon. If you use the CreateIconIndirect_win32_CreateIconIndirect function to create an icon, you can specify any pixel to be the hot spot.
+$#A Icon Creation
Standard icons are predefined in Win32, so it is not necessary to create them. To use a standard icon, an application can obtain its handle by using the LoadImage_win32_LoadImage function. An icon handle is a unique value of the HICON type that identifies a standard or custom icon.
To create a custom icon for an application, developers typically use a graphics application and include the icon as a resource in the application's resource-definition file. At run time, an application can call LoadIcon_win32_LoadIcon or LoadImage to retrieve the handle of the icon. An icon resource contains data for several different display devices. LoadIcon and LoadImage automatically select the most appropriate data for the current display device.
An application can also create a custom icon at run time by using the CreateIconIndirect_win32_CreateIconIndirect function, which creates an icon based on the contents of an ICONINFO_win32_ICONINFO_str structure. The GetIconInfo_win32_GetIconInfo function fills the structure with the hot spot coordinates and information about the bitmask bitmap and color bitmap for the icon.
Applications should implement custom icons as resources and should use LoadIcon or LoadImage, rather than create the icon at run time. Using icon resources avoids device dependence, simplifies localization, and enables applications to share icon shapes.
The CreateIconFromResourceEx_win32_CreateIconFromResourceEx function enables an application to browse through the system's resources and create icons and cursors based on resource data. CreateIconFromResourceEx creates an icon based on binary resource data from other executable files or dynamic-link libraries (DLLs). An application must precede this function with calls to the LookupIconIdFromDirectoryEx_win32_LookupIconIdFromDirectoryEx function and several of the resource functions. LookupIconIdFromDirectoryEx returns the identifier of the most appropriate icon data for the current display device. For more information about the resource functions, see Resources_win32_Resources.
+$#A Icon Display
Once an application has loaded an icon, it can use the DrawIcon_win32_DrawIcon or DrawIconEx_win32_DrawIconEx functions to draw the icon.
When registering a window class, an application can assign one or two icons to the class, depending on the operating system version. A class icon is a picture that the system uses to represent a window of a particular class. For more information about class icons, see Window Classes_win32_Window_Classes.
+$#A Icon Destruction
When an application no longer needs an icon it created by using the CreateIconIndirect_win32_CreateIconIndirect (or CreateIcon_win32_CreateIcon) function, it should destroy the icon. The DestroyIcon_win32_DestroyIcon function destroys the icon handle and frees any memory used by the icon. Applications should use this function only for icons created with CreateIconIndirect; it is not necessary to destroy other icons.
+$#A Icon Duplication
The CopyIcon_win32_CopyIcon function copies an icon handle. This enables an application or DLL to get its own handle for an icon owned by another module. Then, if the other module is freed, the application that copied the icon will still be able to use the icon.
The CopyImage_win32_CopyImage function creates a new icon based on the specified source icon. The new icon can be larger or smaller than the source icon.
For information about adding, removing, or replacing icon resources in executable (.EXE) files, see Resources_win32_Resources.
+$#A Using Icons
· Creating an icon_win32_Creating_an_Icon
· Displaying an icon_win32_Displaying_an_Icon
· Using icon functions to create a mousetrap_win32_Using_Icon_Functions_to_Create_a_Mousetrap
· Sharing icon resources_win32_Sharing_Icon_Resources

+$#A Creating an Icon
To use an icon, your application must get the handle of the icon. The following example shows how to create two different icon handles: one for the standard exclamation icon and one for a custom icon included as a resource in the application's resource-definition file.
HICON hIcon1; /* icon handle */
HICON hIcon2; /* icon handle */

/* Create a standard question icon. */

hIcon1 = LoadIcon(NULL, IDI_QUESTION);

/* Create a custom icon based on a resource. */

hIcon2 = LoadIcon(hinst, MAKEINTRESOURCE(460));

/* Create a custom icon at run time. */

An application should implement custom icons as resources and should use the LoadIcon_win32_LoadIcon or LoadImage_win32_LoadImage function, rather than create the icons at run time. This approach avoids device dependence, simplifies localization, and enables applications to share icon bitmaps. However, the following example uses CreateIcon_win32_CreateIcon to create a custom icon at run time, based on bitmap bitmasks; it is included to illustrate how the system interprets icon bitmap bitmasks.
HICON hIcon3; /* icon handle */

/* Yang icon AND bitmask */

BYTE ANDmaskIcon[] = {0xFF, 0xFF, 0xFF, 0xFF, /* line 1 */
0xFF, 0xFF, 0xC3, 0xFF, /* line 2 */
0xFF, 0xFF, 0x00, 0xFF, /* line 3 */
0xFF, 0xFE, 0x00, 0x7F, /* line 4 */

0xFF, 0xFC, 0x00, 0x1F, /* line 5 */
0xFF, 0xF8, 0x00, 0x0F, /* line 6 */
0xFF, 0xF8, 0x00, 0x0F, /* line 7 */
0xFF, 0xF0, 0x00, 0x07, /* line 8 */

0xFF, 0xF0, 0x00, 0x03, /* line 9 */
0xFF, 0xE0, 0x00, 0x03, /* line 10 */
0xFF, 0xE0, 0x00, 0x01, /* line 11 */
0xFF, 0xE0, 0x00, 0x01, /* line 12 */

0xFF, 0xF0, 0x00, 0x01, /* line 13 */
0xFF, 0xF0, 0x00, 0x00, /* line 14 */
0xFF, 0xF8, 0x00, 0x00, /* line 15 */
0xFF, 0xFC, 0x00, 0x00, /* line 16 */

0xFF, 0xFF, 0x00, 0x00, /* line 17 */
0xFF, 0xFF, 0x80, 0x00, /* line 18 */
0xFF, 0xFF, 0xE0, 0x00, /* line 19 */
0xFF, 0xFF, 0xE0, 0x01, /* line 20 */

0xFF, 0xFF, 0xF0, 0x01, /* line 21 */
0xFF, 0xFF, 0xF0, 0x01, /* line 22 */
0xFF, 0xFF, 0xF0, 0x03, /* line 23 */
0xFF, 0xFF, 0xE0, 0x03, /* line 24 */

0xFF, 0xFF, 0xE0, 0x07, /* line 25 */
0xFF, 0xFF, 0xC0, 0x0F, /* line 26 */
0xFF, 0xFF, 0xC0, 0x0F, /* line 27 */
0xFF, 0xFF, 0x80, 0x1F, /* line 28 */

0xFF, 0xFF, 0x00, 0x7F, /* line 29 */
0xFF, 0xFC, 0x00, 0xFF, /* line 30 */
0xFF, 0xF8, 0x03, 0xFF, /* line 31 */
0xFF, 0xFC, 0x3F, 0xFF}; /* line 32 */

/* Yang icon XOR bitmask */

BYTE XORmaskIcon[] = {0x00, 0x00, 0x00, 0x00, /* line 1 */
0x00, 0x00, 0x00, 0x00, /* line 2 */
0x00, 0x00, 0x00, 0x00, /* line 3 */
0x00, 0x00, 0x00, 0x00, /* line 4 */

0x00, 0x00, 0x00, 0x00, /* line 5 */
0x00, 0x00, 0x00, 0x00, /* line 6 */
0x00, 0x00, 0x00, 0x00, /* line 7 */
0x00, 0x00, 0x38, 0x00, /* line 8 */

0x00, 0x00, 0x7C, 0x00, /* line 9 */
0x00, 0x00, 0x7C, 0x00, /* line 10 */
0x00, 0x00, 0x7C, 0x00, /* line 11 */
0x00, 0x00, 0x38, 0x00, /* line 12 */

0x00, 0x00, 0x00, 0x00, /* line 13 */
0x00, 0x00, 0x00, 0x00, /* line 14 */
0x00, 0x00, 0x00, 0x00, /* line 15 */
0x00, 0x00, 0x00, 0x00, /* line 16 */

0x00, 0x00, 0x00, 0x00, /* line 17 */
0x00, 0x00, 0x00, 0x00, /* line 18 */
0x00, 0x00, 0x00, 0x00, /* line 19 */
0x00, 0x00, 0x00, 0x00, /* line 20 */

0x00, 0x00, 0x00, 0x00, /* line 21 */
0x00, 0x00, 0x00, 0x00, /* line 22 */
0x00, 0x00, 0x00, 0x00, /* line 23 */
0x00, 0x00, 0x00, 0x00, /* line 24 */

0x00, 0x00, 0x00, 0x00, /* line 25 */
0x00, 0x00, 0x00, 0x00, /* line 26 */
0x00, 0x00, 0x00, 0x00, /* line 27 */
0x00, 0x00, 0x00, 0x00, /* line 28 */

0x00, 0x00, 0x00, 0x00, /* line 29 */
0x00, 0x00, 0x00, 0x00, /* line 30 */
0x00, 0x00, 0x00, 0x00, /* line 31 */
0x00, 0x00, 0x00, 0x00}; /* line 32 */

hIcon3 = CreateIcon(hinst, /* application instance */
32, /* icon width */
32, /* icon height */
1, /* number of XOR planes */
1, /* number of bits per pixel */
ANDmaskIcon, /* AND bitmask */
XORmaskIcon); /* XOR bitmask */

To create the icon, CreateIcon_win32_CreateIcon applies the following truth table to the AND and XOR bitmasks.
AND bitmask XOR bitmask Display
0 0 Black
0 1 White
1 0 Screen
1 1 Reverse screen

For information about full-color bitmaps, see Bitmaps_win32_Bitmaps and Colors_win32_Colors.
Before closing, your application must use DestroyIcon_win32_DestroyIcon to destroy any icon it created by using CreateIcon_win32_CreateIcon. It is not necessary to destroy icons created by other functions.
+$#A Displaying an Icon
Your application can load and create icons to display in the application's client area or child windows. The following example demonstrates how to draw an icon in the client area of the window whose display context (DC) is identified by the hdc parameter.
HICON hIcon1; /* icon handle */
HDC hdc; /* handle of display context */

DrawIcon(hdc, 10, 20, hIcon1);

Windows automatically displays the class icon(s) for a window. Your application can assign class icons while registering a window class. Your application can replace a class icon by using the SetClassLong_win32_SetClassLong function. This function changes the default window settings for all windows of a given class. The following example replaces a class icon with the icon whose resource identifier is 480.
HINSTANCE hinst; /* handle of current instance */
HWND hwnd; /* main window handle */

/* Change the icon for hwnd's window class. */

SetClassLong(hwnd, /* window handle */
GCL_HICON, /* changes icon */
(LONG) LoadIcon(hinst, MAKEINTRESOURCE(480))); /* new */

For more information about window classes, see Window Classes_win32_Window_Classes.
+$#A Using Icon Functions to Create a Mousetrap
The following example applies to operating system versions earlier than 4.0. It uses IsIconic_win32_IsIconic, LoadIcon_win32_LoadIcon, and several cursor and timer functions to create a simple mousetrap. The example uses the cursor and timer functions to monitor the location of the cursor at 10-second intervals. If the cursor location has not changed in the last 10 seconds and the application's main window is minimized, the application changes the cursor shape and moves the cursor to the icon.
HICON hIcon1; /* icon handle */
POINT ptOld; /* previous cursor location */
HCURSOR hCurs1; /* cursor handle */
HINSTANCE hinstance; /* handle of current instance */

hCurs1 = LoadCursor(hinstance, MAKEINTRESOURCE(220));

.
. /* Fill in the window class structure. */
.

WNDCLASS wc;

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(470));
wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(240));

.
. /*
. * Register the window class and perform
. * other application initialization.
. */
.

/* Set a timer for the mousetrap. */

GetCursorPos(&ptOld);

SetTimer(hwnd, IDT_CURSOR, 10000, (TIMERPROC) NULL);

LONG APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{

HDC hdc; /* device context handle */
POINT pt; /* current cursor location */
RECT rc; /* iconized window location */

switch (message) {
.
. /* Process other messages here. */
.

case WM_TIMER:

/*
* If the window is minimized, compare the current
* cursor position with the one from 10 seconds
* earlier. If the cursor position has not changed,
* move the cursor to the icon.
*/

if (IsIconic(hwnd)) {
GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {
GetWindowRect(hwnd, &rc);
SetCursorPos(rc.left, rc.top);

}
else {

ptOld.x = pt.x;
ptOld.y = pt.y;
}
}

return 0;

case WM_SETCURSOR:

/*
* If the window is minimized, draw hCurs1.
* If the window is not minimized, draw the default
* cursor (class cursor).
*/

if (IsIconic(hwnd)) {
SetCursor(hCurs1);
break;
}

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_CURSOR);

PostQuitMessage(0);
break;
.
. /* Process other messages here. */
.

} /* ends switch (message) */

An example for a similar mousetrap is included in Cursors_win32_Cursors. That example uses CreateIcon_win32_CreateIcon to create the class icon.
+$#A Sharing Icon Resources
The following code uses the functions CreateIconFromResourceEx_win32_CreateIconFromResourceEx, DrawIcon_win32_DrawIcon, and LookupIconIdFromDirectoryEx_win32_LookupIconIdFromDirectoryEx, and several of the resource functions, to create an icon handle based on icon data from another executable file. Then, it displays the icon in a window.
HICON hIcon1; /* icon handle */
HINSTANCE hExe; /* handle to loaded .EXE file */
HRSRC hResource; /* handle for FindResource */
HRSRC hMem; /* handle for LoadResource */
BYTE *lpResource; /* address of resource data */
int nID; /* ID of resource that best
fits current screen */

HDC hdc; /* handle of display context */

/* Load the file from which to copy the icon. */

hExe = LoadLibrary("myapp.exe");

/* Find the icon directory whose identifier is 440. */

hResource = FindResource(hExe,
MAKEINTRESOURCE(440),
RT_GROUP_ICON);

/* Load and lock the icon directory. */

hMem = LoadResource(hExe, hResource);

lpResource = LockResource(hMem);

/*
* Get the identifier of the icon that is most appropriate
* for the video display.
*/

nID = LookupIconIdFromDirectoryEx((PBYTE) lpResource, TRUE,
CXICON, CYICON, LR_DEFAULTCOLOR);

/* Find the bits for the nID icon. */

hResource = FindResource(hExe,
MAKEINTRESOURCE(nID),
MAKEINTRESOURCE(RT_ICON));

/* Load and lock the icon. */

hMem = LoadResource(hExe, hResource);

lpResource = LockResource(hMem);

/* Create a handle to the icon. */

hIcon1 = CreateIconFromResourceEx((PBYTE) lpResource,
SizeofResource(hExe, hResource), TRUE, 0x00030000,
CXICON, CYICON, LR_DEFAULTCOLOR);

/* Draw the icon in the client area. */

DrawIcon(hdc, 10, 20, hIcon1);

+$#AK Icon Reference
The following functions and messages are associated with icons.
+$#A Icon Functions
Following are the functions used with icons.
ArrangeIconicWindows_win32_ArrangeIconicWindowsCopyIcon_win32_CopyIconCreateIcon_win32_CreateIconCreateIconFromResource_win32_CreateIconFromResourceCreateIconIndirect_win32_CreateIconIndirectDestroyIcon_win32_DestroyIconDrawIcon_win32_DrawIconDrawIconEx_win32_DrawIconExGetIconInfo_win32_GetIconInfoIsIconic_win32_IsIconicLoadIcon_win32_LoadIconLookupIconIdFromDirectory_win32_LookupIconIdFromDirectoryOpenIcon_win32_OpenIcon
+$#A Icon Messages
Following are the messages used with icons.
WM_ERASEBKGND_win32_WM_ERASEBKGNDWM_ICONERASEBKGND_win32_WM_ICONERASEBKGNDWM_PAINTICON_win32_WM_PAINTICON

+$#AK Keyboard Accelerators
In Microsoft® Windows®, a keyboard accelerator (or, simply, accelerator) is a keystroke or combination of keystrokes that generates a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message for an application.
+$#A About Keyboard Accelerators
Accelerators are closely related to menus ¾ both provide the user with access to an application's command set. Typically, users rely on an application's menus to learn the command set and then switch over to using accelerators as they become more proficient with the application. Accelerators provide faster, more direct access to commands than menus do. At a minimum, an application should provide accelerators for the more commonly used commands. Although accelerators typically generate commands that exist as menu items, they can also generate commands that have no equivalent menu items.
+$#A Accelerator Tables
An accelerator table consists of an array of ACCEL_win32_ACCEL_str structures, each defining an individual accelerator. Each ACCEL structure includes the following information:
· The accelerator's keystroke combination.
· The accelerator's identifier.
· Various flags. This includes one that specifies whether Windows is to provide visual feedback by highlighting the corresponding menu item, if any, when the accelerator is used

To process accelerator keystrokes for a given thread, the developer must call the TranslateAccelerator_win32_TranslateAccelerator function in the message loop associated with the thread's message queue. The TranslateAccelerator function monitors keyboard input to the message queue, checking for key combinations that match an entry in the accelerator table. When TranslateAccelerator finds a match, it translates the keyboard input (that is, the WM_KEYUP_win32_WM_KEYUP and WM_KEYDOWN_win32_WM_KEYDOWN messages) into a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message and then sends the message to the window procedure of the specified window. The following illustration shows how accelerators are processed.
{ewc msdncd, EWGraphic, group10803 0 /a "SDK_01.bmp"}
The WM_COMMAND message includes the identifier of the accelerator that caused TranslateAccelerator to generate the message. The window procedure examines the identifier to determine the source of the message and then processes the message accordingly.
Accelerator tables exist at two different levels in Windows. Windows maintains a single, system-wide accelerator table that applies to all applications. An application cannot modify the system accelerator table. For a description of the accelerators provided by the system accelerator table, see Accelerator Keystroke Assignments_win32_Accelerator_Keystroke_Assignments.
Windows also maintains accelerator tables for each application. An application can define any number of accelerator tables for use with its own windows. A unique 32-bit handle (HACCEL) identifies each table. However, only one accelerator table can be active at a time for a given thread. The handle of the accelerator table passed to the TranslateAccelerator_win32_TranslateAccelerator function determines which accelerator table is active for a thread. The active accelerator table can be changed at any time by passing a different accelerator-table handle to TranslateAccelerator.
+$#A Accelerator-Table Creation
Several steps are required to create an accelerator table for an application. First, a resource compiler is used to create accelerator-table resources and to add them to the application's executable file. At run time, the LoadAccelerators_win32_LoadAccelerators function is used to load the accelerator table into memory and retrieve the handle of the accelerator table. This handle is passed to the TranslateAccelerator_win32_TranslateAccelerator function to activate the accelerator table.
An accelerator table can also be created for an application at run time by passing an array of ACCEL_win32_ACCEL_str structures to the CreateAcceleratorTable_win32_CreateAcceleratorTable function. This method supports user-defined accelerators in the application. Like the LoadAccelerators function, CreateAcceleratorTable returns an accelerator-table handle that can be passed to TranslateAccelerator to activate the accelerator table.
Windows automatically destroys accelerator tables loaded by LoadAccelerators. An accelerator table created by CreateAcceleratorTable must be destroyed before an application closes; otherwise, the table continues to exist in memory after the application has closed. An accelerator table is destroyed by calling the DestroyAcceleratorTable_win32_DestroyAcceleratorTable function.
An existing accelerator table can be copied and modified. The existing accelerator table is copied by using the CopyAcceleratorTable_win32_CopyAcceleratorTable function. After the copy is modified, a handle of the new accelerator table is retrieved by calling CreateAcceleratorTable. Finally, the handle is passed to TranslateAccelerator to activate the new table.
+$#A Accelerator Keystroke Assignments
An ASCII character code or a virtual-key code can be used to define the accelerator. An ASCII character code makes the accelerator case sensitive. The ASCII "C" character can define the accelerator as ALT+c rather than ALT+C. Case-sensitive accelerators can, however, be confusing to use. For example, the ALT+C accelerator will be generated if the CAPS LOCK key is down or if the SHIFT key is down, but not if both are down.
Typically, accelerators don't need to be case sensitive, so most applications use virtual-key codes for accelerators rather than ASCII character codes. .
Avoid accelerators that conflict with an application's menu mnemonics, because the accelerator overrides the mnemonic, which can confuse the user. For more information about menu mnemonics, see Menus (Windows NT)_win32_Menus_Windows_NT_.
If an application defines an accelerator that is also defined in the system accelerator table, the application-defined accelerator overrides the system accelerator, but only within the context of the application. Avoid this practice, however, because it prevents the system accelerator from performing its standard role in the Windows user interface. The system-wide accelerators are described in the following list:
Accelerator Description
ALT+ESC Switches to the next application.
ALT+F4 Closes an application or a window.
ALT+HYPHEN Opens the System menu for a document window.
ALT+PRINT SCREEN Copies an image in the active window onto the clipboard.
ALT+SPACEBAR Opens the System menu for an application window.
ALT+TAB Switches to the next application.
CTRL+ESC Switches to Windows Task List.
CTRL+F4 Closes the active group or document window.
F1 Starts Help if the application has Help.
PRINT SCREEN Copies an image on the screen onto the clipboard.
SHIFT+ALT+TAB Switches to the previous application. The user must press and hold down ALT+SHIFT while pressing TAB.

+$#A Accelerators and Menus
Using an accelerator is the same as choosing a menu item: Both actions cause Windows to send a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message to the corresponding window procedure. The WM_COMMAND message includes an identifier that the window procedure examines to determine the source of the message. If an accelerator generated the WM_COMMAND message, the identifier is that of the accelerator. Similarly, if a menu item generated the WM_COMMAND message, the identifier is that of the menu item. Because an accelerator provides a shortcut for choosing a command from a menu, an application usually assigns the same identifier to the accelerator and the corresponding menu item.
An application processes an accelerator WM_COMMAND message in exactly the same way as the corresponding menu item WM_COMMAND message. However, the WM_COMMAND message contains a flag that specifies whether the message originated from an accelerator or a menu item, in case accelerators must be processed differently from their corresponding menu items. The WM_SYSCOMMAND message does not contain this flag.
The identifier determines whether an accelerator generates a WM_COMMAND or WM_SYSCOMMAND message. If the identifier has the same value as a menu item in the System menu, the accelerator generates a WM_SYSCOMMAND message. Otherwise, the accelerator generates a WM_COMMAND message.
If an accelerator has the same identifier as a menu item and the menu item is grayed or disabled, the accelerator is disabled and does not generate a WM_COMMAND or WM_SYSCOMMAND message. Also, an accelerator does not generate a command message if the corresponding window is minimized.
When the user uses an accelerator that corresponds to a menu item, the window procedure receives the WM_INITMENU_win32_WM_INITMENU and WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP messages as though the user had selected the menu item. For information about how to process these messages, see Menus (Windows NT)_win32_Menus_Windows_NT_.
An accelerator that corresponds to a menu item should be included in the text of the menu item.
+$#A Using Keyboard Accelerators
· Using an accelerator-table resource_win32_Using_an_Accelerator_Table_Resource
· Using an accelerator table created at run time_win32_Using_an_Accelerator_Table_Created_at_Run_Time

+$#A Using an Accelerator-Table Resource
The most common way to add accelerator support to a Windows-based application is to include an accelerator-table resource with the application's executable file and then load the resource at run time. The steps involved in using an accelerator-table resource are as follows:
· Create an accelerator table in a resource-definition file. Compile it and add the resulting resource to your application's executable file.
· Include a call to the LoadAccelerators_win32_LoadAccelerators function to load the accelerator-table resource and receive a handle of the accelerator table.
· Add the TranslateAccelerator_win32_TranslateAccelerator function to the message loop associated with the accelerator table.
· Process the WM_COMMAND_win32_WM_COMMAND messages generated when the user uses the accelerator.

+$#A Creating the Accelerator-Table Resource
You create an accelerator-table resource by using the ACCELERATORS statement in your application's resource-definition file. You must assign a name or resource identifier to the accelerator table, preferably unlike that of any other resource. Windows uses this identifier to load the resource at run time.
Each accelerator you define requires a separate entry in the accelerator table. In each entry, you define the keystroke (either an ASCII character code or virtual-key code) that generates the accelerator and the accelerator's identifier. You must also specify whether the keystroke must be used in some combination with the ALT, SHIFT, or CTRL keys. For more information about virtual keys, see Keyboard Input_win32_Keyboard_Input.
An ASCII keystroke is specified either by enclosing the ASCII character in double quotation marks or by using the integer value of the character in combination with the ASCII flag. The following examples show how to define ASCII accelerators.
"A", ID_ACCEL1 ; SHIFT+A
65, ID_ACCEL2, ASCII ; SHIFT+A

A virtual-key code keystroke is specified differently depending on whether the keystroke is an alphanumeric key or a non-alphanumeric key. For an alphanumeric key, the key's letter or number, enclosed in double quotation marks, is combined with the VIRTKEY flag. For a non-alphanumeric key, the Windows virtual-key code for the specific key is combined with the VIRTKEY flag. The following examples show how to define virtual-key code accelerators.
"a", ID_ACCEL3, VIRTKEY ; A (caps-lock on) or a
VK_INSERT, ID_ACCEL4, VIRTKEY ; INSERT key

The following example shows an accelerator-table resource that defines accelerators for file operations. The name of the resource is FileAccel.
FileAccel ACCELERATORS
BEGIN
VK_F12, IDM_OPEN, CONTROL, VIRTKEY ; CTRL+F12
VK_F4, IDM_CLOSE, ALT, VIRTKEY ; ALT+F4
VK_F12, IDM_SAVE, SHIFT, VIRTKEY ; SHIFT+F12
VK_F12, IDM_SAVEAS, VIRTKEY ; F12
END

If you want the user to press the ALT, SHIFT, or CTRL keys in some combination with the accelerator keystroke, specify the ALT, SHIFT, and CONTROL flags in the accelerator's definition. Following are some examples.
"B", ID_ACCEL5, ALT ; ALT_SHIFT+B
"I", ID_ACCEL6, CONTROL, VIRTKEY ; CTRL+I
VK_F5, ID_ACCEL7, CONTROL, ALT, VIRTKEY ; CTRL+ALT+F5

By default, when an accelerator key corresponds to a menu item, Windows highlights the menu item. You can use the NOINVERT flag to prevent highlighting for an individual accelerator. The following example shows how to use the NOINVERT flag.
VK_DELETE, ID_ACCEL8, VIRTKEY, SHIFT, NOINVERT ; SHIFT+DELETE

To define accelerators that correspond to menu items in your application, include the accelerators in the text of the menu items. The following example shows how to include accelerators in menu-item text in a resource-definition file.
FilePopup MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New..", IDM_NEW
MENUITEM "&Open\tCtrl+F12", IDM_OPEN
MENUITEM "&Close\tAlt+F4" IDM_CLOSE
MENUITEM "&Save\tShift+F12", IDM_SAVE
MENUITEM "Save &As...\tF12", IDM_SAVEAS
END
END

+$#A Loading the Accelerator-Table Resource
An application loads an accelerator-table resource by calling the LoadAccelerators_win32_LoadAccelerators function and specifying the instance handle of the application whose executable file contains the resource and the name or identifier of the resource. LoadAccelerators loads the specified accelerator table into memory and returns the handle of the accelerator table.
An application can load an accelerator-table resource at any time. Usually, a single-threaded application loads its accelerator table before entering its main message loop. An application that uses multiple threads typically loads the accelerator-table resource for a thread before entering the message loop for the thread. An application or thread might also use multiple accelerator tables, each associated with a particular window in the application. Such an application would load the accelerator table for the window each time the user activated the window. For more information about threads, see Processes and Threads_win32_Processes_and_Threads.
+$#A Calling the TranslateAccelerator Function
To process accelerators, an application's (or thread's) message loop must contain a call to the TranslateAccelerator_win32_TranslateAccelerator function. TranslateAccelerator compares keystrokes to an accelerator table and, if it finds a match, translates the keystrokes into a WM_COMMAND_win32_WM_COMMAND (or WM_SYSCOMMAND_win32_WM_SYSCOMMAND) message. The function then sends the message to a window procedure. The parameters of the TranslateAccelerator function include the handle of the window that is to receive the WM_COMMAND messages, the handle of the accelerator table used to translate accelerators, and a pointer to an MSG_win32_MSG_str structure containing a message from the queue. The following example shows how to call TranslateAccelerator from within a message loop.
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(
hwndMain, /* handle of receiving window */
haccel, /* handle of active accel. table */
&msg)) { /* address of message data */
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

+$#A Processing WM_COMMAND Messages
When an accelerator is used, the window specified in the TranslateAccelerator_win32_TranslateAccelerator function receives a WM_COMMAND_win32_WM_COMMAND or WM_SYSCOMMAND_win32_WM_SYSCOMMAND message. The low-order word of the wParam parameter contains the identifier of the accelerator. The window procedure examines the identifier to determine the source of the WM_COMMAND message and process the message accordingly.
Typically, if an accelerator corresponds to a menu item in the application, the accelerator and menu item are assigned the same identifier. If you need to know whether a given WM_COMMAND message was generated by an accelerator or by a menu item, you can examine the high-order word of the wParam parameter. If an accelerator generated the message, the high-order word is 1; if a menu item generated the message, the high-order word is 0.
+$#A Destroying the Accelerator-Table Resource
Windows automatically destroys accelerator-table resources loaded by the LoadAccelerators_win32_LoadAccelerators function, removing the resource from memory after the application closes.
+$#A Creating Accelerators for Font Attributes
The example in this section shows how to perform the following tasks:
· Create an accelerator-table resource.
· Load the accelerator table at run time.
· Translate accelerators in a message loop.
· Process WM_COMMAND_win32_WM_COMMAND messages generated by the accelerators.

These tasks are demonstrated in the context of an application that includes a Character menu and corresponding accelerators that allow the user to select attributes of the current font.
The following portion of a resource-definition file defines the Character menu and the associated accelerator table. Note that the menu items show the accelerator keystrokes and that each accelerator has the same identifier as its associated menu item.
#include <windows.h>
#include "acc.h"

MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "&Regular\tF5", IDM_REGULAR
MENUITEM "&Bold\tCtrl+B", IDM_BOLD
MENUITEM "&Italic\tCtrl+I", IDM_ITALIC
MENUITEM "&Underline\tCtrl+U", IDM_ULINE
END
END

FontAccel ACCELERATORS
BEGIN
VK_F5, IDM_REGULAR, VIRTKEY
"B", IDM_BOLD, CONTROL, VIRTKEY
"I", IDM_ITALIC, CONTROL, VIRTKEY
"U", IDM_ULINE, CONTROL, VIRTKEY
END

The following sections from the application's source file show how to implement the accelerators.
HWND hwndMain; /* handle of main window */
HANDLE hinstAcc; /* handle of application instance */

int WINAPI WinMain(hinst, hinstPrev, lpCmdLine, nCmdShow)
HINSTANCE hinst;
HINSTANCE hinstPrev;
LPSTR lpCmdLine;
int nCmdShow;
{
MSG msg; /* application messages */
HACCEL haccel; /* handle of accelerator table */

.
. /* Perform the initialization procedure. */
.

/* Create a main window for this application instance. */

hwndMain = CreateWindowEx(0L, "MainWindowClass",
"Sample Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hinst, NULL );

/* If a window cannot be created, return "failure." */

if (!hwndMain)
return FALSE;

/* Make the window visible and update its client area. */

ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);

/* Load the accelerator table. */

haccel = LoadAccelerators(hinstAcc, "FontAccel");
if (haccel == NULL)
HandleAccelErr(ERR_LOADING); /* application defined */

/*
* Get and dispatch messages until a WM_QUIT message is
* received.
*/

while (GetMessage(&msg, NULL, NULL, NULL)) {

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(
hwndMain, /* handle of receiving window */
haccel, /* handle of active accel. table */
&msg)) { /* address of message data */
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
BYTE fbFontAttrib; /* array of font-attribute flags */
static HMENU hmenu; /* handle of main menu */

switch (uMsg) {
case WM_CREATE:

/*
* Add a check mark to the Regular menu item to
* indicate that it is the default.
*/

hmenu = GetMenu(hwndMain);
CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED);
return 0;

case WM_COMMAND:
switch (LOWORD(wParam)) {

/* Process the accelerator and menu commands. */

case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:

/*
* GetFontAttributes is an application-defined
* function that sets the menu-item check marks
* and returns the user-selected font attributes.
*/

fbFontAttrib = GetFontAttributes(
(BYTE) LOWORD(wParam), hmenu);

/*
* SetFontAttributes is an application-defined
* function that creates a font with the
* user-specified attributes the font with
* the main window's device context.
*/

SetFontAttributes(fbFontAttrib);
break;

default:
break;
}
break;

.
. /* Process other messages. */
.

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

+$#A Using an Accelerator Table Created at Run Time
The Win32 application programming interface (API) allows you to create accelerator tables at run time. The steps involved in creating and using an accelerator table at run time are as follows:
· Define the accelerators by filling an array of ACCEL_win32_ACCEL_str structures, and then create an accelerator table by passing the array to the CreateAcceleratorTable_win32_CreateAcceleratorTable function.
· Activate the accelerator table and process WM_COMMAND_win32_WM_COMMAND messages generated by the accelerators.
· Destroy the accelerator table before the application closes.

+$#A Creating the Accelerator Table
The first step in creating an accelerator table at run time is filling an array of ACCEL_win32_ACCEL_str structures. Each structure in the array defines an accelerator in the table. An accelerator's definition includes its flags, its key, and its identifier. The ACCEL structure has the following form.
typedef struct tagACCEL { // accl
BYTE fVirt;
WORD key;
WORD cmd;
} ACCEL;

You define an accelerator's keystroke by specifying an ASCII character code or a virtual-key code in the key member of the ACCEL_win32_ACCEL_str structure. If you specify a virtual-key code, you must first include the FVIRTKEY flag in the fVirt member; otherwise, Windows interprets the code as an ASCII character code. You can include the FCONTROL, FALT, or FSHIFT flag, or all three, to combine the CTRL, ALT, or SHIFT key with the keystroke.
To create the accelerator table, pass the address of the array of ACCEL structures to the CreateAcceleratorTable_win32_CreateAcceleratorTable function. CreateAcceleratorTable creates the accelerator table and returns the handle of the table.
+$#A Processing Accelerators
The process of loading and calling accelerators provided by an accelerator table created at run time is the same as processing those provided by an accelerator-table resource. For more information, see Loading the Accelerator-Table Resource_win32_Loading_the_Accelerator_Table_Resource through Processing WM_COMMAND Messages_win32_Processing_WM_COMMAND_Messages.
+$#A Destroying the Accelerator Table
Before an application closes, it must destroy accelerator tables created at run time. You can destroy an accelerator table and remove it from memory by passing the table's handle to the DestroyAcceleratorTable_win32_DestroyAcceleratorTable function.
+$#A Creating User-Editable Accelerators
This example shows how to construct a dialog box that allows the user to change the accelerator associated with a menu item. The dialog box consists of a combo box containing menu items, a combo box containing the names of keys, and check boxes for selecting the CTRL, ALT, and SHIFT keys. The following illustration shows the dialog box.
{ewc msdncd, EWGraphic, group10803 1 /a "SDK_02.bmp"}
The following example shows how the dialog box is defined in the resource-definition file.
EdAccelBox DIALOG 5, 17, 193, 114
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Edit Accelerators"
BEGIN
COMBOBOX IDD_MENUITEMS, 10, 22, 52, 53,
CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
WS_TABSTOP
CONTROL "Control", IDD_CNTRL, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 35, 40, 10
CONTROL "Alt", IDD_ALT, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 48, 40, 10
CONTROL "Shift", IDD_SHIFT, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 61, 40, 10
COMBOBOX IDD_KEYSTROKES, 124, 22, 58, 58,
CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
WS_TABSTOP
PUSHBUTTON "Ok", IDOK, 43, 92, 40, 14
PUSHBUTTON "Cancel", IDCANCEL, 103, 92, 40, 14
LTEXT "Select Item:", 101, 10, 12, 43, 8
LTEXT "Select Keystroke:", 102, 123, 12, 60, 8
END

The dialog box uses an array of application-defined VKEY structures, each containing a keystroke-text string and an accelerator-text string. When the dialog box is created, it parses the array and adds each keystroke-text string to the Select Keystroke combo box. When the user clicks the Ok button, the dialog box looks up the selected keystroke-text string and retrieves the corresponding accelerator-text string. The dialog box appends the accelerator-text string to the text of the menu item that the user selected. The following example shows the array of VKEY structures:
/* VKey Lookup Support */

#define MAXKEYS 26

typedef struct _VKEYS {
char *pKeyName;
char *pKeyString;
} VKEYS;

VKEYS vkeys[MAXKEYS] = {
"BkSp", "Back Space",
"PgUp", "Page Up",
"PgDn", "Page Down",
"End", "End",
"Home", "Home",
"Lft", "Left",
"Up", "Up",
"Rgt", "Right",
"Dn", "Down",
"Ins", "Insert",
"Del", "Delete",
"Mult", "Multiply",
"Add", "Add",
"Sub", "Subtract",
"DecPt", "Decimal Point",
"Div", "Divide",
"F2", "F2",
"F3", "F3",
"F5", "F5",
"F6", "F6",
"F7", "F7",
"F8", "F8",
"F9", "F9",
"F11", "F11",
"F12", "F12"
};

The dialog box's initialization procedure fills the Select Item and Select Keystroke combo boxes. After the user selects a menu item and associated accelerator, the dialog box examines the controls in the dialog box to get the user's selection, updates the text of the menu item, and then creates a new accelerator table that contains the user-defined new accelerator. The following example shows the dialog-box procedure.
/* Global variables */

HWND hwndMain; /* handle of main window */
HANDLE hinstAcc; /* handle of application instance */
HACCEL haccel; /* handle of accelerator table */

.
.
.

/* Dialog-box procedure */

LRESULT CALLBACK EdAccelProc(hwndDlg, uMsg, wParam, lParam)
HWND hwndDlg;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
int nCurSel; /* index of list box item */
UINT idItem; /* menu-item identifier */
UINT uItemPos; /* menu-item position */
UINT i, j = 0; /* loop counters */
static UINT cItems; /* count of items in menu */
char szTemp[32]; /* temporary buffer */
char szAccelText[32]; /* buffer for accelerator text */
char szKeyStroke[16]; /* buffer for keystroke text */
static char szItem[32]; /* buffer for menu-item text */
HWND hwndCtl; /* handle of control window */
static HMENU hmenu; /* handle of "Character" menu */
PCHAR pch, pch2; /* pointers for string copying */
WORD wVKCode; /* accelerator virtual-key code */
BYTE fAccelFlags; /* fVirt flags for ACCEL structure */
LPACCEL lpaccelNew; /* address of new accel. table */
HACCEL haccelOld; /* handle of old accel. table */
int cAccelerators; /* number of accelerators in table */
static BOOL fItemSelected = FALSE; /* item selection flag */
static BOOL fKeySelected = FALSE; /* key selection flag */

switch (uMsg) {
case WM_INITDIALOG:

/* Get the handle of the menu-item combo box. */

hwndCtl = GetDlgItem(hwndDlg, IDD_MENUITEMS);

/*
* The application's menu bar contains a "Character"
* submenu whose items have accelerators associated
* with them. Get the handle of the "Character"
* submenu (its position within the main menu is 2),
* and count the number of items it has.
*/

hmenu = GetSubMenu(GetMenu(hwndMain), 2);
cItems = GetMenuItemCount(hmenu);

/*
* Get the text of each item, strip out the '&' and
* the accelerator text, and add the text to the
* menu-item combo box.
*/

for (i = 0; i < cItems; i++) {
if (!(GetMenuString(hmenu, i, szTemp,
sizeof(szTemp), MF_BYPOSITION)))
continue;
for (pch = szTemp, pch2 = szItem;
*pch != '\0'; ) {

if (*pch != '&') {
if (*pch == '\t') {
*pch = '\0';
*pch2 = '\0';
}
else
*pch2++ = *pch++;
}
else
pch++;
}
SendMessage(hwndCtl, CB_ADDSTRING, 0,
(LONG) (LPSTR) szItem);
}

/*
* Now fill the keystroke combo box with the list of
* keystrokes that will be allowed for accelerators.
* The list of keystrokes is in the application-defined
* structure called "vkeys".
*/

hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
for (i = 0; i < MAXKEYS; i++)
SendMessage(hwndCtl, CB_ADDSTRING, 0,
(LONG) (LPSTR) vkeys[i].pKeyString);

return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDD_MENUITEMS:

/*
* The user must select an item from the menu-
* item combo box. This flag is checked during
* IDOK processing to be sure a selection was made.
*/

fItemSelected = TRUE;
return 0;

case IDD_KEYSTROKES:

/*
* The user must select an item from the menu-
* item combo box. This flag is checked during
* IDOK processing to be sure a selection was made.
*/

fKeySelected = TRUE;

return 0;

case IDOK:

/*
* If the user has not selected a menu item
* and a keystroke, display a reminder in a
* message box.
*/

if (!fItemSelected || !fKeySelected) {
MessageBox(hwndDlg,
"Item or key not selected.", NULL,
MB_OK);
return 0;
}

/*
* Determine whether the CTRL, ALT, and SHIFT
* keys are selected. Concatenate the
* appropriate strings to the accelerator-
* text buffer, and set the appropriate
* accelerator flags.
*/

szAccelText[0] = '\0';
hwndCtl = GetDlgItem(hwndDlg, IDD_CNTRL);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)
== 1) {
lstrcat(szAccelText, "Ctl+");
fAccelFlags |= FCONTROL;
}
hwndCtl = GetDlgItem(hwndDlg, IDD_ALT);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)
== 1) {
lstrcat(szAccelText, "Alt+");
fAccelFlags |= FALT;
}
hwndCtl = GetDlgItem(hwndDlg, IDD_SHIFT);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)
== 1) {
lstrcat(szAccelText, "Shft+");
fAccelFlags |= FSHIFT;
}

/*
* Get the selected keystroke, and look up the
* accelerator text and the virtual-key code
* for the keystroke in the vkeys structure.
*/

hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
nCurSel = (int) SendMessage(hwndCtl,
CB_GETCURSEL, 0, 0);
SendMessage(hwndCtl, CB_GETLBTEXT,
nCurSel, (LONG) (LPSTR) szKeyStroke);
for (i = 0; i < MAXKEYS; i++) {
if(lstrcmp(vkeys[i].pKeyString,
szKeyStroke) == 0) {
lstrcpy(szKeyStroke,
vkeys[i].pKeyName);
break;
}
}

/*
* Concatenate the keystroke text to the
* "Ctl+","Alt+", or "Shft+" string.
*/

lstrcat(szAccelText, szKeyStroke);

/*
* Determine the position in the menu of the
* selected menu item. Menu items in the
* "Character" menu have positions 0,2,3, and 4.
*/

if (lstrcmp(szItem, "Regular") == 0)
uItemPos = 0;
else if (lstrcmp(szItem, "Bold") == 0)
uItemPos = 2;
else if (lstrcmp(szItem, "Italic") == 0)
uItemPos = 3;
else if (lstrcmp(szItem, "Underline") == 0)
uItemPos = 4;

/*
* Get the string that corresponds to the
* selected item.
*/

GetMenuString(hmenu, uItemPos, szItem,
sizeof(szItem), MF_BYPOSITION);

/*
* Append the new accelerator text to the
* menu-item text.
*/

for (pch = szItem; *pch != '\t'; pch++);
++pch;

for (pch2 = szAccelText; *pch2 != '\0';
pch2++)
*pch++ = *pch2;
*pch = '\0';

/*
* Modify the menu item to reflect the new
* accelerator text.
*/

idItem = GetMenuItemID(hmenu, uItemPos);
ModifyMenu(hmenu, idItem, MF_BYCOMMAND |
MF_STRING, idItem, szItem);

/* Reset the selection flags. */

fItemSelected = FALSE;
fKeySelected = FALSE;

/* Save the current accelerator table. */

haccelOld = haccel;

/*
* Count the number of entries in the current
* table, allocate a buffer for the table, and
* then copy the table into the buffer.
*/

cAccelerators = CopyAcceleratorTable(
haccelOld, NULL, 0);
lpaccelNew = (LPACCEL) LocalAlloc(LPTR,
cAccelerators * sizeof(ACCEL));

if (lpaccelNew != NULL)
CopyAcceleratorTable(haccel, lpaccelNew,
cAccelerators);

/*
* Find the accelerator that the user modified
* and change its flags and virtual-key code
* as appropriate.
*/

for (i = 0; (lpaccelNew[i].cmd ==
(WORD) idItem)
&& (i < (UINT) cAccelerators); i++) {
lpaccelNew[i].fVirt = fAccelFlags;
lpaccelNew[i].key = wVKCode;
}

/*
* Create the new accelerator table, and
* destroy the old one.
*/

DestroyAcceleratorTable(haccelOld);
haccel = CreateAcceleratorTable(lpaccelNew,
cAccelerators);

/* Destroy the dialog box. */

EndDialog(hwndDlg, TRUE);
return 0;

case IDCANCEL:
EndDialog(hwndDlg, TRUE);
return TRUE;

default:
break;
}
default:
break;
}
return FALSE;
}

+$#AK Keyboard Accelerator Reference
The following functions and messages are associated with keyboard accelerators.
+$#A Keyboard Accelerator Functions
Following are the functions used with accelerators.
CopyAcceleratorTable_win32_CopyAcceleratorTableCreateAcceleratorTable_win32_CreateAcceleratorTableDestroyAcceleratorTable_win32_DestroyAcceleratorTableLoadAccelerators_win32_LoadAcceleratorsTranslateAccelerator_win32_TranslateAccelerator
+$#A Keyboard Accelerator Messages
Following are the messages used with accelerators.
WM_COMMAND_win32_WM_COMMANDWM_INITMENU_win32_WM_INITMENUWM_INITMENUPOPUP_win32_WM_INITMENUPOPUPWM_MENUCHAR_win32_WM_MENUCHARWM_MENUSELECT_win32_WM_MENUSELECTWM_SYSCHAR_win32_WM_SYSCHARWM_SYSCOMMAND_win32_WM_SYSCOMMAND

+$#AK Keyboard Input
This overview describes how Windows generates keyboard input and how an application receives and processes that input.
+$#A About Keyboard Input
All applications written for Microsoft® Windows® should accept user input from the keyboard as well as from the mouse. A Windows-based application receives keyboard input in the form of messages posted to its windows.
+$#A Keyboard Input Model
Windows provides device-independent keyboard support for applications by installing a keyboard device driver appropriate for the current keyboard. Windows provides language-independent keyboard support by using the language-specific keyboard layout currently selected by the user or the application. The keyboard device driver receives scan codes from the keyboard, which are sent to the keyboard layout where they are translated into messages and posted to the appropriate windows in your application.
Assigned to each key on a keyboard is a unique value called a scan code, a device-dependent identifier for the key on the keyboard. A keyboard generates two scan codes when the user types a key ¾ one when the user presses the key and another when the user releases the key.
The keyboard device driver interprets a scan code and translates (maps) it to a virtual-key code, a device-independent value defined by Windows that identifies the purpose of a key. After translating a scan code, the keyboard layout creates a message that includes the scan code, the virtual-key code, and other information about the keystroke, and then places the message in the system message queue. Windows removes the message from the system message queue and posts it to the message queue of the appropriate thread. Eventually, the thread's message loop removes the message and passes it to the appropriate window procedure for processing. The following figure illustrates the keyboard input model for Windows.
{ewc msdncd, EWGraphic, group10804 0 /a "SDK_01.bmp"}
+$#A Keyboard Focus and Activation
Windows posts keyboard messages to the message queue of the foreground thread that created the window with the keyboard focus. The keyboard focus is a temporary property of a window. Windows shares the keyboard among all windows on the display by shifting the keyboard focus, at the user's direction, from one window to another. The window that has the keyboard focus receives (from the message queue of the thread that created it) all keyboard messages until the focus changes to a different window.
A thread can call the GetFocus_win32_GetFocus function to determine which of its windows (if any) currently has the keyboard focus. A thread can give the keyboard focus to one of its windows by calling the SetFocus_win32_SetFocus function. When the keyboard focus changes from one window to another, the system sends a WM_KILLFOCUS_win32_WM_KILLFOCUS message to the window that has lost the focus, and then sends a WM_SETFOCUS_win32_WM_SETFOCUS message to the window that has gained the focus.
The concept of keyboard focus is related to that of the active window. The active window is the top-level window the user is currently working with. The window with the keyboard focus is either the active window, or a child window of the active window. So the user can easily identify the active window, the system places it at the top of the Z order and makes its title bar (if it has one) and border highlighted.
The user can activate a top-level window by clicking it, selecting it using the ALT+TAB or ALT+ESC key combination, or selecting if from Task List. A thread can activate a top-level window by using the SetActiveWindow_win32_SetActiveWindow function. It can determine whether a top-level window it created is active by using the GetActiveWindow_win32_GetActiveWindow function.
When one window is deactivated and another activated, Windows sends the WM_ACTIVATE_win32_WM_ACTIVATE message first to the window being deactivated, then to the window being activated. The low-order word of the wParam parameter is zero if the window is being deactivated and nonzero if it is being activated. When the default window procedure receives the WM_ACTIVATE message, it sets the keyboard focus to the active window.
+$#A Keystroke Messages
Pressing a key results in a WM_KEYDOWN_win32_WM_KEYDOWN or WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN message being placed in the thread message queue associated with the window that has the keyboard focus. Releasing a key results in a WM_KEYUP_win32_WM_KEYUP or WM_SYSKEYUP_win32_WM_SYSKEYUP message being placed in the queue.
Key-up and key-down messages typically occur in pairs, but if the user holds down a key long enough to start the keyboard's automatic repeat feature, the system generates a number of WM_KEYDOWN or WM_SYSKEYDOWN messages in a row. It then generates a single WM_KEYUP or WM_SYSKEYUP message when the user releases the key.
+$#A System and Nonsystem Keystrokes
Windows makes a distinction between system keystrokes and nonsystem keystrokes. System keystrokes produce system keystroke messages, WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN and WM_SYSKEYUP_win32_WM_SYSKEYUP. Nonsystem keystrokes produce nonsystem keystroke messages, WM_KEYDOWN_win32_WM_KEYDOWN and WM_KEYUP_win32_WM_KEYUP.
If your window procedure must process a system keystroke message, make sure that after processing the message the procedure passes it to the DefWindowProc_win32_DefWindowProc function. Otherwise, all system operations involving the ALT key will be disabled whenever the window has the keyboard focus. That is, the user won't be able to access the window's menus or System menu, or use the ALT+ESC or ALT+TAB key combination to activate a different window.
System keystroke messages are primarily for use by Windows rather than by an application. Windows uses them to provide its built-in keyboard interface to menus and to allow the user to control which window is active. System keystroke messages are generated when the user types a key in combination with the ALT key, or when the user types and no window has the keyboard focus (for example, when the active application is minimized). In this case, the messages are posted to the message queue associated with the active window.
Nonsystem keystroke messages are for use by application windows; the DefWindowProc function does nothing with them. A window procedure can discard any nonsystem keystroke messages that it does not need.
+$#A Virtual-Key Codes Described
The wParam parameter of a keystroke message contains the virtual-key code of the key that was pressed or released. A window procedure processes or ignores a keystroke message, depending on the value of the virtual-key code.
A typical window procedure processes only a small subset of the keystroke messages that it receives and ignores the rest. For example, a window procedure might process only WM_KEYDOWN_win32_WM_KEYDOWN keystroke messages, and only those that contain virtual-key codes for the cursor movement keys, shift keys (also called control keys), and function keys. A typical window procedure does not process keystroke messages from character keys. Instead, it uses the TranslateMessage_win32_TranslateMessage function to convert the message into character messages. For more information about TranslateMessage and character messages, see Character Messages_win32_Character_Messages.
+$#A Keystroke Message Flags
The lParam parameter of a keystroke message contains additional information about the keystroke that generated the message. This information includes the repeat count, the scan code, the extended-key flag, the context code, the previous key-state flag, and the transition-state flag. The following illustration shows the locations of these flags and values in the lParam parameter:
{ewc msdncd, EWGraphic, group10804 1 /a "SDK_02.bmp"}
An application can use the following values to manipulate the keystroke flags:
Value Meaning
KF_ALTDOWN Manipulates the ALT key flag, which indicated if the ALT key is pressed.
KF_DLGMODE Manipulates the dialog mode flag, which indicates whether a dialog box is active.
KF_EXTENDED Manipulates the extended key flag.
KF_MENUMODE Manipulates the menu mode flag, which indicates whether a menu is active.
KF_REPEAT Manipulates the repeat count.
KF_UP Manipulates the transition state flag.

Repeat Count
You can check the repeat count to determine whether a keystroke message represents more than one keystroke. The system increments the count when the keyboard generates WM_KEYDOWN_win32_WM_KEYDOWN or WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN messages faster than an application can process them. This often occurs when the user holds down a key long enough to start the keyboard's automatic repeat feature. Instead of filling the system message queue with the resulting key-down messages, the system combines the messages into a single key down message and increments the repeat count. Releasing a key cannot start the automatic repeat feature, so the repeat count for WM_KEYUP_win32_WM_KEYUP and WM_SYSKEYUP_win32_WM_SYSKEYUP messages is always set to 1.
Scan Code
The scan code is the value that the keyboard hardware generates when the user presses a key. It is a device-dependent value that identifies the key pressed, as opposed to the character represented by the key. An application typically ignores scan codes. Instead, it uses the device-independent virtual-key codes to interpret keystroke messages.
Extended-Key Flag
The extended-key flag indicates whether the keystroke message originated from one of the additional keys on the enhanced keyboard. The extended keys consist of the ALT and CTRL keys on the right-hand side of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE DOWN and arrow keys in the clusters to the left of the numeric keypad; the NUM LOCK key; the BREAK (CTRL+PAUSE) key; the PRINT SCRN key; and the divide (/) and ENTER keys in the numeric keypad. The extended-key flag is set if the key is an extended key.
Context Code
The context code indicates whether the ALT key was down when the keystroke message was generated. The code is 1 if the ALT key was down and 0 if it was up.
Previous Key-State Flag
The previous key-state flag indicates whether the key that generated the keystroke message was previously up or down. It is 1 if the key was previously down and 0 if the key was previously up. You can use this flag to identify keystroke messages generated by the keyboard's automatic repeat feature. This flag is set to 1 for WM_KEYDOWN_win32_WM_KEYDOWN and WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN keystroke messages generated by the automatic repeat feature. It is always set to 0 for WM_KEYUP_win32_WM_KEYUP and WM_SYSKEYUP_win32_WM_SYSKEYUP messages.
Transition-State Flag
The transition-state flag indicates whether pressing a key or releasing a key generated the keystroke message. This flag is always set to 0 for WM_KEYDOWN_win32_WM_KEYDOWN and WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN messages; it is always set to 1 for WM_KEYUP_win32_WM_KEYUP and WM_SYSKEYUP_win32_WM_SYSKEYUP messages.
+$#A Character Messages
Keystroke messages provide a lot of information about keystrokes, but they don't provide character codes for character keystrokes. To retrieve character codes, an application must include the TranslateMessage_win32_TranslateMessage function in its thread message loop. TranslateMessage passes a WM_KEYDOWN_win32_WM_KEYDOWN or WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN message to the keyboard layout. The layout examines the message's virtual-key code and, if it corresponds to a character key, provides the character code equivalent (taking into account the state of the SHIFT and CAPS LOCK keys). It then generates a character message that includes the character code and places the message at the top of the message queue. The next iteration of the message loop removes the character message from the queue and dispatches the message to the appropriate window procedure.
+$#A Nonsystem Character Messages
A window procedure can receive four different character messages, including WM_CHAR_win32_WM_CHAR, WM_DEADCHAR_win32_WM_DEADCHAR, WM_SYSCHAR_win32_WM_SYSCHAR, and WM_SYSDEADCHAR_win32_WM_SYSDEADCHAR. The TranslateMessage_win32_TranslateMessage function generates a WM_CHAR or WM_DEADCHAR message when it processes a WM_KEYDOWN_win32_WM_KEYDOWN message. Similarly, it generates a WM_SYSCHAR or WM_SYSDEADCHAR message when it processes a WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN message.
An application that processes keyboard input typically ignores all but the WM_CHAR message, passing any other messages to the DefWindowProc_win32_DefWindowProc function. Windows uses the WM_SYSCHAR and WM_SYSDEADCHAR messages to implement menu mnemonics.
The wParam parameter of all character messages contains the character code of the character key that was pressed. The value of the character code depends on the window class of the window receiving the message. If the Unicode™ version of the RegisterClass_win32_RegisterClass function was used to register the window class, the system provides Unicode characters to all windows of that class. Otherwise, the system provides ASCII character codes from the Windows character set. For more information about Unicode, see Unicode and Character Sets_win32_Unicode_and_Character_Sets.
The contents of the lParam parameter of a character message are identical to the contents of the lParam parameter of the key-down message that was translated to produce the character message. For information about the contents of the lParam parameter, see Keystroke Message Flags_win32_Keystroke_Message_Flags.
+$#A Dead-Character Messages
Some non-English keyboards contain character keys that are not expected to produce characters by themselves. Instead, they are used to add a diacritic to the character produced by the subsequent keystroke. These keys are called dead keys. The circumflex key on a German keyboard is an example of a dead key. To enter the character consisting of an "o" with a circumflex, a German user would type the circumflex key followed by the "o" key. The window with the keyboard focus would receive the following sequence of messages:
WM_KEYDOWNWM_DEADCHARWM_KEYUPWM_KEYDOWNWM_CHARWM_KEYUP
TranslateMessage_win32_TranslateMessage generates the WM_DEADCHAR_win32_WM_DEADCHAR message when it processes the WM_KEYDOWN_win32_WM_KEYDOWN message from a dead key. Although the wParam parameter of the WM_DEADCHAR message contains the character code of the diacritic for the dead key, an application typically ignores the message. Instead, it processes the WM_CHAR_win32_WM_CHAR message generated by the subsequent keystroke. The wParam parameter of the WM_CHAR message contains the character code of the letter with the diacritic. If the subsequent keystroke generates a character that cannot be combined with a diacritic, Windows generates two WM_CHAR messages. The wParam parameter of the first contains the character code of the diacritic; the wParam parameter of the second contains the character code of the subsequent character key.
The TranslateMessage function generates the WM_SYSDEADCHAR_win32_WM_SYSDEADCHAR message when it processes the WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN message from a system dead key (a dead key that is pressed in combination with the ALT key). An application typically ignores the WM_SYSDEADCHAR message.
+$#A Key Status
While processing a keyboard message, an application may need to determine the status of another key besides the one that generated the current message. For example, a word-processing application that allows the user to press SHIFT+END to select a block of text must check the status of the SHIFT key whenever it receives a keystroke message from the END key. The application can use the GetKeyState_win32_GetKeyState function to determine the status of a virtual key at the time the current message was generated; it can use the GetAsyncKeyState_win32_GetAsyncKeyState function to retrieve the current status of a virtual key.
The keyboard layout maintains a list of names. The name of a key that produces a single character is the same as the character produced by the key. The name of a noncharacter key such as TAB and ENTER is stored as a character string. An application can retrieve the name of any key from the device driver by calling the GetKeyNameText_win32_GetKeyNameText function.
+$#A Keystroke and Character Translations
Windows includes several special purpose functions that translate scan codes, character codes, and virtual-key codes provided by various keystroke messages. These functions include MapVirtualKey_win32_MapVirtualKey, ToAscii_win32_ToAscii, ToUnicode_win32_ToUnicode, and VkKeyScan_win32_VkKeyScan.
+$#A Hot-Key Support
Windows provides a set of functions that applications can use to define hot keys. A hot key is a key combination that generates a WM_HOTKEY_win32_WM_HOTKEY message, a message the system places at the top of a thread's message queue, bypassing any existing messages in the queue. Applications use hot keys to obtain high-priority keyboard input from the user. For example, by defining a hot key consisting of the CTRL+C key combination, an application can allow the user to cancel a lengthy operation.
To define a hot key, an application calls the RegisterHotKey_win32_RegisterHotKey function, specifying the combination of keys that generates the WM_HOTKEY message, the handle of the window to receive the message, and the identifier of the hot key. When the user presses the hot key, a WM_HOTKEY message is placed in the message queue of the thread that created the given window. The wParam parameter of the message contains the identifier of the hot key. The application can define multiple hot keys for a thread, but each hot key in the thread must have a unique identifier. Before the application terminates, it should use the UnregisterHotKey_win32_UnregisterHotKey function to destroy the hot key.
Applications can use a hot key control to make it easy for the user to choose a hot key. Hot key controls are typically used to define a hot key that activates a window; they do not use the RegisterHotKey and UnregisterHotKey functions. Instead, an application that uses a hot key control typically sends the WM_SETHOTKEY_win32_WM_SETHOTKEY message to set the hot key. Whenever the user presses the hot key, the system sends a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message specifying SC_HOTKEY. For more information about hot key controls, see Hot-Key Controls_win32_Hot_Key_Controls.
+$#A Languages, Locales, and Keyboard Layouts
A language is a natural language, such as English, French, and Japanese. A sublanguange is a variant of a natural language that is spoken in a specific geographical region, such as the English sublanguages spoken in Great Britian and the United States. Win32-based applications use values, called locales, to uniquely identify languages and sublanguages. Applications typically use locales to set the language in which input and output is processed. Setting the locale for the keyboard, for example, affects the character values generated by the keyboard. Setting the locale for the display or printer affects the glyphs displayed or printed. Applications set the locale for a keyboard by loading and using keyboard layouts. They set the locale for a display or printer by selecting a font that supports the given locale.
A keyboard layout not only specifies the physical position of the keys on the keyboard but also determines the character values generated by pressing those keys. Each layout has an associated locale which identifies the current input language and determines which character values are generated by which keys and key combinations.
Every keyboard layout has a corresponding handle which identifies of the layout and language. The low word of the handle is a language identifier. The high word is a device handle, specifying the physical layout, or is zero, indicating a default physical layout. The user can associate any input language with a given physical layout. For example, an English-speaking user who very occasionally works in French can set the input language of the keyboard to French without changing the physical layout of the keyboard. This means the user can enter text in French using the familiar English layout.
Applications are generally not expected to manipulate input languages directly. Instead, the user sets up language and layout combinations, then switches among them. When the user clicks into text marked with a different language, the application calls the ActivateKeyboardLayout_win32_ActivateKeyboardLayout function to activate the user's default layout for that language. If the user edits text in a language which is not in the active list, the application can call the LoadKeyboardLayout_win32_LoadKeyboardLayout function with the language to get a layout based on that language.
The ActivateKeyboardLayout function sets the input language for the current task. The hkl parameter can be either the handle of the keyboard layout or a zero-extended language identifier. Keyboard layout handles can be obtained from the LoadKeyboardLayout or GetKeyboardLayoutList_win32_GetKeyboardLayoutList function. The HKL_NEXT and HKL_PREV values can also be used to select the next or previous keyboard. For some operating systems, the KLF_UNLOADPREVIOUS value has no meaning and is ignored.
The GetKeyboardLayoutName_win32_GetKeyboardLayoutName function retrieves the name of the active keyboard layout for the calling thread. If an application creates the active layout using the LoadKeyboardLayout function, GetKeyboardLayoutName retrieves the same string used to create the layout. Otherwise, the string is the primary language identifier correspoding to the locale of the active layout. This means the function may not necessarily differentiate among different layouts with the same primary language, so cannot return specific information about the input language. The GetKeyboardLayout_win32_GetKeyboardLayout function, however, can be used to determine the input language.
The LoadKeyboardLayout function loads a keyboard layout and makes the layout available to the user. Applications can use the make the layout immediately active for the current thread by using the KLF_ACTIVATE value. An application can use the KLF_REORDER value to reorder the layouts without also specifying the KLF_ACTIVATE value. Applications should always use the KLF_SUBSTITUTE_OK value when loading keyboard layouts to ensure that the user's preference, if any, is selected. For some operating systems, the KLF_UNLOADPREVIOUS value is ignored.
For multilingual support, the LoadKeyboardLayout_win32_LoadKeyboardLayout function provides the KLF_REPLACELANG and KLF_NOTELLSHELL flags. The KLF_REPLACELANG flag directs the function to replace an existing keyboard layout without changing associated the language. Attempting to replace an existing layout using the same language identifier but without specifying KLF_REPLACELANG is an error. The KLF_NOTELLSHELL flag prevents the function from notifying the shell when a keyboard layout is added or replaced. This is useful for applications that add multiple layouts in a consecutive series of calls. This flag should be used in all but the last call.
The UnloadKeyboardLayout_win32_UnloadKeyboardLayout function is restricted in that it cannot unload the system default input language. This ensures that the user always has one layout available for enter text using the same character set as used by the shell and file system.
+$#A Using Keyboard Input
A window receives keyboard input in the form of keystroke messages and character messages. The message loop associated with the window must include code to translate keystroke messages into the corresponding character messages. If the window displays keyboard input in its client area, it should create and display a caret to indicate the position where the next character will be entered. The following sections describe the code involved in receiving, processing, and displaying keyboard input.
· Processing Keystroke Messages_win32_Processing_Keystroke_Messages
· Translating Character Messages_win32_Translating_Character_Messages
· Processing Character Messages_win32_Processing_Character_Messages
· Using the Caret_win32_Using_the_Caret
· Displaying Keyboard Input_win32_Displaying_Keyboard_Input

+$#A Processing Keystroke Messages
The window procedure of the window that has the keyboard focus receives keystroke messages when the user types at the keyboard. The keystroke messages are WM_KEYDOWN_win32_WM_KEYDOWN, WM_KEYUP_win32_WM_KEYUP, WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN, and WM_SYSKEYUP_win32_WM_SYSKEYUP. A typical window procedure ignores all keystroke messages except WM_KEYDOWN. Windows posts the WM_KEYDOWN message when the user presses a key.
When the window procedure receives the WM_KEYDOWN message, it should examine the virtual-key code that accompanies the message to determine how to process the keystroke. The virtual-key code is in the message's wParam parameter. Typically, an application processes only keystrokes generated by noncharacter keys, including the function keys, the cursor movement keys, and the special-purpose keys such as INS, DEL, HOME and END.
The following example shows the window procedure framework that a typical application uses to receive and process keystroke messages.
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
.
. /* Process the LEFT ARROW key. */
.

break;

case VK_RIGHT:
.
. /* Process the RIGHT ARROW key. */
.

break;

case VK_UP:
.
. /* Process the UP ARROW key. */
.

break;

case VK_DOWN:
.
. /* Process the DOWN ARROW key. */
.

break;

case VK_HOME:
.
. /* Process the HOME key. */
.

break;

case VK_END:
.
. /* Process the END key. */
.

break;

case VK_INSERT:
.
. /* Process the INS key. */
.

break;

case VK_DELETE:
.
. /* Process the DEL key. */
.

break;

case VK_F2:
.
. /* Process the F2 key. */
.

break;

.
. /* Process other noncharacter keystrokes. */
.

default:
break;
}

+$#A Translating Character Messages
Any thread that receives character input from the user must include the TranslateMessage_win32_TranslateMessage function in its message loop. This function examines the virtual-key code of a keystroke message and, if the code corresponds to a character, places a character message into the message queue. The character message is removed and dispatched on the next iteration of the message loop; the wParam parameter of the message contains the character code.
In general, a thread's message loop should use the TranslateMessage function to translate every message, not just virtual-key messages. Although TranslateMessage has no effect on other types of messages, it guarantees that keyboard input is translated correctly. The following example shows how to include the TranslateMessage function in a typical thread message loop.
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

+$#A Processing Character Messages
A window procedure receives a character message when the TranslateMessage_win32_TranslateMessage function translates a virtual-key code corresponding to a character key. The character messages are WM_CHAR_win32_WM_CHAR, WM_DEADCHAR_win32_WM_DEADCHAR, WM_SYSCHAR_win32_WM_SYSCHAR, and WM_SYSDEADCHAR_win32_WM_SYSDEADCHAR. A typical window procedure ignores all character messages except WM_CHAR. The TranslateMessage function generates a WM_CHAR message when the user presses any of the following keys.
· Any character key
· BACKSPACE
· ENTER (carriage return)
· ESC
· SHIFT+ENTER (linefeed)
· TAB

When a window procedure receives the WM_CHAR_win32_WM_CHAR message, it should examine the character code that accompanies the message to determine how to process the character. The character code is in the message's wParam parameter.
The following example shows the window procedure framework that a typical application uses to receive and process character messages.
case WM_CHAR:
switch (wParam) {
case 0x08:
.
. /* Process a backspace. */
.

break;

case 0x0A:
.
. /* Process a linefeed. */
.

break;

case 0x1B:
.
. /* Process an escape. */
.

break;

case 0x09:
.
. /* Process a tab. */
.

break;

case 0x0D:
.
. /* Process a carriage return. */
.

break;

default:
.
. /* Process displayable characters. */
.

break;
}

+$#A Using the Caret
A window that receives keyboard input typically displays the characters the user types in the window's client area. A window should use a caret to indicate the position in the client area where the next character will appear. The window should also create and display the caret when it receives the keyboard focus, and hide and destroy the caret when it loses the focus. A window can perform these operations in the processing of the WM_SETFOCUS_win32_WM_SETFOCUS and WM_KILLFOCUS_win32_WM_KILLFOCUS messages. For more information about carets, see Carets_win32_Carets.
+$#A Displaying Keyboard Input
The example in this section shows how an application can receive characters from the keyboard, display them in the client area of a window, and update the position of the caret with each character typed. It also demonstrates how to move the caret in response to the LEFT ARROW, RIGHT ARROW, HOME and END keystrokes, and shows how to highlight selected text in response to the SHIFT+RIGHT ARROW key combination.
During processing of the WM_CREATE_win32_WM_CREATE message, the window procedure shown in the example allocates a 64K buffer for storing keyboard input. It also retrieves the metrics of the currently loaded font, saving the height and average width of characters in the font. The height and width are used in processing the WM_SIZE_win32_WM_SIZE message to calculate the line length and maximum number of lines, based on the size of the client area.
The window procedure creates and displays the caret when processing the WM_SETFOCUS_win32_WM_SETFOCUS message. It hides and deletes the caret when processing the WM_KILLFOCUS_win32_WM_KILLFOCUS message.
When processing the WM_CHAR_win32_WM_CHAR message, the window procedure displays characters, stores them in the input buffer, and updates the caret position. The window procedure also converts tab characters to four consecutive space characters. Backspace, linefeed, and escape characters generate a beep, but are not otherwise processed.
The window procedure performs the left, right, end, and home caret movements when processing the WM_KEYDOWN_win32_WM_KEYDOWN message. While processing the action of the RIGHT ARROW key, the window procedure checks the state of the SHIFT key and, if it is down, selects the character to the right of the caret as the caret is moved.
Note that the following code is written so that it can be compiled either as Unicode™ or as ANSI. If the source code defines Unicode, strings are handled as Unicode characters; otherwise, they are handled as ANSI characters.
#define BUFSIZE 65535
#define SHIFTED 0x8000

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
UINT wParam;
LONG lParam;
{
HDC hdc; /* handle of device context */
TEXTMETRIC tm; /* structure for text metrics */
static DWORD dwCharX; /* average width of characters */
static DWORD dwCharY; /* height of characters */
static DWORD dwClientX; /* width of client area */
static DWORD dwClientY; /* height of client area */
static DWORD dwLineLen; /* line length */
static DWORD dwLines; /* text lines in client area */
static int nCaretPosX = 0; /* horizontal position of caret */
static int nCaretPosY = 0; /* vertical position of caret */
static int nCharWidth = 0; /* width of a character */
static int cch = 0; /* characters in buffer */
static int nCurChar = 0; /* index of current character */
static PTCHAR pchInputBuf; /* address of input buffer */
int i, j; /* loop counters */
int cCR = 0; /* count of carriage returns */
int nCRIndex = 0; /* index of last carriage return */
int nVirtKey; /* virtual-key code */
TCHAR szBuf[128]; /* temporary buffer */
TCHAR ch; /* current character */
PAINTSTRUCT ps; /* required by BeginPaint */
RECT rc; /* output rectangle for DrawText */
SIZE sz; /* string dimensions */
COLORREF crPrevText; /* previous text color */
COLORREF crPrevBk; /* previous background color */

switch (uMsg) {
case WM_CREATE:

/* Get the metrics of the current font. */

hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);

/* Save the average character width and height. */

dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;

/* Allocate a buffer to store keyboard input. */

pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));
return 0;

case WM_SIZE:

/* Save the new width and height of the client area. */

dwClientX = LOWORD(lParam);
dwClientY = HIWORD(lParam);

/*
* Calculate the maximum width of a line and the
* maximum number of lines in the client area.
*/

dwLineLen = dwClientX - dwCharX;
dwLines = dwClientY / dwCharY;
break;

case WM_SETFOCUS:

/*
* Create, position, and display the caret when the
* window receives the keyboard focus.
*/

CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
ShowCaret(hwndMain);
break;

case WM_KILLFOCUS:

/*
* Hide and destroy the caret when the window loses the
* keyboard focus.
*/

HideCaret(hwndMain);
DestroyCaret();
break;

case WM_CHAR:
switch (wParam) {
case 0x08: /* backspace */
case 0x0A: /* linefeed */
case 0x1B: /* escape */
MessageBeep(0xFFFFFFFF);
return 0;

case 0x09: /* tab */

/* Convert tabs to four consecutive spaces. */

for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;

case 0x0D: /* carriage return */

/*
* Record the carriage return and position the
* caret at the beginning of the new line.
*/

pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;

default: /* displayable character */

ch = (TCHAR) wParam;
HideCaret(hwndMain);

/*
* Retrieve the character's width and output
* the character.
*/

hdc = GetDC(hwndMain);
GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,
&nCharWidth);
TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY,
&ch, 1);
ReleaseDC(hwndMain, hdc);

/* Store the character in the buffer. */

pchInputBuf[cch++] = ch;

/*
* Calculate the new horizontal position of the
* caret. If the position exceeds the maximum,
* insert a carriage return and move the caret
* to the beginning of the next line.
*/

nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen) {
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwndMain);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;

case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT: /* LEFT ARROW */

/*
* The caret can move only to the beginning of
* the current line.
*/

if (nCaretPosX > 0) {
HideCaret(hwndMain);

/*
* Retrieve the character to the left of
* the caret, calculate the character's
* width, then subtract the width from the
* current horizontal position of the caret
* to obtain the new position.
*/

ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth,
0);
ShowCaret(hwndMain);
}
break;

case VK_RIGHT: /* RIGHT ARROW */

/*
* Caret moves to the right or, when a carriage
* return is encountered, to the beginning of
* the next line.
*/

if (nCurChar < cch) {
HideCaret(hwndMain);

/*
* Retrieve the character to the right of
* the caret. If it's a carriage return,
* position the caret at the beginning of
* the next line.
*/

ch = pchInputBuf[nCurChar];
if (ch == 0x0D) {
nCaretPosX = 0;
nCaretPosY++;
}

/*
* If the character isn't a carriage
* return, check to see whether the SHIFT
* key is down. If it is, invert the text
* colors and output the character.
*/

else {
hdc = GetDC(hwndMain);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED) {
crPrevText = SetTextColor(hdc,
RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,
RGB(0,0,0));
TextOut(hdc, nCaretPosX,
nCaretPosY * dwCharY,
&ch, 1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}

/*
* Get the width of the character and
* calculate the new horizontal
* position of the caret.
*/

GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwndMain);
break;
}
break;

case VK_UP: /* UP ARROW */
case VK_DOWN: /* DOWN ARROW */
MessageBeep(0xFFFFFFFF);
return 0;

case VK_HOME: /* HOME */

/*
* Set the caret's position to the upper left
* corner of the client area.
*/

nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;

case VK_END: /* END */

/* Move the caret to the end of the text. */

for (i=0; i < cch; i++) {

/*
* Count the carriage returns and save the
* index of the last one.
*/

if (pchInputBuf[i] == 0x0D) {
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;

/*
* Copy all text between the last carriage
* return and the end of the keyboard input
* buffer to a temporary buffer.
*/

for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');

/*
* Retrieve the text extent and use it
* to set the horizontal position of the
* caret.
*/

hdc = GetDC(hwndMain);
GetTextExtentPoint32(hdc, szBuf, lstrlen(szBuf),
&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwndMain, hdc);
nCurChar = cch;
break;

default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;

case WM_PAINT:
if (cch == 0) /* nothing in input buffer */
break;

hdc = BeginPaint(hwndMain, &ps);
HideCaret(hwndMain);

/*
* Set the clipping rectangle, and then draw the text
* into it.
*/

SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);

ShowCaret(hwndMain);
EndPaint(hwndMain, &ps);
break;
.
. /* Process other messages. */
.
case WM_DESTROY:
PostQuitMessage(0);

/* Free the input buffer. */

GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwndMain, 0xAAAA);
break;

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

+$#AK Keyboard Input Reference
The following functions and messages are associated with keyboard input.
+$#A Keyboard Input Functions
Following are the functions used to receive and process keyboard input.
ActivateKeyboardLayout_win32_ActivateKeyboardLayoutEnableWindow_win32_EnableWindowGetActiveWindow_win32_GetActiveWindowGetAsyncKeyState_win32_GetAsyncKeyStateGetFocus_win32_GetFocusGetKeyboardLayoutName_win32_GetKeyboardLayoutNameGetKeyboardState_win32_GetKeyboardStateGetKeyNameText_win32_GetKeyNameTextGetKeyState_win32_GetKeyStateIsWindowEnabled_win32_IsWindowEnabledkeybd_event_win32_keybd_eventLoadKeyboardLayout_win32_LoadKeyboardLayoutMapVirtualKey_win32_MapVirtualKeyOemKeyScan_win32_OemKeyScanRegisterHotKey_win32_RegisterHotKeySetActiveWindow_win32_SetActiveWindowSetFocus_win32_SetFocusSetKeyboardState_win32_SetKeyboardStateToAscii_win32_ToAsciiToUnicode_win32_ToUnicodeUnloadKeyboardLayout_win32_UnloadKeyboardLayoutUnregisterHotKey_win32_UnregisterHotKeyVkKeyScan_win32_VkKeyScan
Obsolete or Deleted Functions
GetKBCodePage_win32_GetKBCodePage
+$#A Keyboard Input Messages
Following are the messages used to receive and process keyboard input.
WM_ACTIVATE_win32_WM_ACTIVATEWM_CHAR_win32_WM_CHARWM_DEADCHAR_win32_WM_DEADCHARWM_HOTKEY_win32_WM_HOTKEYWM_KEYDOWN_win32_WM_KEYDOWNWM_KEYUP_win32_WM_KEYUPWM_KILLFOCUS_win32_WM_KILLFOCUSWM_SETFOCUS_win32_WM_SETFOCUSWM_SYSCHAR_win32_WM_SYSCHARWM_SYSDEADCHAR_win32_WM_SYSDEADCHARWM_SYSKEYDOWN_win32_WM_SYSKEYDOWNWM_SYSKEYUP_win32_WM_SYSKEYUP

+$#AK Multiple Document Interface
The multiple document interface (MDI) is a specification that defines the standard user interface for applications written for Microsoft® Windows®.
Windows provides extensive support for MDI applications. This overview describes the structure of an MDI application and how to take advantage of the built-in MDI support found in the Microsoft® Win32® application program interface (API).
+$#A About the Multiple Document Interface
An MDI application enables the user to work with more than one document at the same time. Each document is displayed in a separate child window within the client area of the application's main window. Typical MDI applications include word-processing applications that allow the user to work with multiple text documents, and spreadsheet applications that allow the user to work with multiple charts and spreadsheets.
+$#A Frame, Client, and Child Windows
An MDI application has three kinds of windows: a frame window, a client window, as well as a number of child windows. The frame window is like the main window of the application: it has a sizing border, a title bar, a System menu (also known as the Control menu), a Minimize button, and a Maximize button. The application must register a window class for the frame window and provide a window procedure to support it.
An MDI application does not display output in the client area of the frame window. Instead, it displays the MDI client window. An MDI client window is a special type of child window belonging to the preregistered window class MDICLIENT. The client window is a child of the frame window; it serves as the background for child windows. It also provides support for creating and manipulating child windows. For example, an MDI application can create, activate, or maximize child windows by sending messages to the MDI client window.
When the user opens or creates a document, the client window creates a child window for the document. The client window is the parent window of all MDI child windows in a given application. Each child window has a sizing border, a title bar, a System menu, a Minimize button, and a Maximize button. Because a child window is clipped, it is confined to the client window and cannot appear outside it.
An MDI application can support more than one kind of document. For example, a typical spreadsheet application enables the user to work with both charts and spreadsheets. For each type of document that it supports, an MDI application must register a child window class and provide a window procedure to support the windows belonging to that class. For more information about window classes, see Window Classes_win32_Window_Classes." For more information about window procedures, see Window Procedures_win32_Window_Procedures.
Following is a typical MDI application. It is named Multipad.
{ewc msdncd, EWGraphic, group10805 0 /a "SDK_01.bmp"}
+$#A Child Window Creation
To create a child window, an MDI application either calls the CreateMDIWindow_win32_CreateMDIWindow function or sends the WM_MDICREATE_win32_WM_MDICREATE message to the MDI client window. A more efficient way to create an MDI child window is to call the CreateWindowEx_win32_CreateWindowEx function, specifying the WS_EX_MDICHILD extended style. A thread in an MDI application can use CreateMDIWindow or CreateWindowEx to create a child window in a different thread. The WM_MDICREATE message is used only in the context of the same thread.
To destroy a child window, an MDI application sends a WM_MDIDESTROY_win32_WM_MDIDESTROY message to the MDI client window.
+$#A Child Window Activation
Any number of child windows can appear in the client window at any one time, but only one can be active. The active child window is positioned in front of all other child windows, and its border is highlighted.
The user can activate an inactive child window by clicking it. An MDI application activates a child window by sending a WM_MDIACTIVATE_win32_WM_MDIACTIVATE message to the MDI client window. As the client window processes this message, it sends a WM_MDIACTIVATE message to the window procedure of the child window to be activated and to the window procedure of the child window being deactivated.
Windows keeps track of each child window's position in the stack of overlapping windows. This stacking is known as the Z order. The user can activate the next child window in the Z order by choosing the Next command from the System menu in the active window. An application activates the next (or previous) child window in the Z order by sending a WM_MDINEXT_win32_WM_MDINEXT message to the client window.
To retrieve the handle of the active child window, the MDI application sends a WM_MDIGETACTIVE_win32_WM_MDIGETACTIVE message to the client window.
+$#A Multiple Document Menus
The frame window of an MDI application should include a menu bar with a Window menu item and its corresponding pop-up menu. The Window pop-up menu should include command items that arrange the child windows within the client window or that close all child windows. The Window menu of a typical MDI application might include the items in the following table.
Menu item Purpose
Tile Arranges child windows in a tile format so that each appears in its entirety in the client window.
Cascade Arranges child windows in a cascade format. The child windows overlap one another, but the title bar of each is visible.
Arrange Icons Arranges the icons of minimized child windows along the bottom of the client window.
Close All Closes all child windows.

Whenever a child window is created, Windows automatically appends a new menu item to the Window menu. The text of the menu item is the same as the text on the menu bar of the new child window. By choosing the menu item, the user can activate the corresponding child window. When a child window is destroyed, Windows automatically removes the corresponding menu item from the Window menu.
Windows can add up to ten menu items to the Window menu. When the tenth child window is created, Windows adds the More Windows item to the Window menu. Choosing this item causes the Select Window dialog box to appear. The dialog box contains a list box with the titles of all MDI child windows currently available. The user can activate a child window by choosing its title from the list box.
If your MDI application supports several types of child windows, tailor the menu bar to reflect the operations associated with the active window. To do this, provide separate menu resources for each type of child window the application supports. When a new type of child window is activated, the application should send a WM_MDISETMENU_win32_WM_MDISETMENU message to the client window, passing to it the handle of the corresponding menu.
When no child window exists, the menu bar should contain only items used to create or open a document.
When the user is navigating through an MDI application's menus by using cursor keys, the keys behave differently than when the user is navigating through a typical application's menus. In an MDI application, control passes from the application's System menu to the System menu of the active child window, and then to the first item on the menu bar.
+$#A Accelerators
To receive and process accelerator keys for its child windows, an MDI application must include the TranslateMDISysAccel_win32_TranslateMDISysAccel function in its message loop. The loop must call TranslateMDISysAccel before calling the TranslateAccelerator_win32_TranslateAccelerator or DispatchMessage_win32_DispatchMessage function.
Accelerator keys on the System menu for an MDI child window are different from those for a non-MDI child window. In an MDI child window, the ALT+ - (minus) key combination opens the System menu, the CTRL+F4 key combination closes the active child window, and the CTRL+F6 key combination activates the next child window.
+$#A Child Window Size and Arrangement
An MDI application controls the size and position of its child windows by sending messages to the MDI client window. To maximize the active child window, the application sends the WM_MDIMAXIMIZE_win32_WM_MDIMAXIMIZE message to the client window. When a child window is maximized, its client area completely fills the MDI client window. In addition, Windows automatically hides the child window's title bar, and adds the child window's System menu icon and Restore icon to the MDI application's menu bar. The application can restore the client window to its original (premaximized) size and position by sending the client window a WM_MDIRESTORE_win32_WM_MDIRESTORE message.
An MDI application can arrange its child windows in either a cascade or tile format. When the child windows are cascaded, the windows appear in a stack. The window on the bottom of the stack occupies the upper left corner of the screen, and the remaining windows are offset vertically and horizontally so that the left border and title bar of each child window is visible. To arrange child windows in the cascade format, an MDI application sends the WM_MDICASCADE_win32_WM_MDICASCADE message. Typically, the application sends this message when the user chooses the Cascade command from the Window menu.
When the child windows are tiled, Windows displays each child window in its entirety ¾ overlapping none of the windows. All of the windows are sized, as necessary, to fit within the client window. To arrange child windows in the tile format, an MDI application sends a WM_MDITILE_win32_WM_MDITILE message to the client window. Typically, the application sends this message when the user chooses the Tile command from the Window menu.
An MDI application should provide a different icon for each type of child window it supports. The application specifies an icon when registering the child window class. Windows automatically displays a child window's icon in the lower portion of the client window when the child window is minimized. An MDI application directs Windows to arrange child window icons by sending a WM_MDIICONARRANGE_win32_WM_MDIICONARRANGE message to the client window. Typically, the application sends this message when the user chooses the Arrange Icons command from the Window menu.
+$#A Icon Title Windows
Because MDI child windows may be minimized, an MDI application must avoid manipulating icon title windows as if they were normal MDI child windows. Icon title windows appear when the application enumerates child windows of the MDI client window. Icon title windows differ from other child windows, however, in that they are owned by an MDI child window.
To determine whether a child window is an icon title window, use the GetWindow_win32_GetWindow function with the GW_OWNER index. Nontitle windows return NULL. Note that this test is insufficient for top-level windows, because menus and dialog boxes are owned windows.
+$#A Child Window Data
Because the number of child windows varies depending on how many documents the user opens, an MDI application must be able to associate data (for example, the name of the current file) with each child window. There are two ways to do this:
· Store child window data in the window structure.
· Use window properties.

+$#A Data in the Window Structure
When an MDI application registers a window class, it may reserve extra space in the window structure for application data specific to this particular class of windows. To store and retrieve data in this extra space, the application uses the GetWindowWord_win32_GetWindowWord, SetWindowWord_win32_SetWindowWord, GetWindowLong_win32_GetWindowLong, and SetWindowLong_win32_SetWindowLong functions.
To maintain a large amount of data for a child window, an application can allocate memory for a data structure and then store the handle of the memory containing the structure in the extra space associated with the child window.
+$#A Multiple Document Window Properties
An MDI application can also store per-document data by using window properties. (Per-document data is data specific to the type of document contained in a particular child window.) Properties are different from extra space in the window structure in that you need not allocate extra space when registering the window class. A window can have any number of properties. Also, where offsets are used to access the extra space in window structures, properties are referred to by string names. For more information about window properties, see Window Properties_win32_Window_Properties.
+$#A Using the Multiple Document Interface
This section explains how to perform the following tasks:
· Registering child and frame window classes_win32_Registering_Child_and_Frame_Window_Classes.
· Creating frame and child windows_win32_Creating_Frame_and_Child_Windows.
· Writing the main message loop_win32_Writing_the_Main_Message_Loop.
· Writing the frame window procedure_win32_Writing_the_Frame_Window_Procedure.
· Writing the child window procedure_win32_Writing_the_Child_Window_Procedure.
· Creating a child window_win32_Creating_a_Child_Window.

To illustrate these tasks, this section includes examples from Multipad, a typical MDI application.
+$#A Registering Child and Frame Window Classes
A typical MDI application must register two window classes: one for its frame window and one for its child windows. If an application supports more than one type of document (for example, a spreadsheet and a chart), it must register a window class for each type.
The class structure for the frame window is similar to the class structure for the main window in non-MDI applications. The class structure for the MDI child windows differs slightly from the structure for child windows in non-MDI applications as follows:
· The class structure should have an icon, because the user can minimize an MDI child window as if it were a normal application window.
· The menu name should be NULL, because an MDI child window cannot have its own menu.
· The class structure should reserve extra space in the window structure. With this space, the application can associate data, such as a filename, with a particular child window.

The following example shows how Multipad registers its frame and child window classes.
BOOL APIENTRY InitializeApplication()
{
WNDCLASS wc;

/* Register the frame window class. */

wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, IDMULTIPAD);
wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
wc.lpszMenuName = IDMULTIPAD;
wc.lpszClassName = szFrame;

if (!RegisterClass (&wc) )
return FALSE;

/* Register the MDI child window class. */

wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;
wc.hIcon = LoadIcon(hInst, IDNOTE);
wc.lpszMenuName = (LPCTSTR) NULL;
wc.cbWndExtra = CBWNDEXTRA;
wc.lpszClassName = szChild;

if (!RegisterClass(&wc))
return FALSE;

return TRUE;
}

+$#A Creating Frame and Child Windows
After registering its window classes, an MDI application can create its windows. First, it creates its frame window by using the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function. After creating its frame window, the application creates its client window, again by using CreateWindow or CreateWindowEx. The application should specify MDICLIENT as the client window's class name; MDICLIENT is a preregistered window class defined by Windows. The lpvParam parameter of CreateWindow or CreateWindowEx should point to a CLIENTCREATESTRUCT_win32_CLIENTCREATESTRUCT_str structure. This structure contains the members described in the following table:
Member Description
hWindowMenu Identifies the Window menu used for controlling MDI child windows. As child windows are created, the application adds their titles to the Window menu as menu items. The user can then activate a child window by choosing its title from the Window menu.
idFirstChild Specifies the identifier of the first MDI child window. The first MDI child window created is assigned this identifier. Additional windows are created with incremented window identifiers. When a child window is destroyed, Windows immediately reassigns the window identifiers to keep their range contiguous.

When a child window's title is added to the Window menu, Windows assigns an identifier to the child window. When the user chooses a child window's title, the frame window receives a WM_COMMAND_win32_WM_COMMAND message with the identifier in the wParam parameter. You should specify a value for the idFirstChild member that does not conflict with menu-item identifiers in the frame window's menu.
Multipad's frame window procedure creates the MDI client window while processing the WM_CREATE_win32_WM_CREATE message. The following example shows how the client window is created.
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;

/*
* Retrieve the handle of the Window menu and assign the
* first child window identifier.
*/

ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU);
ccs.idFirstChild = IDM_WINDOWCHILD;

/* Create the MDI client window. */

hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);

ShowWindow(hwndMDIClient, SW_SHOW);
}
break;

Titles of child windows are added to the bottom of the Window menu. If the application adds strings to the Window menu by using the AppendMenu_win32_AppendMenu function, these strings can be overwritten by the titles of the child windows when the Window menu is repainted. (The Window menu is repainted whenever a child window is created or destroyed.) An MDI application that adds strings to its Window menu should use the InsertMenu_win32_InsertMenu function and verify that the titles of child windows have not overwritten these new strings.
Use the WS_CLIPCHILDREN style to create the MDI client window to prevent the window from painting over its child windows.
+$#A Writing the Main Message Loop
The main message loop of an MDI application is similar to that of a non-MDI application handling accelerator keys. The difference is that the MDI message loop calls the TranslateMDISysAccel_win32_TranslateMDISysAccel function before checking for application-defined accelerator keys or before dispatching the message.
The following example shows the message loop of a typical MDI application.
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
!TranslateAccelerator(hwndFrame, hAccel, &msg)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

The TranslateMDISysAccel_win32_TranslateMDISysAccel function translates WM_KEYDOWN_win32_WM_KEYDOWN messages into WM_SYSCOMMAND_win32_WM_SYSCOMMAND messages and sends them to the active MDI child window. If the message is not an MDI accelerator message, the function returns FALSE, in which case the application uses the TranslateAccelerator_win32_TranslateAccelerator function to determine whether any of the application-defined accelerator keys were pressed. If not, the loop dispatches the message to the appropriate window procedure.
+$#A Writing the Frame Window Procedure
The window procedure for an MDI frame window is similar to that of a non-MDI application's main window. The difference is that a frame window procedure passes all messages it does not handle to the DefFrameProc_win32_DefFrameProc function rather than to the DefWindowProc_win32_DefWindowProc function. In addition, the frame window procedure must also pass some messages that it does handle, including those listed in the following table.
Message Response
WM_COMMAND_win32_WM_COMMAND Activates the MDI child window that the user chooses. This message is sent when the user chooses an MDI child window from the Window menu of the MDI frame window. The window identifier accompanying this message identifies the MDI child window to be activated.
WM_MENUCHAR_win32_WM_MENUCHAR Opens the System menu of the active MDI child window when the user presses the ALT+ - (minus) key combination.
WM_SETFOCUS_win32_WM_SETFOCUS Passes the keyboard focus to the MDI client window, which in turn passes it to the active MDI child window.
WM_SIZE_win32_WM_SIZE Resizes the MDI client window to fit in the new frame window's client area. If the frame window procedure sizes the MDI client window to a different size, it should not pass the message to the DefWindowProc_win32_DefWindowProc function.

The frame window procedure in Multipad is called MPFrameWndProc. The handling of other messages by MPFrameWndProc is similar to that of non-MDI applications. WM_COMMAND_win32_WM_COMMAND messages in Multipad are handled by the locally defined CommandHandler function. For command messages Multipad does not handle, CommandHandler calls the DefFrameProc_win32_DefFrameProc function. If Multipad doesn't use DefFrameProc by default, the user can't activate a child window from the Window menu, because the WM_COMMAND message sent by choosing the window's menu item (command) would be lost.
+$#A Writing the Child Window Procedure
Like the frame window procedure, an MDI child window procedure uses a special function for processing messages by default. All messages that the child window procedure does not handle must be passed to the DefMDIChildProc_win32_DefMDIChildProc function rather than to the DefWindowProc_win32_DefWindowProc function. In addition, some window-management messages must be passed to DefMDIChildProc, even if the application handles the message, in order for MDI to function correctly. Following are the messages the application must pass to DefMDIChildProc.
Message Response
WM_CHILDACTIVATE_win32_WM_CHILDACTIVATE Performs activation processing when MDI child windows are sized, moved, or displayed. This message must be passed.
WM_GETMINMAXINFO_win32_WM_GETMINMAXINFO Calculates the size of a maximized MDI child window, based on the current size of the MDI client window.
WM_MENUCHAR_win32_WM_MENUCHAR Passes the message to the MDI frame window.
WM_MOVE_win32_WM_MOVE Recalculates MDI client scroll bars, if they are present.
WM_SETFOCUS_win32_WM_SETFOCUS Activates the child window, if it is not the active MDI child window.
WM_SIZE_win32_WM_SIZE Performs operations necessary for changing the size of a window, especially for maximizing or restoring an MDI child window. Failing to pass this message to the DefMDIChildProc_win32_DefMDIChildProc function produces highly undesirable results.
WM_SYSCOMMAND_win32_WM_SYSCOMMAND Handles System menu commands: SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZE, and SC_MAXIMIZE.

+$#A Creating a Child Window
To create an MDI child window, an application can either call the CreateMDIWindow_win32_CreateMDIWindow function or send an WM_MDICREATE_win32_WM_MDICREATE message to the MDI client window. (The application can use the CreateWindowEx_win32_CreateWindowEx function with the WS_EX_MDICHILD style to create MDI child windows.) A single-threaded MDI application can use either method to create a child window. A thread in a multithreaded MDI application must use the CreateMDIWindow or CreateWindowEx function to create a child window in a different thread.
The lParam parameter of a WM_MDICREATE message is a far pointer to an MDICREATESTRUCT_win32_MDICREATESTRUCT_str structure. The structure includes four dimension members: x and y, which indicate the horizontal and vertical positions of the window, and cx and cy, which indicate the horizontal and vertical extents of the window. Any of these members may be assigned explicitly by the application, or they may be set to CW_USEDEFAULT, in which case Windows selects a position, size, or both, according to a cascading algorithm. In any case, all four members must be initialized. Multipad uses CW_USEDEFAULT for all dimensions.
The last member of the MDICREATESTRUCT structure is the style member, which may contain style bits for the window. To create an MDI child window that can have any combination of window styles, specify the MDIS_ALLCHILDSTYLES window style. When this style is not specified, an MDI child window has the WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLL, and WS_VSCROLL styles as default settings.
Multipad creates its MDI child windows by using its locally defined AddFile function (located in the source file MPFILE.C). The AddFile function sets the title of the child window by assigning the szTitle member of the window's MDICREATESTRUCT structure to either the name of the file being edited or to "Untitled." The szClass member is set to the name of the MDI child window class registered in Multipad's InitializeApplication function. The hOwner member is set to the application's instance handle.
The following example shows the AddFile function in Multipad.
HWND APIENTRY AddFile(pName)
CHAR * pName;
{
HWND hwnd;
CHAR sz[160];
MDICREATESTRUCT mcs;

if (!pName) {

/*
* If the pName parameter is NULL, load the "Untitled"
* string from the STRINGTABLE resource and set the szTitle
* member of MDICREATESTRUCT.
*/

LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz));
mcs.szTitle = (LPCTSTR) sz;
}
else

/*
* Title the window with the full path and filename,
* obtained by calling the OpenFile function with the
* OF_PARSE flag, which is called before AddFile().
*/

mcs.szTitle = of.szPathName;

mcs.szClass = szChild;
mcs.hOwner = hInst;

/* Use the default size for the child window. */

mcs.x = mcs.cx = CW_USEDEFAULT;
mcs.y = mcs.cy = CW_USEDEFAULT;

/*
* Give the child window the default style. The styleDefault
* variable is defined in MULTIPAD.C.
*/

mcs.style = styleDefault;

/* Tell the MDI client window to create the child window. */

hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0,
(LONG) (LPMDICREATESTRUCT) &mcs);

/*
* If the file is found, read its contents into the child
* window's client area.
*/

if (pName) {
if (!LoadFile(hwnd, pName)) {

/* Cannot load the file; close the window. */

SendMessage(hwndMDIClient, WM_MDIDESTROY,
(DWORD) hwnd, 0L);
}
}
return hwnd;
}

The pointer passed in the lParam parameter of the WM_MDICREATE_win32_WM_MDICREATE message is passed to the CreateWindow_win32_CreateWindow function and appears as the first member in the CREATESTRUCT_win32_CREATESTRUCT_str structure, passed in the WM_CREATE_win32_WM_CREATE message. In Multipad, the child window initializes itself during WM_CREATE message processing by initializing document variables in its extra data and by creating the edit control's child window.
+$#AK Multiple Document Interface Reference
The following functions and messages are used in MDI applications.
+$#A Multiple Document Interface Functions
CreateMDIWindow_win32_CreateMDIWindowDefFrameProc_win32_DefFrameProcDefMDIChildProc_win32_DefMDIChildProcTranslateMDISysAccel_win32_TranslateMDISysAccel
+$#A Multiple Document Interface Messages
WM_MDIACTIVATE_win32_WM_MDIACTIVATEWM_MDICASCADE_win32_WM_MDICASCADEWM_MDICREATE_win32_WM_MDICREATEWM_MDIDESTROY_win32_WM_MDIDESTROYWM_MDIGETACTIVE_win32_WM_MDIGETACTIVEWM_MDIICONARRANGE_win32_WM_MDIICONARRANGEWM_MDIMAXIMIZE_win32_WM_MDIMAXIMIZEWM_MDINEXT_win32_WM_MDINEXTWM_MDIREFRESHMENU_win32_WM_MDIREFRESHMENUWM_MDIRESTORE_win32_WM_MDIRESTOREWM_MDISETMENU_win32_WM_MDISETMENUWM_MDITILE_win32_WM_MDITILE

+$#AK Messages and Message Queues
This overview describes messages and message queues and explains how to use them in your application.
+$#A About Messages and Message Queues
Unlike traditional applications, applications written for Microsoft® Windows® do not make explicit function calls (such as to getchar) to obtain input. Instead, they wait for Windows to pass input to them.
Windows passes all input for an application to the various windows in the application. Each window has a function, called a window procedure, that Windows calls whenever it has input for the window. The window procedure processes the input and returns control to Windows. For more information about window procedures, see Window Procedures_win32_Window_Procedures.
+$#A Messages
Windows passes input to a window procedure in the form of messages. Messages are generated by Windows and by applications. Windows generates a message at each input event ¾ for example, when the user types, moves the mouse, or clicks a control such as a scroll bar. Windows also generates messages in response to changes in the system brought about by an application, such as when an application changes the pool of system font resources or resizes one of its windows. An application can generate messages to direct its own windows to perform tasks or to communicate with windows in other applications.
Windows sends a message to a window procedure with a set of four parameters: a window handle, a message identifier, and two 32-bit values called message parameters. The window handle identifies the window for which the message is intended. Windows uses it to determine which window procedure to send the message to.
A message identifier is a named constant that identifies the purpose of a message. When a window procedure receives a message, it uses a message identifier to determine how to process the message. For example, the message identifier WM_PAINT_win32_WM_PAINT tells the window procedure that the window's client area has changed and must be repainted.
Message parameters specify data or the location of data used by a window procedure when processing a message. The meaning and value of the message parameters depend on the message. A message parameter can contain an integer, packed bit flags, a pointer to a structure containing additional data, and so on. When a message does not use message parameters, it typically sets them to NULL. A window procedure must check the message identifier to determine how to interpret the message parameters.
+$#A Message Routing
Windows uses two methods to route messages to a window procedure: posting messages to a first-in, first-out queue called a message queue, a system-defined memory object that temporarily stores messages, and sending messages directly to a window procedure.
Messages posted to a message queue are called queued messages. They are primarily the result of user input entered through the mouse or keyboard, such as WM_MOUSEMOVE_win32_WM_MOUSEMOVE, WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN, WM_KEYDOWN_win32_WM_KEYDOWN, and WM_CHAR_win32_WM_CHAR messages. Other queued messages include the timer, paint, and quit messages: WM_TIMER_win32_WM_TIMER, WM_PAINT_win32_WM_PAINT, and WM_QUIT_win32_WM_QUIT. Most other messages, which are sent directly to a window procedure, are called nonqueued messages.
+$#A Queued Messages
Windows can display any number of windows at a time. To route mouse and keyboard input to the appropriate window, Windows uses message queues.
Windows maintains a single system message queue and any number of thread message queues, one for each GUI thread. To avoid the overhead of creating a message queue for non-GUI threads, all threads are created initially without a message queue. The system creates a thread's message queue only when the thread makes its first call to one of the Win32 USER or GDI functions.
Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the mouse or keyboard converts the input into messages and places them in the system message queue. Windows removes the messages, one at a time, from the system message queue, examines them to determine the destination window, and then posts them to the message queue of the thread that created the destination window. A thread's message queue receives all mouse and keyboard messages for the windows created by the thread. The thread removes messages from its queue and directs Windows to send them to the appropriate window procedure for processing. For more information about threads, see Processes and Threads_win32_Processes_and_Threads.
With the exception of the WM_PAINT_win32_WM_PAINT message, Windows always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first-in, first-out sequence. The WM_PAINT message, however, is kept in the queue and is forwarded to the window procedure only when the queue contains no other messages. Multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.
The system posts a message to a thread's message queue by filling an MSG_win32_MSG_str structure and then copying it to the message queue. Information in MSG includes: the handle of the window for which the message is intended, the message identifier, the two message parameters, the time the messate was posted, and the mouse cursor position. A thread can post a message to its own message queue or to the queue of another thread by using the PostMessage_win32_PostMessage or PostThreadMessage_win32_PostThreadMessage function.
An application can remove a message from its queue by using the GetMessage_win32_GetMessage function. To examine a message without removing it from its queue, an application can use the PeekMessage_win32_PeekMessage function. This function fills MSG with information about the message.
After removing a message from its queue, an application can use the DispatchMessage_win32_DispatchMessage function to direct Windows to send the message to a window procedure for processing. DispatchMessage takes a pointer to MSG that was filled by a previous call to the GetMessage or PeekMessage function. DispatchMessage passes the window handle, the message identifier, and the two message parameters to the window procedure, but it does not pass the time the message was posted or mouse cursor position. An application can retrieve this information by calling the GetMessageTime_win32_GetMessageTime and GetMessagePos_win32_GetMessagePos functions while processing a message.
+$#A Nonqueued Messages
Nonqueued messages are sent immediately to the destination window procedure, bypassing the system message queue and thread message queue. Windows typically sends nonqueued messages to notify a window of events that affect it. For example, when the user activates a new application window, Windows sends the window a series of messages, including WM_ACTIVATE_win32_WM_ACTIVATE, WM_SETFOCUS_win32_WM_SETFOCUS, and WM_SETCURSOR_win32_WM_SETCURSOR. These messages notify the window that it has been activated, that keyboard input is being directed to the window, and that the mouse cursor has been moved within the borders of the window. Nonqueued messages can also result when an application calls certain Windows functions. For example, Windows sends the WM_WINDOWPOSCHANGED_win32_WM_WINDOWPOSCHANGED message after an application uses the SetWindowPos_win32_SetWindowPos function to move a window.
An application can send a message by calling the SendMessage_win32_SendMessage, SendNotifyMessage_win32_SendNotifyMessage, or SendDlgItemMessage_win32_SendDlgItemMessage function. For more information about sending messages, see Sending a Message_win32_Sending_a_Message.
+$#A Message Handling
An application must remove and process messages posted to the message queues of its threads. A single-threaded application usually uses a message loop in its WinMain_win32_WinMain function to remove and send messages to the appropriate window procedures for processing. Applications with multiple threads can include a message loop in each thread that creates a window. The following sections describe how a message loop works and explain the role of a window procedure.
+$#A Message Loop
A simple message loop consists of one function call to each of these three functions: GetMessage_win32_GetMessage, TranslateMessage_win32_TranslateMessage, and DispatchMessage_win32_DispatchMessage. The GetMessage function retrieves a message from the queue and copies it to an MSG_win32_MSG_str structure. GetMessage returns TRUE unless it encounters the WM_QUIT_win32_WM_QUIT message, in which case it returns FALSE and ends the loop. In a single-threaded application, ending the message loop is often the first step in closing the application. An application can end its own loop by using the PostQuitMessage_win32_PostQuitMessage function, typically in response to the WM_DESTROY_win32_WM_DESTROY message in the window procedure of the application's main window.
If you specify a window handle in GetMessage, only messages for the specified window are retrieved from the queue. GetMessage can also filter messages in the queue, retrieving only those messages that fall within a specified range. For more information about filtering messages, see Message Filtering_win32_Message_Filtering.
A thread's message loop must include TranslateMessage, if the thread is to receive character input from the keyboard. Windows generates virtual-key messages (WM_KEYDOWN_win32_WM_KEYDOWN and WM_KEYUP_win32_WM_KEYUP) each time the user presses a key. A virtual-key message contains a virtual-key code that identifies which key was pressed, but not its character value. To retrieve this value, the message loop must contain TranslateMessage, which translates the virtual-key message into a character message (WM_CHAR_win32_WM_CHAR) and places it back into the application message queue. The character message can then be removed upon a subsequent iteration of the message loop and dispatched to a window procedure.
The DispatchMessage function sends a message to the window procedure associated with the window handle specified in the MSG structure. If the window handle is HWND_TOPMOST, DispatchMessage sends the message to the window procedures of all top-level windows in the system. If the window handle is NULL, DispatchMessage does nothing with the message.
An application's main thread starts its message loop after initializing the application and creating at least one window. Once started, the message loop continues to retrieve messages from the thread's message queue and to dispatch them to the appropriate windows. The message loop ends when the GetMessage_win32_GetMessage function removes the WM_QUIT_win32_WM_QUIT message from the message queue.
Only one message loop is needed for a message queue, even if an application contains many windows. DispatchMessage_win32_DispatchMessage always dispatches the message to the proper window; this is because each message in the queue is an MSG_win32_MSG_str structure that contains the handle of the window to which the message belongs.
An application can modify its message loop in a variety of ways. For example, it can retrieve messages from the queue without dispatching them to a window. This is useful for applications that post messages not specifying a window. (These messages apply to the application rather than to a specific window because they have NULL window handles.) An application can also direct GetMessage to search for specific messages, leaving other messages in the queue. This is useful for applications that must temporarily bypass the usual first-in, first-out order of the message queue.
An application that uses accelerator keys must be able to translate keyboard messages into command messages. To do this, the application's message loop must include a call to the TranslateAccelerator_win32_TranslateAccelerator function. For more information about accelerator keys, see Keyboard Accelerators_win32_Keyboard_Accelerators.
+$#A Window Procedure
A window procedure is a function that receives and processes all messages sent to the window. Every window class has a window procedure, and every window created with that class uses that same window procedure to respond to messages.
The system sends a message to a window procedure by passing the message data as arguments to the procedure. The window procedure then performs an appropriate action for the message; it checks the message identifier and, while processing the message, uses the information specified by the message parameters.
A window procedure does not usually ignore a message. If it does not process a message, it must send the message back to the system for default processing. The window procedure does this by calling the DefWindowProc_win32_DefWindowProc function, which performs a default action and returns a message result. The window procedure must then return this value as its own message result. Most window procedures process just a few types of messages and pass the others on to the system by calling DefWindowProc.
Because a window procedure is shared by all windows belonging to the same class, it can process messages for several different windows. To identify the specific window affected by the message, a window procedure can examine the window handle passed with a message. For more information about window procedures, see Window Procedures_win32_Window_Procedures.
+$#A Posting and Sending Messages
Any application can post and send messages. Like the system, an application posts a message by copying it to a message queue and sends a message by passing the message data as arguments to a window procedure. To post and send messages, an application uses the PostMessage_win32_PostMessage and SendMessage_win32_SendMessage functions.
An application typically posts a message to notify a specific window to perform a task. PostMessage creates an MSG_win32_MSG_str structure for the message and copies the message to the message queue. The application's message loop eventually retrieves the message and dispatches it to the appropriate window procedure.
An application typically sends a message to notify a window procedure to perform a task immediately. The SendMessage function sends the message to the window procedure corresponding to the given window. The function waits until the window procedure completes processing and then returns the message result. Parent and child windows often communicate by sending messages to each other. For example, a parent window that has an edit control as its child window can set the text of the control by sending a message to it. The control can notify the parent window of changes to the text that are carried out by the user by sending messages back to the parent.
Occasionally, an application may be required to send or post a message to all top-level windows in the system. For example, if the application changes the system time, it must notify all top-level windows about the change by sending a WM_TIMECHANGE_win32_WM_TIMECHANGE message. An application can send or post a message to all top-level windows by calling the SendMessage or PostMessage function and specifying HWND_TOPMOST in the hwnd parameter.
An application can post a message without specifying a window. If the application supplies a NULL window handle when calling PostMessage_win32_PostMessage, the message is posted to the queue associated with the current thread. Because no window handle is specified, the application must process the message in the message loop. This is one way to create a message that applies to the entire application, instead of to a specific window.
By using the InSendMessage_win32_InSendMessage function, a window procedure can determine whether it is processing a message sent by another thread. This capability is useful when message processing depends on the origin of the message.
A common programming error is to assume that the PostMessage function always posts a message. This is not true when the message queue is full. An application should check the return value of the PostMessage function to determine whether the message has been posted and, if it has not been, repost it.
+$#A Message Types
This section describes the two types of Windows messages: system defined and application defined.
+$#A System-Defined Messages
The system uses system-defined messages to control the operations of applications and to provide input and other information for applications to process. The system sends or posts a system-defined message when it communicates with an application. An application can also send or post system-defined messages. Applications usually use these messages to control the operation of control windows created by using preregistered window classes.
Each system message has a unique message identifier and a corresponding symbolic constant (defined in the Windows header files) that states the purpose of the message. For example, the WM_PAINT_win32_WM_PAINT constant requests that a window paint its contents.
Symbolic constants specify the category to which system-defined messages belong. The prefix of the constant identifies the type of window that can interpret and process the message. Following are the prefixes and their related message categories.
Prefix Message category
BM Button control
CB Combo box control
DM Default push button control
EM Edit control
LB List box control
SBM Scroll bar control
WM General window

General window messages cover a wide range of information and requests, including messages for mouse and keyboard input, menu and dialog box input, window creation and management, and dynamic data exchange (DDE).
+$#A Application-Defined Messages
An application can create messages to use in its own windows or to communicate with windows in other processes. If an application creates its own messages, the window procedure that receives them must interpret the messages and provide appropriate processing.
Windows reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER_win32_WM_USER - 1) and 0x8000 through 0xBFFF for system-defined messages. Applications cannot use these values for private messages.
Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers defined by an application for its own use. Values in the range 0xC000 through 0xFFFF are available for message identifiers defined by an application for use in communicating with windows in other applications.
Windows returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage_win32_RegisterWindowMessage function to register a message. The message identifier returned by this function is guaranteed to be unique throughout the system. If your application creates messages to communicate with windows in other applications, it should use RegisterWindowMessage to register the message. Use of this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.
+$#A Message Filtering
An application can choose specific messages to retrieve from the message queue (while ignoring other messages) by using the GetMessage_win32_GetMessage or PeekMessage_win32_PeekMessage function to specify a message filter. The filter is a range of message identifiers (specified by a first and last identifier), a window handle, or both. GetMessage and PeekMessage use a message filter to select which messages to retrieve from the queue. Message filtering is useful if an application must search the message queue for messages that have arrived later in the queue.
Any application that filters messages must ensure that a message satisfying the message filter can be posted. For example, if an application filters for a WM_CHAR_win32_WM_CHAR message in a window that does not receive keyboard input, the GetMessage function does not return. This effectively "hangs" the application.
To filter for keyboard, mouse, and DDE messages, an application can use the WM_KEYFIRST and WM_KEYLAST, WM_MOUSEFIRST and WM_MOUSELAST messages, and WM_DDE_FIRST and WM_DDE_LAST constants.
+$#A Message Deadlocks
A thread that calls the SendMessage_win32_SendMessage function to send a message to another thread cannot continue executing until the window procedure that receives the message returns. If the receiving thread yields control while processing the message, the sending thread cannot continue executing, because it is waiting for SendMessage to return. This situation is called a deadlock. The receiving thread need not yield control explicitly; calling any of the following functions can cause a thread to yield control.
DialogBox_win32_DialogBoxDialogBoxIndirect_win32_DialogBoxIndirectDialogBoxIndirectParam_win32_DialogBoxIndirectParamDialogBoxParam_win32_DialogBoxParamGetMessage_win32_GetMessageMessageBox_win32_MessageBoxPeekMessage_win32_PeekMessage
A window procedure can determine whether a message it has received was sent by another thread by calling the InSendMessage_win32_InSendMessage function. Before calling any of the functions in the preceding list while processing a message, the window procedure should first call InSendMessage. If this function returns TRUE, the window procedure must call the ReplyMessage_win32_ReplyMessage function before any function that causes the thread to yield control.
+$#A Using Messages and Message Queues
· Creating a message loop_win32_Creating_a_Message_Loop
· Examining a message queue_win32_Examining_a_Message_Queue
· Posting a message_win32_Posting_a_Message
· Sending a message_win32_Sending_a_Message

+$#A Creating a Message Loop
Windows automatically creates a message queue for each thread. If the thread creates one or more windows, a message loop must be provided; this message loop retrieves messages from the thread's message queue and dispatches them to the appropriate window procedures.
Because Windows directs messages to individual windows in an application, a thread must create at least one window before starting its message loop. Most Windows-based applications contain a single thread that creates windows. A typical application registers the window class for its main window, creates and shows the main window, and then starts its message loop ¾ all in the WinMain_win32_WinMain function.
You create a message loop by using the GetMessage_win32_GetMessage and DispatchMessage_win32_DispatchMessage functions. If your application must obtain character input from the user, include the TranslateMessage_win32_TranslateMessage function in the loop. TranslateMessage translates virtual-key messages into character messages. The following example shows the message loop in the WinMain function of a simple Windows-based application.
HINSTANCE hinst;
HWND hwndMain;

int APIENTRY WinMain(hInstance, hPrevInstance, lpszCmdLine,
nCmdShow)
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpszCmdLine;
int nCmdShow;
{
MSG msg;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);

/* Register the window class for the main window. */

if (!hPrevInstance) {
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL,
IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL,
IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";

if (!RegisterClass(&wc))
return FALSE;
}

hinst = hInstance; /* save instance handle */

/* Create the main window. */

hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);

/*
* If the main window cannot be created, terminate
* the application.
*/

if (!hwndMain)
return FALSE;

/* Show the window and paint its contents. */

ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);

/* Start the message loop. */

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

/* Return the exit code to Windows. */

return msg.wParam;
}

The GetMessage_win32_GetMessage, TranslateMessage_win32_TranslateMessage, and DispatchMessage_win32_DispatchMessage functions take a pointer to an MSG_win32_MSG_str structure as a parameter. If a message is available, GetMessage copies it to the MSG structure. If the message is a virtual-key message (such as WM_KEYDOWN_win32_WM_KEYDOWN or WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN), TranslateMessage generates a character message (WM_CHAR_win32_WM_CHAR or WM_SYSCHAR_win32_WM_SYSCHAR) and places it in the message queue. DispatchMessage uses the members of the MSG structure as arguments for the window procedure; DispatchMessage does not return until the window procedure finishes processing the message.
If a thread supports accelerators, its message loop must include the TranslateAccelerator_win32_TranslateAccelerator function. This function checks for key combinations that match an entry in the thread's accelerator table. When it finds a match, TranslateAccelerator translates the key combination into a WM_COMMAND_win32_WM_COMMAND message and dispatches it to the window procedure. For more information about accelerators, see Keyboard Accelerators_win32_Keyboard_Accelerators.
If a thread uses a modeless dialog box, the message loop must include the IsDialogMessage_win32_IsDialogMessage function so that the dialog box can receive keyboard input. For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes.
The following example shows a message loop for a thread that uses accelerators and displays a modeless dialog box. When TranslateAccelerator or IsDialogMessage returns TRUE (indicating that the message has been processed), TranslateMessage and DispatchMessage are not called. The reason for this is that TranslateAccelerator and IsDialogMessage perform all necessary translating and dispatching of messages.
HWND hwndMain;
HWND hwndDlgModeless = NULL;
MSG msg;
HACCEL haccel;
.
. /* Perform initialization and create a main window. */
.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
if (hwndDlgModeless == (HWND) NULL ||
!IsDialogMessage(hwndDlgModeless, &msg) &&
!TranslateAccelerator(hwndMain, haccel,
&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

+$#A Examining a Message Queue
Occasionally, an application needs to examine the contents of a thread's message queue from outside the thread's message loop. For example, if an application's window procedure performs a lengthy drawing operation, you may want the user to be able to interrupt the operation. Unless your application periodically examines the message queue during the operation for mouse and keyboard messages, it will not respond to user input until after the operation has completed. The reason for this is that the DispatchMessage_win32_DispatchMessage function in the thread's message loop does not return until the window procedure finishes processing a message.
You can use the PeekMessage_win32_PeekMessage function to examine a message queue during a lengthy operation. PeekMessage is similar to the GetMessage_win32_GetMessage function; both check a message queue for a message that matches the filter criteria and then copy the message to an MSG_win32_MSG_str structure. The main difference between the two functions is that GetMessage does not return until a message matching the filter criteria is placed in the queue, whereas PeekMessage returns immediately regardless of whether a message is in the queue.
The following example shows how to use PeekMessage to examine a message queue for mouse clicks and keyboard input during a lengthy operation.
HWND hwnd;
BOOL fDone;
MSG msg;

/*
* Begin the operation and continue until it is complete
* or until the user clicks the mouse or presses a key.
*/

fDone = FALSE;
while (!fDone) {
fDone = DoLengthyOperation(); /* application-defined */

/*
* Remove any messages that may be in the queue. If the
* queue contains any mouse or keyboard
* messages, end the operation.
*/

while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
switch(msg.message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
.
. /* Perform any required cleanup. */
.
fDone = TRUE;
}
}
}

Other functions, including GetQueueStatus_win32_GetQueueStatus and GetInputState_win32_GetInputState, also allow you to examine the contents of a thread's message queue. GetQueueStatus returns an array of flags that indicates the types of messages in the queue; using it is the fastest way to discover whether the queue contains any messages. GetInputState returns TRUE if the queue contains mouse or keyboard messages. Both of these functions can be used to determine whether the queue contains messages that need to be processed.
+$#A Posting a Message
You can post a message to a message queue by using the PostMessage_win32_PostMessage function. PostMessage places a message at the end of a thread's message queue and returns immediately, without waiting for the thread to process the message. The function's parameters include a window handle, a message identifier, and two message parameters. Windows copies these parameters to an MSG_win32_MSG_str structure, fills the time and pt members of the structure, and places the structure in the message queue.
Windows uses the window handle passed with the PostMessage function to determine which thread message queue should receive the message. If the handle is HWND_TOPMOST, Windows posts the message to the thread message queues of all top-level windows.
You can use the PostThreadMessage_win32_PostThreadMessage function to post a message to a specific thread message queue. PostThreadMessage is similar to PostMessage, except the first parameter is a thread identifier rather than a window handle. You can retrieve the thread identifier by calling the GetCurrentThreadId_win32_GetCurrentThreadId function.
Use the PostQuitMessage_win32_PostQuitMessage function to exit a message loop. PostQuitMessage posts the WM_QUIT_win32_WM_QUIT message to the currently executing thread. The thread's message loop terminates and returns control to Windows when it encounters the WM_QUIT message. An application usually calls PostQuitMessage in response to the WM_DESTROY_win32_WM_DESTROY message, as shown in the following example.
case WM_DESTROY:
.
. /* Perform cleanup tasks. */
.
PostQuitMessage(0);
break;

+$#A Sending a Message
The SendMessage_win32_SendMessage function is used to send a message directly to a window procedure. SendMessage calls a window procedure and waits for that procedure to process the message and return a result.
A message can be sent to any window in the system; all that is required is a window handle. Windows uses the handle to determine which window procedure should receive the message.
If a window procedure yields control when processing a message sent by another thread, a message deadlock may result. (For more information about message deadlocks, see Message Deadlocks_win32_Message_Deadlocks.) Before processing a message that may have been sent from another thread, a window procedure should first call the InSendMessage_win32_InSendMessage function. If this function returns TRUE, the window procedure should call ReplyMessage_win32_ReplyMessage before any function that causes the thread to yield control, as shown in the following example.
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);

DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;

A number of messages can be sent to controls in a dialog box. These control messages set the appearance, behavior, and content of controls or retrieve information about controls. For example, the CB_ADDSTRING_win32_CB_ADDSTRING message can add a string to a combo box, and the BM_SETCHECK_win32_BM_SETCHECK message can set the check state of a check box or radio button.
Use the SendDlgItemMessage_win32_SendDlgItemMessage function to send a message to a control, specifying the identifier of the control and the handle of the dialog box window that contains the control. The following example, taken from a dialog box procedure, copies a string from a combo box's edit control into its list box. The example uses SendDlgItemMessage to send a CB_ADDSTRING message to the combo box.
HWND hwndCombo;
int cTxtLen;
PSTR pszMem;

switch (uMsg) {

case WM_COMMAND:
switch (LOWORD(wParam)) {

case IDD_ADDCBITEM:

/*
* Get the handle of the combo box and the
* length of the string in the edit control
* of the combo box.
*/

hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hwndCombo);

/*
* Allocate memory for the string and copy
* the string into the memory.
*/

pszMem = (PSTR) VirtualAlloc((LPVOID) NULL,
(DWORD) (cTxtLen + 1), MEM_COMMIT,
PAGE_READWRITE);
GetWindowText(hwndCombo, pszMem,
cTxtLen + 1);

/*
* Add the string to the list box of the
* combo box and remove the string from the
* edit control of the combo box.
*/

if (*pszMem != NULL) {
SendDlgItemMessage(hwndDlg, IDD_COMBO,
CB_ADDSTRING, 0,
(DWORD) ((LPSTR) pszMem));
SetWindowText(hwndCombo, (LPSTR) NULL);
}

/* Free the memory and return. */

VirtualFree(pszMem, 0, MEM_RELEASE);
return TRUE;
.
. /* Process other dialog box commands. */
.

}
.
. /* Process other dialog box messages. */
.

}

+$#A Message and Message Queue Functions
Following are the functions used with messages and message queues.
BroadcastSystemMessage_win32_BroadcastSystemMessageDefWindowProc_win32_DefWindowProcDispatchMessage_win32_DispatchMessageGetInputState_win32_GetInputStateGetMessage_win32_GetMessageGetMessageExtraInfo_win32_GetMessageExtraInfoGetMessagePos_win32_GetMessagePosGetMessageTime_win32_GetMessageTimeGetQueueStatus_win32_GetQueueStatusInSendMessage_win32_InSendMessagePeekMessage_win32_PeekMessagePostAppMessage_win32_PostAppMessagePostMessage_win32_PostMessagePostQuitMessage_win32_PostQuitMessagePostThreadMessage_win32_PostThreadMessageRegisterWindowMessage_win32_RegisterWindowMessageReplyMessage_win32_ReplyMessageSendAsyncProc_win32_SendAsyncProcSendMessage_win32_SendMessageSendMessageCallback_win32_SendMessageCallbackSendMessageTimeout_win32_SendMessageTimeoutSendNotifyMessage_win32_SendNotifyMessageSetMessageExtraInfo_win32_SetMessageExtraInfoTranslateMessage_win32_TranslateMessageWaitMessage_win32_WaitMessage
Obsolete or Deleted Functions
SetMessageQueue_win32_SetMessageQueue

+$#AK Mouse Input
This overview describes how Windows generates mouse input and explains how an application receives and processes that input.
+$#A About Mouse Input
The mouse is an important, but optional, user-input device for applications written for Microsoft® Windows®. A well-written Windows-based application should include a mouse interface, but it should not depend on the mouse as the sole means of acquiring user input. The application should provide full keyboard support as well.
A Windows-based application receives mouse input in the form of messages that are sent or posted to its windows.
+$#A Mouse Cursor
When the user moves the mouse, the system moves a bitmap on the screen called the mouse cursor. The mouse cursor contains a single-pixel point called the hot spot, a point that the system tracks and recognizes as the position of the cursor. When a mouse event occurs, the window that contains the hot spot typically receives the mouse message resulting from the event. The window need not be active or have the keyboard focus to receive a mouse message.
The system maintains a variable that controls the mouse speed ¾ that is, the distance the cursor moves when the user moves the mouse. You can use the SystemParametersInfo_win32_SystemParametersInfo function with the SPI_GETMOUSE or SPI_SETMOUSE flag to retrieve or set the mouse speed. For more information about mouse cursors, see Cursors_win32_Cursors.
+$#A Mouse Capture
The system typically posts a mouse message to the window that contains the cursor hot spot at the time a mouse event occurs. An application can change this behavior by using the SetCapture_win32_SetCapture function to route mouse messages to a specific window. The window receives all mouse messages until the application calls the ReleaseCapture_win32_ReleaseCapture function or specifies another capture window, or until the user clicks a window created by another thread.
Whenever the mouse capture changes, Windows sends a WM_CAPTURECHANGED_win32_WM_CAPTURECHANGED message to the window that is losing the mouse capture. The lParam parameter of the message specifies the handle of the window that is gaining the mouse capture.
Only the foreground window can capture mouse input. When a background window attempts to capture mouse input, it receives messages only for mouse events that occur when the cursor hot spot is within the visible portion of the window.
Capturing mouse input is useful if a window must receive all mouse input, even when the cursor moves outside the window. For example, an application typically tracks the cursor position after a mouse "button down" event, following the cursor until a "button up" event occurs. If an application has not captured mouse input and the user releases the mouse button outside the window, the window does not receive the button up message.
A thread can use the GetCapture_win32_GetCapture function to determine whether one of its windows has captured the mouse. If one of the thread's windows has captured the mouse, GetCapture retrieves the handle of the window.
+$#A Mouse Configuration
Although the mouse is an important input device for Windows-based applications, not every user necessarily has a mouse. An application can determine whether the system includes a mouse by passing the SM_MOUSEPRESENT value to the GetSystemMetrics_win32_GetSystemMetrics function.
Windows supports a mouse having up to three buttons. On a three-button mouse, the buttons are designated as the left, middle, and right buttons. Windows messages and named constants related to the mouse buttons use the letters L, M, and R to identify the buttons. The button on a single-button mouse is considered to be the left button. Although Windows supports a mouse with multiple buttons, most applications use the left button primarily and use the others little, if at all.
An application can determine the number of buttons on the mouse by passing the SM_CMOUSEBUTTONS value to the GetSystemMetrics function. To configure the mouse for a left-handed user, the application can use the SwapMouseButton_win32_SwapMouseButton function to reverse the meaning of the left and right mouse buttons. Passing the SPI_SETMOUSEBUTTONSWAP value to the SystemParametersInfo_win32_SystemParametersInfo function is another way to reverse the meaning of the buttons. Note, however, that the mouse is a shared resource, so reversing the meaning of the buttons affects all applications.
+$#A Mouse Messages
The mouse generates an input event whenever the user moves the mouse, or presses or releases a mouse button. Windows converts mouse input events into messages and posts them to the appropriate thread's message queue. When mouse messages are posted faster than a thread can process them, Windows discards all but the most recent mouse message.
A window receives a mouse message when a mouse event occurs while the cursor is within the borders of the window, or when the window has captured the mouse. Mouse messages are divided into two groups: client area messages and nonclient area messages. Typically, an application processes client area messages and ignores nonclient area messages.
+$#A Client Area Mouse Messages
A window receives a client area mouse message when a mouse event occurs within the window's client area. The system posts the WM_MOUSEMOVE_win32_WM_MOUSEMOVE message to the window when the user moves the cursor within the client area. It posts one of the following messages when the user presses or releases a mouse button while the cursor is within the client area.
Message Meaning
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK The left mouse button was double-clicked.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN The left mouse button was pressed.
WM_LBUTTONUP_win32_WM_LBUTTONUP The left mouse button was released.
WM_MBUTTONDBLCLK_win32_WM_MBUTTONDBLCLK The middle mouse button was double-clicked.
WM_MBUTTONDOWN_win32_WM_MBUTTONDOWN The middle mouse button was pressed.
WM_MBUTTONUP_win32_WM_MBUTTONUP The middle mouse button was released.
WM_RBUTTONDBLCLK_win32_WM_RBUTTONDBLCLK The right mouse button was double-clicked.
WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN The right mouse button was pressed.
WM_RBUTTONUP_win32_WM_RBUTTONUP The right mouse button was released.

Message Parameters
The lParam parameter of a client area mouse message indicates the position of the cursor hot spot. The low-order word indicates the x-coordinate of the hot spot, and the high-order word indicates the y-coordinate. The coordinates are given in client coordinates. In the client coordinate system, all points on the screen are given relative to the coordinates (0,0) of the upper left corner of the client area.
The wParam parameter contains flags that indicate the status of the other mouse buttons and the CTRL and SHIFT keys at the time of the mouse event. You can check for these flags when mouse-message processing depends on the state of another mouse button or of the CTRL or SHIFT key. The lParam parameter can be a combination of the following values.
Value Meaning
MK_CONTROL The CTRL key is down.
MK_LBUTTON The left mouse button is down.
MK_MBUTTON The middle mouse button is down.
MK_RBUTTON The right mouse button is down.
MK_SHIFT The SHIFT key is down.

Double-Click Messages
The system generates a double-click message when the user clicks a mouse button twice in quick succession. When the user clicks a button, the system establishes a rectangle centered around the cursor hot spot. It also marks the time at which the click occurred. When the user clicks the same button a second time, the system determines whether the hot spot is still within the rectangle and calculates the time elapsed since the first click. If the hot spot is still within the rectangle and the elapsed time does not exceeded the double-click time-out value, the system generates a double-click message.
An application can get and set double-click time-out values by using the GetDoubleClickTime_win32_GetDoubleClickTime and SetDoubleClickTime_win32_SetDoubleClickTime functions, respectively. Alternatively, the application can set the double-click time-out value by using the SPI_SETDOUBLECLICKTIME flag with the SystemParametersInfo_win32_SystemParametersInfo function. It can also set the size of the rectangle that Windows uses to detect double-clicks by passing the SPI_SETDOUBLECLKWIDTH and SPI_SETDOUBLECLKHEIGHT flags to SystemParametersInfo. Note, however, that setting the double-click time-out value and rectangle affects all applications.
An application-defined window does not, by default, receive double-click messages. Because of the system overhead involved in generating double-click messages, these messages are generated only for windows belonging to classes that have the CS_DBLCLKS class style. Your application must set this style when registering the window class. For more information about window classes, see Window Classes_win32_Window_Classes.
A double-click message is always the third message in a four-message series. The first two messages are the button down and button up messages generated by the first click. The second click generates the double-click message followed by another button up message. For example, double-clicking the left mouse button generates the following message sequence:
WM_LBUTTONDOWNWM_LBUTTONUPWM_LBUTTONDBLCLKWM_LBUTTONUP
Because a window always receives a button down message before receiving a double-click message, an application typically uses a double-click message to extend a task it began during a button down message. For example, when the user clicks a color in the color palette of Windows Paintbrush™, Paintbrush displays the selected color next to the palette. When the user double-clicks a color, Paintbrush displays the color and opens the Edit Colors dialog box.
+$#A Nonclient Area Mouse Messages
A window receives a nonclient area mouse message when a mouse event occurs in any part of a window except the client area. A window's nonclient area consists of its border, menu bar, title bar, scroll bar, System menu (also called the Control menu), Minimize button, and Maximize button.
Windows generates nonclient area messages primarily for its own use. For example, Windows uses nonclient area messages to change the cursor to a two-headed arrow when the cursor hot spot moves into a window's border. A window must pass nonclient area mouse messages to the DefWindowProc_win32_DefWindowProc function to take advantage of the built-in mouse interface found in Windows.
There is a corresponding nonclient area mouse message for each client area mouse message. The names of these messages are similar except that the named constants for the nonclient area messages include the letters "NC". For example, moving the cursor in the nonclient area generates a WM_NCMOUSEMOVE_win32_WM_NCMOUSEMOVE message, and pressing the left mouse button while the cursor is in the nonclient area generates a WM_NCLBUTTONDOWN_win32_WM_NCLBUTTONDOWN message.
The lParam parameter of a nonclient area mouse message is a POINTS_win32_POINTS_str structure that contains the x- and y-coordinates of the cursor hot spot. Unlike coordinates of client area mouse messages, the coordinates are given in screen coordinates rather than client coordinates. In the screen coordinate system, all points on the screen are relative to the coordinates (0,0) of the upper left corner of the screen.
The wParam parameter contains a hit-test value, a value that indicates where in the nonclient area the mouse event occurred. The following section explains the purpose of hit-test values.
+$#A The WM_NCHITTEST Message
Whenever a mouse event occurs, the system sends a WM_NCHITTEST_win32_WM_NCHITTEST message to either the window that contains the cursor hot spot or the window that has captured the mouse. Windows uses this message to determine whether to send a client area or nonclient area mouse message. An application that must receive mouse movement and mouse button messages must pass the WM_NCHITTEST message to the DefWindowProc_win32_DefWindowProc function.
The lParam parameter of the WM_NCHITTEST message contains the screen coordinates of the cursor hot spot. The DefWindowProc function examines the coordinates and returns a hit-test value that identifies the location of the hot spot. The hit-test value can be one of the following values.
Value Location of hot spot
HTBORDER In the border of a window that does not have a sizing border
HTBOTTOM In the lower horizontal border of a window
HTBOTTOMLEFT In the lower left corner of a window border
HTBOTTOMRIGHT In the lower right corner of a window border
HTCAPTION In a title bar
HTCLIENT In a client area
HTCLOSE In close button.
HTERROR On the screen background or on a dividing line between windows (same as HTNOWHERE, except that the DefWindowProc_win32_DefWindowProc function produces a system beep to indicate an error)
HTGROWBOX In a size box (same as HTSIZE)
HTHELP In Help button.
HTHSCROLL In a horizontal scroll bar
HTLEFT In the left border of a window
HTMENU In a menu
HTMAXBUTTON In Maximize button.
HTMINBUTTON In Minimize button.
HTNOWHERE On the screen background or on a dividing line between windows
HTREDUCE In a Minimize button
HTRIGHT In the right border of a window
HTSIZE In a size box (same as HTGROWBOX)
HTSYSMENU In a System menu or in a Close button in a child window
HTTOP In the upper horizontal border of a window
HTTOPLEFT In the upper left corner of a window border
HTTOPRIGHT In the upper right corner of a window border
HTTRANSPARENT In a window currently covered by another window
HTVSCROLL In the vertical scroll bar
HTZOOM In a Maximize button

If the cursor is in the client area of a window, DefWindowProc_win32_DefWindowProc returns the HTCLIENT hit-test value to the window procedure. When the window procedure returns this code to the system, Windows converts the screen coordinates of the cursor hot spot to client coordinates, and then posts the appropriate client area mouse message.
The DefWindowProc function returns one of the other hit-test values when the cursor hot spot is in a window's nonclient area. When the window procedure returns one of these hit-test values, Windows posts a nonclient area mouse message, placing the hit-test value in the message's wParam parameter and the cursor coordinates in the lParam parameter.
+$#A Window Activation
When the user clicks an inactive top-level window or the child window of a inactive top-level window, Windows sends the WM_MOUSEACTIVATE_win32_WM_MOUSEACTIVATE message (among others) to the top-level or child window. Windows sends this message after posting the WM_NCHITTEST_win32_WM_NCHITTEST message to the window, but before posting the button down message. When WM_MOUSEACTIVATE is passed to the DefWindowProc_win32_DefWindowProc function, Windows activates the top-level window and then posts the button down message to the top-level or child window.
By processing WM_MOUSEACTIVATE, a window can control whether the top-level window becomes the active window as a result of a mouse click, and whether the window that was clicked receives the subsequent button down message. It does so by returning one of the following values after processing WM_MOUSEACTIVATE.
Value Meaning
MA_ACTIVATE Activates the window and does not discard the mouse message.
MA_NOACTIVATE Does not activate the window and does not discard the mouse message.
MA_ACTIVATEANDEAT Activates the window and discards the mouse message.
MA_NOACTIVATEANDEAT Does not activate the window but discards the mouse message.

+$#A Using Mouse Input
· Tracking the mouse cursor_win32_Tracking_the_Mouse_Cursor
· Drawing lines with the mouse_win32_Drawing_Lines_with_the_Mouse
· Processing a double-click message_win32_Processing_a_Double_Click_Message
· Selecting a line of text_win32_Selecting_a_Line_of_Text

+$#A Tracking the Mouse Cursor
Windows-based applications often perform tasks that involve tracking the position of the mouse cursor. Most drawing applications, for example, track it during drawing operations, allowing the user to draw in a window's client area by dragging the mouse. Word-processing applications also track the cursor, enabling the user to select a word or block of text by clicking and dragging the mouse.
Tracking the cursor typically involves processing the WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN, WM_MOUSEMOVE_win32_WM_MOUSEMOVE, and WM_LBUTTONUP_win32_WM_LBUTTONUP messages. A window determines when to begin tracking the cursor by checking the cursor position provided in the lParam parameter of the WM_LBUTTONDOWN message. For example, a word-processing application would begin tracking the cursor only if the WM_LBUTTONDOWN message occurred while the cursor was on a line of text, but not if it was past the end of the document.
A window tracks the position of the cursor by processing the stream of WM_MOUSEMOVE messages posted to the window as the mouse moves. Processing the WM_MOUSEMOVE message typically involves a repetitive painting or drawing operation in the client area. For example, a drawing application might redraw a line repeatedly as the mouse moves. A window uses the WM_LBUTTONUP message as a signal to stop tracking the cursor.
+$#A Drawing Lines with the Mouse
The example in this section demonstrates how to track the mouse cursor. It contains portions of a window procedure that enables the user to draw lines in a window's client area by dragging the mouse.
When the window procedure receives a WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message, it captures the mouse and saves the coordinates of the cursor, using the coordinates as the starting point of the line. It also uses the ClipCursor_win32_ClipCursor function to confine the cursor to the client area during the line drawing operation.
During the first WM_MOUSEMOVE_win32_WM_MOUSEMOVE message, the window procedure draws a line from the starting point to the current position of the cursor. During subsequent WM_MOUSEMOVE messages, the window procedure erases the previous line by drawing over it with an inverted pen color. Then it draws a new line from the starting point to the new position of the cursor.
The WM_LBUTTONUP_win32_WM_LBUTTONUP message signals the end of the drawing operation. The window procedure releases the mouse capture and frees the mouse from the client area.
LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
HDC hdc; /* handle of device context */
RECT rcClient; /* client area rectangle */
POINT ptClientUL; /* client upper left corner */
POINT ptClientLR; /* client lower right corner */
static POINTS ptsBegin; /* beginning point */
static POINTS ptsEnd; /* new endpoint */
static POINTS ptsPrevEnd; /* previous endpoint */
static BOOL fPrevLine = FALSE; /* previous line flag */

switch (uMsg) {
case WM_LBUTTONDOWN:

/* Capture mouse input. */

SetCapture(hwndMain);

/*
* Retrieve the screen coordinates of the client area,
* and convert them into client coordinates.
/*

GetClientRect(hwndMain, &rcClient);
ptClientUL.x = rcClient.left;
ptClientUL.y = rcClient.top;

/*
* Add one to the right and bottom sides, because the
* coordinates retrieved by GetClientRect do not
* include the far left and lowermost pixels.
*/

ptClientLR.x = rcClient.right + 1;
ptClientLR.y = rcClient.bottom + 1;
ClientToScreen(hwndMain, &ptClientUL);
ClientToScreen(hwndMain, &ptClientLR);

/*
* Copy the client coordinates of the client area
* to the rcClient structure. Confine the mouse cursor
* to the client area by passing the rcClient structure
* to the ClipCursor function.
*/

SetRect(&rcClient, ptClientUL.x, ptClientUL.y,
ptClientLR.x, ptClientLR.y);
ClipCursor(&rcClient);

/*
* Convert the cursor coordinates into a POINTS
* structure, which defines the beginning point of the
* line drawn during a WM_MOUSEMOVE message.
*/

ptsBegin = MAKEPOINTS(lParam);
return 0;

case WM_MOUSEMOVE:

/*
* When moving the mouse, the user must hold down
* the left mouse button to draw lines.
*/

if (wParam & MK_LBUTTON) {

/*
* Retrieve a device context (DC) for the client
* area.
*/

hdc = GetDC(hwndMain);

/*
* The following function ensures that pixels of
* the previously drawn line are set to white and
* those of the new line are set to black.
*/

SetROP2(hdc, R2_NOTXORPEN);

/*
* If a line was drawn during an earlier
* WM_MOUSEMOVE message, draw over it. This erases
* the line by setting the color of its pixels to
* white.
*/

if (fPrevLine) {
MoveToEx(hdc, ptsBegin.x, ptsBegin.y,
(LPPOINT) NULL);
LineTo(hdc, ptsPrevEnd.x, ptsPrevEnd.y);
}

/*
* Convert the current cursor coordinates to a
* POINTS structure, and then draw a new line.
*/

ptsEnd = MAKEPOINTS(lParam);
MoveToEx(hdc, ptsBegin.x, ptsBegin.y,
(LPPOINT) NULL);
LineTo(hdc, ptsEnd.x, ptsEnd.y);

/*
* Set the previous line flag, save the ending
* point of the new line, and then release the DC.
*/

fPrevLine = TRUE;
ptsPrevEnd = ptsEnd;
ReleaseDC(hwndMain, hdc);
}
break;

case WM_LBUTTONUP:

/*
* The user has finished drawing the line. Reset the
* previous line flag, release the mouse cursor, and
* release the mouse capture.
*/

fPrevLine = FALSE;
ClipCursor(NULL);
ReleaseCapture();
return 0;

case WM_DESTROY:
PostQuitMessage(0);
break;

.
. /* Process other messages. */
.

+$#A Processing a Double-Click Message
To receive double-click messages, a window must belong to a window class that has the CS_DBLCLKS class style. You set this style when registering the window class, as shown in the following example.
BOOL InitApplication(hInstance)
HINSTANCE hInstance;
{
WNDCLASS wc;

wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_IBEAM);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWClass";

return RegisterClass(&wc);
}

A double-click message is always preceded by a button down message. For this reason, applications typically use a double-click message to extend a task that it began during a button down message.
+$#A Selecting a Line of Text
The example in this section is taken from a simple word-processing application. It includes code that enables the user to set the position of the caret by clicking anywhere on a line of text, and to select (highlight) a line of text by double-clicking anywhere on the line.
LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
HDC hdc; /* handle of device context */
TEXTMETRIC tm; /* font size data */
int i, j; /* loop counters */
int cCR = 0; /* count of carriage returns */
char ch; /* character from input buffer */
static int nBegLine; /* beginning of selected line */
static int nCurrentLine = 0; /* currently selected line */
static int nLastLine = 0; /* last text line */
static int nCaretPosX = 0; /* x-coordinate of caret */
static int cch = 0; /* number of characters entered */
static int nCharWidth = 0; /* exact width of a character */
static char szHilite[128]; /* text string to highlight */
static DWORD dwCharX; /* average width of characters */
static DWORD dwLineHeight; /* line height */
static POINTS ptsCursor; /* coordinates of mouse cursor */
static COLORREF crPrevText; /* previous text color */
static COLORREF crPrevBk; /* previous background color */
static PTCHAR pchInputBuf; /* address of input buffer */
static BOOL fTextSelected = FALSE; /* text-selection flag */

switch (uMsg) {
case WM_CREATE:

/* Get the metrics of the current font. */

hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);

/* Save the average character width and height. */

dwCharX = tm.tmAveCharWidth;
dwLineHeight = tm.tmHeight;

/* Allocate a buffer to store keyboard input. */

pchInputBuf = (LPSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));

return 0;

case WM_CHAR:
switch (wParam) {
case 0x08: /* backspace */
case 0x0A: /* linefeed */
case 0x1B: /* escape */
MessageBeep(0xFFFFFFFF);
return 0;

case 0x09: /* tab */

/* Convert tabs to four consecutive spaces. */

for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;

case 0x0D: /* carriage return */

/*
* Record the carriage return, and position the
* caret at the beginning of the new line.
*/

pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCurrentLine += 1;
break;

default: /* displayable character */

ch = (char) wParam;
HideCaret(hwndMain);

/*
* Retrieve the character's width, and display the
* character.
*/

hdc = GetDC(hwndMain);
GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,
&nCharWidth);
TextOut(hdc, nCaretPosX,
nCurrentLine * dwLineHeight, &ch, 1);
ReleaseDC(hwndMain, hdc);

/* Store the character in the buffer. */

pchInputBuf[cch++] = ch;

/*
* Calculate the new horizontal position of
* the caret. If the new position exceeds the
* maximum, insert a carriage return and
* reposition the caret at the beginning of
* the next line.
*/

nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwMaxCharX) {
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCurrentLine;
}

ShowCaret(hwndMain);

break;
}
SetCaretPos(nCaretPosX, nCurrentLine * dwLineHeight);
nLastLine = max(nLastLine, nCurrentLine);
break;

.
. /* Process other messages. */
.

case WM_LBUTTONDOWN:

/*
* If a line of text is currently highlighted, redraw
* the text to remove the highlighting.
*/

if (fTextSelected) {
hdc = GetDC(hwndMain);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
TextOut(hdc, 0, nCurrentLine * dwLineHeight,
szHilite, lstrlen(szHilite));
ReleaseDC(hwndMain, hdc);
ShowCaret(hwndMain);
fTextSelected = FALSE;
}

/* Save the current mouse-cursor coordinates. */

ptsCursor = MAKEPOINTS(lParam);

/*
* Determine which line the cursor is on, and save
* the line number. Do not allow line numbers greater
* than the number of the last line of text. The
* line number is later multiplied by the average height
* of the current font. The result is used to set the
* y-coordinate of the caret.
*/

nCurrentLine = min((int)(ptsCursor.y / dwLineHeight),
nLastLine);

/*
* Parse the text input buffer to find the first
* character in the selected line of text. Each
* line ends with a carriage return, so it is possible
* to count the carriage returns to find the selected
* line.
*/

cCR = 0;
nBegLine = 0;
if (nCurrentLine != 0) {
for (i = 0; (i < cch) &&
(cCR < nCurrentLine); i++) {
if (pchInputBuf[i] == 0x0D)
++cCR;
}
nBegLine = i;
}

/*
* Starting at the beginning of the selected line,
* measure the width of each character, summing the
* width with each character measured. Stop when the
* sum is greater than the x-coordinate of the cursor.
* The sum is used to set the x-coordinate of the caret.
*/

hdc = GetDC(hwndMain);
nCaretPosX = 0;
for (i = nBegLine;
(pchInputBuf[i] != 0x0D) && (i < cch); i++) {
ch = pchInputBuf[i];
GetCharWidth32(hdc, (int) ch, (int) ch, &nCharWidth);
if ((nCaretPosX + nCharWidth) > ptsCursor.x)
break;
else
nCaretPosX += nCharWidth;
}
ReleaseDC(hwndMain, hdc);

/* Set the caret to the user-selected position. */

SetCaretPos(nCaretPosX, nCurrentLine * dwLineHeight);
break;

case WM_LBUTTONDBLCLK:

/* Copy the selected line of text to a buffer. */

for (i = nBegLine, j = 0; (pchInputBuf[i] != 0x0D) &&
(i < cch); i++)
szHilite[j++] = pchInputBuf[i];
szHilite[j] = '\0';

/*
* Hide the caret, invert the background and foreground
* colors, and then redraw the selected line.
*/

HideCaret(hwndMain);
hdc = GetDC(hwndMain);
crPrevText = SetTextColor(hdc, RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc, RGB(0, 0, 0));
TextOut(hdc, 0, nCurrentLine * dwLineHeight, szHilite,
lstrlen(szHilite));
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
ReleaseDC(hwndMain, hdc);

fTextSelected = TRUE;
break;
.
. /* Process other messages. */
.
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

+$#AK Mouse Input Reference
The following functions and messages are associated with mouse input.
+$#A Mouse Input Functions
Following are the functions used with mouse input.
DragDetect_win32_DragDetect GetCapture_win32_GetCaptureGetDoubleClickTime_win32_GetDoubleClickTimemouse_event_win32_mouse_eventReleaseCapture_win32_ReleaseCaptureSetCapture_win32_SetCaptureSetDoubleClickTime_win32_SetDoubleClickTimeSwapMouseButton_win32_SwapMouseButton
+$#A Mouse Input Messages
Following are the messages used with mouse input.
WM_CAPTURECHANGED_win32_WM_CAPTURECHANGEDWM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLKWM_LBUTTONDOWN_win32_WM_LBUTTONDOWNWM_LBUTTONUP_win32_WM_LBUTTONUPWM_MBUTTONDBLCLK_win32_WM_MBUTTONDBLCLKWM_MBUTTONDOWN_win32_WM_MBUTTONDOWNWM_MBUTTONUP_win32_WM_MBUTTONUPWM_MOUSEACTIVATE_win32_WM_MOUSEACTIVATEWM_MOUSEMOVE_win32_WM_MOUSEMOVEWM_NCHITTEST_win32_WM_NCHITTESTWM_NCLBUTTONDBLCLK_win32_WM_NCLBUTTONDBLCLKWM_NCLBUTTONDOWN_win32_WM_NCLBUTTONDOWNWM_NCLBUTTONUP_win32_WM_NCLBUTTONUPWM_NCMBUTTONDBLCLK_win32_WM_NCMBUTTONDBLCLKWM_NCMBUTTONDOWN_win32_WM_NCMBUTTONDOWNWM_NCMBUTTONUP_win32_WM_NCMBUTTONUPWM_NCMOUSEMOVE_win32_WM_NCMOUSEMOVEWM_NCRBUTTONDBLCLK_win32_WM_NCRBUTTONDBLCLKWM_NCRBUTTONDOWN_win32_WM_NCRBUTTONDOWNWM_NCRBUTTONUP_win32_WM_NCRBUTTONUPWM_RBUTTONDBLCLK_win32_WM_RBUTTONDBLCLKWM_RBUTTONDOWN_win32_WM_RBUTTONDOWNWM_RBUTTONUP_win32_WM_RBUTTONUP

+$#AK Painting and Drawing
This overview describes how Windows manages output to the screen and explains what applications must do to draw in a window. In particular, this overview describes display device contexts (or, more simply, display DCs) and how to prepare and use them. The overview does not explain how to use graphics device interface (GDI) functions to generate output, nor does it explain how to print.
+$#A About Painting and Drawing
Nearly all applications use the screen to display the data they manipulate. An application paints images, draws figures, and writes text so that the user can view data as it is created, edited, and printed. Microsoft® Windows® provides a rich environment for painting and drawing, but, because Windows is a multitasking operating system, applications must cooperate with one another when accessing the screen.
To keep all applications functioning smoothly and cooperatively, Windows manages all output to the screen. Applications use windows as their primary output device rather than the screen itself. Windows supplies display DCs that uniquely correspond to the windows. Applications use display DCs to direct their output to the given windows. Drawing in a window (directing output to it) prevents an application from interfering with the output of other applications and allows applications to coexist with one another while still taking full advantage of the graphics capabilities of Windows.
+$#A When to Draw in a Window
An application draws in a window at a variety of times: when first creating a window, when changing the size of the window, when moving the window from behind another window, when minimizing or maximizing the window, when displaying data from an opened file, and when scrolling, changing, or selecting a portion of the displayed data.
Windows manages actions such as moving and sizing a window. If an action affects the content of the window, Windows marks the affected portion of the window as ready for updating and, at the next opportunity, sends a WM_PAINT_win32_WM_PAINT message to the window procedure of the window. The message is a signal to the application to determine what must be updated and to carry out the necessary drawing.
Some actions are managed by the application, such as displaying open files and selecting displayed data. For these actions, an application can mark for updating the portion of the window affected by the action, causing a WM_PAINT message to be sent at the next opportunity. If an action requires immediate feedback, the application can draw while the action takes place, without waiting for WM_PAINT. For example, a typical application highlights the area the user selects rather than waiting for the next WM_PAINT message to update the area.
In all cases, an application can draw in a window as soon as it is created. To draw in the window, the application must first retrieve a handle of a display DC for the window. Ideally, an application carries out most of its drawing operations during the processing of WM_PAINT messages. In this case, the application retrieves a display DC by calling the BeginPaint_win32_BeginPaint function. If an application draws at any other time, such as from within WinMain_win32_WinMain or during processing of keyboard or mouse messages, it calls the GetDC_win32_GetDC or GetDCEx_win32_GetDCEx function to retrieve the display DC.
+$#A The WM_PAINT Message
Typically, an application draws in a window in response to a WM_PAINT_win32_WM_PAINT message. Windows sends this message to a window procedure when changes to the window have altered the content of the client area. Windows sends the message only if there are no other messages in the application message queue.
Upon receiving a WM_PAINT message, an application can call BeginPaint_win32_BeginPaint to retrieve the display DC for the client area and use it in calls to GDI functions to carry out whatever drawing operations are necessary to update the client area. After completing the drawing operations, the application calls the EndPaint_win32_EndPaint function to release the display DC.
Before BeginPaint returns the display DC, Windows prepares the DC for the given window. It first sets the clipping region for the DC to be equal to the intersection of the portion of the window that needs updating and the portion that is visible to the user. Only those portions of the window that have changed are redrawn. Attempts to draw outside this region are clipped and do not appear on the screen.
Windows can also send WM_NCPAINT_win32_WM_NCPAINT and WM_ERASEBKGND_win32_WM_ERASEBKGND messages to the window procedure before BeginPaint returns. These messages direct the application to draw the nonclient area and window background. The nonclient area is the part of a window that is outside of the client area. The area includes features such as the title bar, System menu (also known as the Control menu), and scroll bars. Most applications rely on the default window function, DefWindowProc_win32_DefWindowProc, to draw this area and therefore pass the WM_NCPAINT message to this function. The window background is the color or pattern a window is filled with before other drawing operations begin. The background covers any images previously in the window or on the screen under the window. If a window belongs to a window class having a class background brush, the DefWindowProc function draws the window background automatically.
BeginPaint fills a PAINTSTRUCT_win32_PAINTSTRUCT_str structure with information such as the dimensions of the portion of the window to be updated and a flag indicating whether the window background has been drawn. The application can use this information to optimize drawing. For example, it can use the dimensions of the update region, specified by the rcPaint member, to limit drawing to only those portions of the window that need updating. If an application has very simple output, it can ignore the update region and draw in the entire window, relying on Windows to discard (clip) any unneeded output. Because the system clips drawing that extends outside the clipping region, only drawing that is in the update region is visible.
BeginPaint_win32_BeginPaint sets the update region of a window to NULL. This clears the region, preventing it from generating subsequent WM_PAINT_win32_WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message.
After the application finishes drawing, it should call EndPaint_win32_EndPaint. For most windows, EndPaint releases the display DC, making it available to other windows. EndPaint also shows the caret, if it was previously hidden by BeginPaint. BeginPaint hides the caret to prevent drawing operations from corrupting it.
+$#A The Update Region
The update region identifies the portion of a window that is out-of-date or invalid and in need of redrawing. Windows uses the update region to generate WM_PAINT_win32_WM_PAINT messages for applications and to minimize the time applications spend bringing the contents of their windows up to date. Windows adds only the invalid portion of the window to the update region, requiring only that portion to be drawn.
When Windows determines that a window needs updating, it sets the dimensions of the update region to the invalid portion of the window. Setting the update region does not immediately cause the application to draw. Instead, the application continues retrieving messages from the application message queue until no messages remain. Windows then checks the update region, and if the region is not empty (non-NULL), it sends a WM_PAINT message to the window procedure.
An application can use the update region to generate its WM_PAINT messages. For example, an application that loads data from open files typically sets the update region while loading so that new data is drawn during processing of the next WM_PAINT message. In general, an application should not draw at the time its data changes, but route all drawing operations through the WM_PAINT message.
+$#A Invalidating and Validating the Update Region
An application invalidates a portion of a window and sets the update region by using the InvalidateRect_win32_InvalidateRect or InvalidateRgn_win32_InvalidateRgn function. These functions add the specified rectangle or region (given in client coordinates) to the update region, combining the rectangle or region with anything Windows or the application may have previously added to the update region.
The InvalidateRect and InvalidateRgn functions do not generate WM_PAINT_win32_WM_PAINT messages. Instead, Windows accumulates the changes made by these functions and its own changes while a window processes other messages in its message queue. By accumulating changes, a window processes all changes at once instead of updating bits and pieces one step at a time.
The ValidateRect_win32_ValidateRect and ValidateRgn_win32_ValidateRgn functions validate a portion of the window by removing a specified rectangle or region from the update region. These functions are typically used when the window has updated a specific part of the screen in the update region before receiving the WM_PAINT message.
+$#A Retrieving the Update Region
The GetUpdateRect_win32_GetUpdateRect and GetUpdateRgn_win32_GetUpdateRgn functions retrieve the current update region for the window. GetUpdateRect retrieves the smallest rectangle (in client coordinates) that encloses the entire update region. GetUpdateRgn retrieves the update region itself. These functions can be used to calculate the current size of the update region to determine where to carry out a drawing operation.
BeginPaint_win32_BeginPaint also retrieves the dimensions of the smallest rectangle enclosing the current update region, copying the dimensions to the rcPaint member in the PAINTSTRUCT_win32_PAINTSTRUCT_str structure. Because BeginPaint validates the update region, any call to GetUpdateRect and GetUpdateRgn immediately after a call to BeginPaint returns an empty update region.
+$#A Excluding the Update Region
The ExcludeUpdateRgn_win32_ExcludeUpdateRgn function excludes the update region from the clipping region for the display DC. This function is useful when drawing in a window other than when a WM_PAINT_win32_WM_PAINT message is processing. It prevents drawing in the areas that will be updated during the next WM_PAINT message.
+$#A Synchronous and Asynchronous Drawing
Most drawing carried out during processing of the WM_PAINT_win32_WM_PAINT message is asynchronous; that is, there is a delay between the time a portion of the window is invalidated and the time WM_PAINT is sent. During the delay, the application typically retrieves messages from the queue and carries out other tasks. The reason for the delay is that Windows generally treats drawing in a window as a low-priority operation and works as though user-input messages and messages that may affect the position or size of a window will be processed before WM_PAINT.
In some cases, it is necessary for an application to draw synchronously ¾ that is, draw in the window immediately after invalidating a portion of the window. A typical application draws its main window immediately after creating the window to signal the user that the application has started successfully. Windows draws some control windows synchronously, such as buttons, because such windows serve as the focus for user input. Although any window with a simple drawing routine can be drawn synchronously, all such drawing should be done quickly and should not interfere with the application's ability to respond to user input.
The UpdateWindow_win32_UpdateWindow and RedrawWindow_win32_RedrawWindow functions allow for synchronous drawing. UpdateWindow sends a WM_PAINT message directly to the window if the update region is not empty. RedrawWindow also sends a WM_PAINT message, but gives the application greater control over how to draw the window, such as whether to draw the nonclient area and window background or whether to send the message regardless of whether the update region is empty. These functions send the WM_PAINT message directly to the window, regardless of the number of other messages in the application message queue.
Any window requiring time-consuming drawing operations should be drawn asynchronously to prevent pending messages from being blocked as the window is drawn. Also, any application that frequently invalidates small portions of a window should allow these invalid portions to consolidate into a single asynchronous WM_PAINT message, rather than a series of synchronous WM_PAINT messages.
+$#A Drawing Without the WM_PAINT Message
Although applications carry out most drawing operations while the WM_PAINT_win32_WM_PAINT message is processing, it is sometimes more efficient for an application to draw directly in a window without relying on the WM_PAINT message. This can be useful when the user needs immediate feedback, such as when selecting text and dragging or sizing an object. In such cases, the application usually draws while processing keyboard or mouse messages.
To draw in a window without using a WM_PAINT message, the application uses the GetDC_win32_GetDC or GetDCEx_win32_GetDCEx function to retrieve a display DC for the window. With the display DC, the application can draw in the window and avoid intruding into other windows. When the application has finished drawing, it calls the ReleaseDC_win32_ReleaseDC function to release the display DC for use by other applications.
When drawing without using a WM_PAINT message, the application usually does not invalidate the window. Instead, it draws in such a fashion that it can easily restore the window and remove the drawing. For example, when the user selects text or an object, the application typically draws the selection by inverting whatever is already in the window. The application can remove the selection and restore the original contents of the window by simply inverting again.
The application is responsible for carefully managing any changes it makes to the window. In particular, if an application draws a selection and an intervening WM_PAINT message occurs, the application must ensure that any drawing done during the message does not corrupt the selection. To avoid this, many applications remove the selection, carry out usual drawing operations, and then restore the selection when drawing is complete.
+$#A Window Coordinate System
The coordinate system for a window is based on the coordinate system of the display device. The basic unit of measure is the device unit (typically, the pixel). Points on the screen are described by x- and y-coordinate pairs. The x-coordinates increase to the right; y-coordinates increase from top to bottom. The origin (0,0) for the system depends on the type of coordinates being used.
Windows and applications specify the position of a window on the screen in screen coordinates. For screen coordinates, the origin is the upper left corner of the screen. The full position of a window is often described by a RECT_win32_RECT_str structure containing the screen coordinates of two points that define the upper left and lower right corners of the window.
Windows and applications specify the position of points in a window by using client coordinates. The origin in this case is the upper left corner of the window or client area. Client coordinates ensure that an application can use consistent coordinate values while drawing in the window, regardless of the position of the window on the screen.
The dimensions of the client area are also described by a RECT structure that contains client coordinates for the area. In all cases, the upper left coordinate of the rectangle is included in the window or client area, while the lower right coordinate is excluded. Graphics operations in a window or client area are excluded from the right and lower edges of the enclosing rectangle.
Occasionally, applications may be required to map coordinates in one window to those of another window. An application can map coordinates by using the MapWindowPoints_win32_MapWindowPoints function. If one of the windows is the desktop window, the function effectively converts screen coordinates to client coordinates and vice versa; the desktop window is always specified in screen coordinates.
+$#A Window Regions
In addition to the update region, every window has a visible region that defines the window portion visible to the user. The system changes the visible region for the window whenever the window changes size or whenever another window is moved such that it obscures or exposes a portion of the window. Applications cannot change the visible region directly, but Windows automatically uses the visible region to create the clipping region for any display DC retrieved for the window.
The clipping region determines where the system permits drawing. When the application retrieves a display DC using the BeginPaint_win32_BeginPaint, GetDC_win32_GetDC, or GetDCEx_win32_GetDCEx function, the system sets the clipping region for the DC to the intersection of the visible region and the update region. Applications can change the clipping region by using functions such as SetWindowRgn_win32_SetWindowRgn, SelectClipPath_win32_SelectClipPath and SelectClipRgn_win32_SelectClipRgn, to further limit drawing to a particular portion of the update area.
The WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles further specify how Windows calculates the visible region for a window. If a window has one or both of these styles, the visible region excludes any child window or sibling windows (windows having the same parent window). Therefore, drawing that would otherwise intrude in these windows will always be clipped.
+$#A Window Background
The window background is the color or pattern used to fill the client area before a window begins drawing. The window background covers whatever was on the screen before the window was moved there, erasing existing images and preventing the application's new output from being mixed with unrelated information.
Windows paints the background for a window or gives the window the opportunity to do so by sending it a WM_ERASEBKGND_win32_WM_ERASEBKGND message when the application calls BeginPaint_win32_BeginPaint. If an application does not process the message but passes it to DefWindowProc_win32_DefWindowProc, Windows erases the background by filling it with the pattern in the background brush specified by the window's class. If the brush is not valid or the class has no background brush, Windows sets the fErase member in the PAINTSTRUCT_win32_PAINTSTRUCT_str structure BeginPaint returns, but carries out no other action. The application then has a second chance to draw the window background, if necessary.
If it processes WM_ERASEBKGND, the application should use the message's wParam parameter to draw the background. This parameter contains the handle of the display DC for the window. After drawing the background, the application should return a nonzero value. This ensures that BeginPaint does not erroneously set the fErase member of the PAINTSTRUCT structure to a nonzero value (indicating the background should be erased) when the application processes the subsequent WM_PAINT_win32_WM_PAINT message.
An application can define a class background brush by assigning a brush handle or a system color value to the hbrBackground member of the WNDCLASS_win32_WNDCLASS_str structure when registering the class with the RegisterClass_win32_RegisterClass function. The GetStockObject_win32_GetStockObject or CreateSolidBrush_win32_CreateSolidBrush function can be used to create a brush handle. A system color value can be one of those defined for the SetSysColors_win32_SetSysColors function. (The value must be increased by one before it is assigned to the member.)
An application can process the WM_ERASEBKGND message even though a class background brush is defined. This is typical in applications that enable the user to change the window background color or pattern for a given window without affecting other windows in the class. In such cases, the application must not pass the message to DefWindowProc.
It is not necessary for an application to align brushes, because the system draws the brush using the window origin as the point of reference. Given this, the user can move the window without affecting the alignment of pattern brushes.
+$#A Minimized Windows
Windows reduces an application's main window (overlapping style) to a minimized window when the user chooses the Minimize command from the System menu or the application calls the ShowWindow_win32_ShowWindow function and specifies a value such as SW_MINIMIZE. Minimizing a window speeds up system performance by reducing the amount of work an application must do when updating its main window.
For a typical application, Windows draws an icon, called the class icon, when the window is minimized, labeling the icon with the name of the window. The class icon, a static image that represents the application, is specified by the application when it registers the window class. The application assigns the handle of the class icon to the hIcon member of WNDCLASS_win32_WNDCLASS_str before calling RegisterClass_win32_RegisterClass. The application can use the LoadIcon_win32_LoadIcon function to retrieve the icon handle.
Before drawing the class icon, Windows sends a WM_ICONERASEBKGND_win32_WM_ICONERASEBKGND message to the window procedure, enabling the application to prepare the background for drawing the icon by setting the best possible background colors for the icon. This is useful for applications that combine the icon with the current background colors. If the application processes the message, it should use the display DC provided with the message to draw the background (the wParam parameter contains the handle of the display DC). If the application does not process the WM_ICONERASEBKGND message, it should pass the message to DefWindowProc_win32_DefWindowProc; the function fills the icon area with the current desktop color and pattern. After sending the WM_ICONERASEBKGND message, Windows sends the WM_PAINTICON_win32_WM_PAINTICON message to the window procedure. The application should immediately forward this internal message to DefWindowProc.
Windows does not require that a window class have a class icon. If an application sets the hIcon member of WNDCLASS to NULL, a class icon is not defined. In this case, Windows sends the WM_ERASEBKGND_win32_WM_ERASEBKGND message (instead of WM_ICONERASEBKGND) to a window of the class whenever the window must paint the icon background. Windows then sends a WM_PAINT_win32_WM_PAINT message and the application draws an icon or another image representing the minimized window. In such cases, the application must determine when the window is minimized and draw accordingly. It can do so by calling the IsIconic_win32_IsIconic function. If the function returns TRUE, the window is minimized. If an application has no class icon and fails to process WM_ERASEBKGND and WM_PAINT, the area that Windows reserves for the application's icon will contain whatever was previously on the screen.
+$#A Window Size
Windows changes the size of a window when the user chooses System menu commands, such as Size and Maximize, or when the application calls functions, such as the SetWindowPos_win32_SetWindowPos function. When a window changes size, Windows assumes that the contents of the previously exposed portion of the window are not affected and need not be redrawn. Windows invalidates only the newly exposed portion of the window, which saves time when the eventual WM_PAINT_win32_WM_PAINT message is processed by the application. In this case, WM_PAINT is not generated when the size of the window is reduced.
For some windows, any change to the size of the window invalidates the contents. For example, a clock application that adapts the face of the clock to fit neatly within its window must redraw the clock whenever the window changes size. To force Windows to invalidate the entire client area of the window when a vertical, horizontal, or both vertical and horizontal change is made, an application must specify the CS_VREDRAW or CS_HREDRAW style, or both, when registering the window class. Any window belonging to a window class having these styles is invalidated each time the user or the application changes the size of the window.
+$#A Nonclient Area
Windows sends a WM_NCPAINT_win32_WM_NCPAINT message to the window whenever a part of the nonclient area of the window, such as the title bar, menu bar, or window frame, must be updated. Windows may also send other messages to direct a window to update a portion of its client area; for example, when a window becomes active or inactive, it sends the WM_NCACTIVATE_win32_WM_NCACTIVATE message to update its title bar. In general, processing these messages for standard windows is not recommended, because the application must be able to draw all the required parts of the nonclient area for the window. For this reason, most applications pass these messages to DefWindowProc_win32_DefWindowProc for default processing.
An application that creates custom nonclient areas for its windows must process these messages. When doing so, the application must use a window DC to carry out drawing in the window. The window DC enables the application to draw in all portions of the window, including the nonclient area. An application retrieves a window DC by using the GetWindowDC_win32_GetWindowDC or GetDCEx_win32_GetDCEx function and, when drawing is complete, must release the window DC by using the ReleaseDC_win32_ReleaseDC function.
Windows maintains an update region for the nonclient area. When an application receives a WM_NCPAINT message, the wParam parameter contains the handle of a region defining the dimensions of the update region. The application can use the handle to combine the update region with the clipping region for the window DC. Windows does not automatically combine the update region when retrieving the window DC unless the application uses GetDCEx and specifies both the region handle and the DCX_INTERSECTRGN flag. If the application does not combine the update region, only drawing operations that would otherwise extend outside the window are clipped. The application is not responsible for clearing the update region, regardless of whether it uses the region.
If an application processes the WM_NCACTIVATE message, after processing it must return TRUE to direct Windows to complete the change of active window. If the window is minimized when the application receives the WM_NCACTIVATE message, it should pass the message to DefWindowProc. In such cases, the default function redraws the label for the icon.
+$#A Child Windows
A child window is a window with the WS_CHILD or WS_CHILDWINDOW style. Like other window styles, child windows receive WM_PAINT_win32_WM_PAINT messages to prompt updating. Each child window has an update region, which either Windows or the application can set to generate eventual WM_PAINT messages.
A child window's update and visible regions are affected by the child's parent window; this is not true for windows of other styles. Windows often sets the child window's update region when it sets the parent window's update region, causing the child window to receive WM_PAINT messages when the parent window receives them. Windows limits the location of the child window's visible region to within the client area of the parent window and clips any portion of the child window moved outside the parent window.
Windows sets the update region for a child window whenever part of the parent window's update region includes a portion of the child window. In such cases, Windows first sends a WM_PAINT message to the parent window and then sends a message to the child window, allowing the child to restore any portions of the window that the parent may have drawn over.
Windows does not set the parent's update region when the child's is set. An application cannot generate a WM_PAINT message for the parent window by invalidating the child window. Similarly, an application cannot generate a WM_PAINT message for the child by invalidating a portion of the parent's client area that lies entirely under the child window. In such cases, neither window receives a WM_PAINT message.
An application can prevent a child window's update region from being set when the parent window's is set by specifying the WS_CLIPCHILDREN style when creating the parent window. When this style is set, Windows excludes the child windows from the parent's visible region and therefore ignores any portion of the update region that may contain the child windows. When the application paints in the parent window, any drawing that would cover the child window is clipped, making a subsequent WM_PAINT_win32_WM_PAINT message to the child window unnecessary.
The update and visible regions of a child window are also affected by the child window's siblings. Sibling windows are any windows that have a common parent window. If sibling windows overlap, then setting the update region for one affects the update region of another, causing WM_PAINT messages to be sent to both windows. Sibling windows receive WM_PAINT messages in the reverse order of their position in the Z order. Given this, the window highest in the Z order (on the top) receives its WM_PAINT message last, and vice versa.
Sibling windows are not automatically clipped. One sibling can draw over another overlapping sibling even if the window that is drawing has a lower position in the Z order. An application can prevent this by specifying the WS_CLIPSIBLINGS style when creating the windows. When this style is set, Windows excludes all portions of an overlapping sibling window from a window's visible region if the overlapping sibling window has a higher position in the Z order.

Note The update and visible regions for windows that have the WS_POPUP or WS_POPUPWINDOW style are not affected by their parent windows.

+$#A About Display Device Contexts
A display DC is a device context, created by Windows, that an application uses to paint and draw a window. Windows prepares each display DC for output to a window, setting the drawing objects, colors, and modes for the window instead of for the display device. When the application supplies the display DC through calls to GDI functions, GDI uses the information in the context to generate output in the given window without intruding on other windows or other parts of the screen.
Windows provides five kinds of display DC: common, class, parent, private, and window. The common, class, and private DCs permit drawing in the client area of a given window. The parent and window DCs permit drawing anywhere in the window. Although the parent DC also permits drawing in the parent window, it is not intended to be used in this way.
Windows supplies a common, class, parent, or private DC to a window based on the type of display DC specified in that window's class style. Windows supplies a window DC only when the application explicitly requests one ¾ for example, by calling the GetWindowDC_win32_GetWindowDC or GetDCEx_win32_GetDCEx function. In all cases, an application can use the WindowFromDC_win32_WindowFromDC function to determine which window a display DC currently represents.
+$#A Display Device Context Cache
Windows maintains a cache of display DCs that it uses for common, parent, and window DCs. Windows retrieves a DC from the cache whenever an application calls the GetDC_win32_GetDC or BeginPaint_win32_BeginPaint function; Windows returns the DC to the cache when the application subsequently calls the ReleaseDC_win32_ReleaseDC or EndPaint_win32_EndPaint function.
In Windows 3.x, the cache contains five display DCs, but only five DCs from the cache can be active at a time. To ensure that other applications have access to these DCs, an application must release a device context immediately after using it. Failure to do so eventually causes the application to fail.
There is no predetermined limit on the amount of DCs that a cache can hold; Windows creates a new display DC for the cache if none is available. Given this, a Win32-based application can have more than five active DCs from the cache at a time. However, the application must continue to release these DCs after use. Because new display DCs for the cache are allocated in the application's heap space, failing to release the DCs eventually consumes all available heap space. Windows indicates this failure by returning an error when it cannot allocate space for the new DC. Other functions unrelated to the cache may also return errors.

Portability To minimize porting efforts, applications that use common DCs should be limited to no more than five and ensure that a common DC is released as soon as possible after it is used.

+$#A Display Device Context Defaults
Upon first creating a display DC, Windows assigns default values for the attributes (that is, drawing objects, colors, and modes) that comprise the DC. The following table shows the default values for the attributes of a display DC.
Attribute Default value
Background color Background color setting from Windows Control Panel (typically, white).
Background mode OPAQUE.
Bitmap None.
Brush WHITE_BRUSH.
Brush origin (0,0).
Clipping region Entire window or client area with the update region clipped, as appropriate. Child and pop-up windows in the client area may also be clipped.
Palette DEFAULT_PALETTE.
Current pen position (0,0).
Device origin Upper left corner of the window or the client area.
Drawing mode R2_COPYPEN.
Font SYSTEM_FONT (SYSTEM_FIXED_FONT for applications written to run with Windows versions 3.0 and earlier).
Intercharacter spacing 0.
Mapping mode MM_TEXT.
Pen BLACK_PEN.
Polygon_win32_Polygon-fill mode ALTERNATE.
Stretch mode BLACKONWHITE.
Text color Text color setting from Control Panel (typically, black).
Viewport extent (1,1).
Viewport origin (0,0).
Window extent (1,1).
Window origin (0,0).

An application can modify the values of the display DC attributes by using selection and attribute functions, such as SelectObject_win32_SelectObject, SetMapMode_win32_SetMapMode, and SetTextColor_win32_SetTextColor. For example, an application can modify the default units of measure in the coordinate system by using SetMapMode to change the mapping mode.
Changes to the attribute values of a common, parent, or window DC are not permanent. When an application releases these DCs, the current selections, such as mapping mode and clipping region, are lost as the context is returned to the cache. Changes to a class or private DC persist indefinitely. To restore them to their original defaults, an application must explicitly set each attribute.
+$#A Common Display Device Contexts
A common DC is used for drawing in the client area of the window. The Win32 API provides a common DC by default for any window whose window class does not explicitly specify a display DC style. Common DCs are typically used with windows that can be drawn without extensive changes to the DC attributes. Common DCs are convenient because they do not require additional memory or system resources, but they can be inconvenient if the application must set up many attributes before using them.
Windows retrieves all common DCs from the display DC cache. An application can retrieve a common DC immediately after the window is created. Because the common DC is from the cache, the application must always release the DC as soon as possible after drawing. After the common DC is released, it is no longer valid and the application must not attempt to draw with it. To draw again, the application must retrieve a new common DC, and continue to retrieve and release a common DC each time it draws in the window. If the application retrieves the DC handle by using the GetDC_win32_GetDC function, it must use the ReleaseDC_win32_ReleaseDC function to release the handle. Similarly, for each BeginPaint_win32_BeginPaint function, the application must use a corresponding EndPaint_win32_EndPaint function.
When the application retrieves the DC, Windows adjusts the origin so that it aligns with the upper left corner of the client area. It also sets the clipping region so that output to the DC is clipped to the client area. Any output that would otherwise appear outside the client area is clipped. If the application retrieves the common DC by using BeginPaint, Windows also includes the update region in the clipping region to further restrict the output.
When an application releases a common DC, Windows restores the default values for the attributes of the DC. An application that modifies attribute values must do so each time it retrieves a common DC. Releasing the DC releases any drawing objects the application may have selected into it, so the application need not release these objects before releasing the DC. In all cases, an application must never assume that the common DC retains nondefault selections after being released.
+$#A Private Display Device Contexts
A private DC enables an application to avoid retrieving and initializing a display DC each time the application must draw in a window. Private DCs are useful for windows that require many changes to the values of the attributes of the DC to prepare it for drawing. Private DCs reduce the time required to prepare the DC and therefore the time needed to carry out drawing in the window.
An application directs Windows to create a private DC for a window by specifying the CS_OWNDC style in the window class. Windows creates a unique private DC each time it creates a new window belonging to the class. Initially, the private DC has the same default values for attributes as a common DC, but the application can modify these at any time. Windows preserves changes to the DC for the life of the window or until the application makes additional changes.
An application can retrieve the handle of the private DC by using the GetDC_win32_GetDC function any time after the window is created. The application must retrieve the handle only once. Thereafter, it can keep and use the handle any number of times. Because a private DC is not part of the display DC cache, an application need never release the DC by using the ReleaseDC_win32_ReleaseDC function.
Windows automatically adjusts the DC to reflect changes to the window, such as moving or sizing. This ensures that any overlapping windows are always properly clipped; that is, no action is required by the application to ensure clipping. However, Windows does not revise the DC to include the update region. Therefore, when processing a WM_PAINT_win32_WM_PAINT message, the application must incorporate the update region either by calling BeginPaint_win32_BeginPaint or by retrieving the update region and intersecting it with the current clipping region. If the application does not call BeginPaint, it must explicitly validate the update region by using the ValidateRect_win32_ValidateRect or ValidateRgn_win32_ValidateRgn function. If the application does not validate the update region, the window receives an endless series of WM_PAINT messages.
Because BeginPaint hides the caret if a window is showing it, an application that calls BeginPaint should also call the EndPaint_win32_EndPaint function to restore the caret. EndPaint has no other effect on a private DC.
Although a private DC is convenient to use, it is expensive in terms of system resources, requiring 800 or more bytes to store. Private DCs are recommended when performance considerations outweigh storage costs.
Windows includes the private DC when sending the WM_ERASEBKGND_win32_WM_ERASEBKGND message to the application. The current selections of the private DC, including mapping mode, are in effect when the application or Windows processes these messages. To avoid undesirable effects, Windows uses logical coordinates when erasing the background; for example, it uses the GetClipBox_win32_GetClipBox function to retrieve the logical coordinates of the area to erase and passes these coordinates to the FillRect_win32_FillRect function. Applications that process these messages can use similar techniques. Windows supplies a window DC with the WM_ICONERASEBKGND_win32_WM_ICONERASEBKGND message regardless of whether the corresponding window has a private DC.
An application can use the GetDCEx_win32_GetDCEx function to force Windows to return a common DC for the window that has a private DC. This is useful for carrying out quick touch-ups to a window without changing the current values of the attributes of the private DC.
+$#A Class Display Device Contexts
By using a class DC, an application can use a single display DC for every window belonging to a given class. Class DCs are often used with control windows that are drawn using the same attribute values. Like private DCs, class DCs minimize the time required to prepare a DC for drawing.
Windows supplies a class DC for a window if it belongs to a window class having the CS_CLASSDC style. Windows creates the DC when creating the first window belonging to the class and then uses the same DC for all subsequently created windows in the class. Initially, the class DC has the same default values for attributes as a common DC, but the application can modify these at any time. Windows preserves all changes, except for the clipping region and device origin, until the last window in the class has been destroyed. A change made for one window applies to all windows in that class.
An application can retrieve the handle for the class DC by using the GetDC_win32_GetDC function any time after the first window has been created. The application can keep and use the handle without releasing it because the class DC is not part of the display DC cache. If the application creates another window in the same window class, the application must retrieve the class DC again. Retrieving the DC sets the correct device origin and clipping region for the new window. After the application retrieves the class DC for a new window in the class, the DC can no longer be used to draw in the original window without again retrieving it for that window. In general, each time it must draw in a window, an application must explicitly retrieve the class DC for the window.
Applications that use class DCs should always call BeginPaint_win32_BeginPaint when processing a WM_PAINT_win32_WM_PAINT message. The function sets the correct device origin and clipping region for the window, and incorporates the update region. The application should also call EndPaint_win32_EndPaint to restore the caret if BeginPaint hid it. EndPaint has no other effect on a class DC.
Windows passes the class DC when sending the WM_ERASEBKGND_win32_WM_ERASEBKGND message to the application, permitting the current attribute values to affect any drawing carried out by the application or Windows when processing this message. Windows supplies a window DC with the WM_ICONERASEBKGND_win32_WM_ICONERASEBKGND message regardless of whether the corresponding window has a class DC. As it could with a window having a private DC, an application can use GetDCEx_win32_GetDCEx to force Windows to return a common DC for the window that has a class DC.
Using class DCs is not recommended.
+$#A Window Display Device Contexts
A window DC enables an application to draw anywhere in a window, including the nonclient area. Window DCs are typically used by applications that process the WM_NCPAINT_win32_WM_NCPAINT and WM_NCACTIVATE_win32_WM_NCACTIVATE messages for windows with custom nonclient areas. Using a window DC is not recommended for any other purpose.
An application can retrieve a window DC by using the GetWindowDC_win32_GetWindowDC or GetDCEx_win32_GetDCEx function with the DCX_WINDOW option specified. The function retrieves a window DC from the display DC cache. A window that uses a window DC must release it after drawing by using the ReleaseDC_win32_ReleaseDC function as soon as possible. Window DCs are always from the cache; the CS_OWNDC and CS_CLASSDC class styles do not affect the DC.
When an application retrieves a window DC, Windows sets the device origin to the upper left corner of the window instead of the upper left corner of the client area. It also sets the clipping region to include the entire window, not just the client area. Windows sets the current attribute values of a window DC to the same default values as a common DC. An application can change the attribute values, but Windows does not preserve any changes when the DC is released.
+$#A Parent Display Device Contexts
A parent DC enables an application to minimize the time necessary to set up the clipping region for a window. An application typically uses parent DCs to speed up drawing for control windows without requiring a private or class DC. For example, Windows uses parent DCs for push button and edit controls. Parent DCs are intended for use with child windows only, never with top-level or pop-up windows.
Windows supplies a parent DC for any window belonging to a window class specifying the CS_PARENTDC style. An application can retrieve the parent DC by using GetDC_win32_GetDC immediately after the window is created. Windows always retrieves the parent DC from the display DC cache even if the parent uses a private or class DC, so the application must release the DC by using ReleaseDC_win32_ReleaseDC as soon as possible after drawing. When processing a WM_PAINT_win32_WM_PAINT message, the application must retrieve the parent DC by using BeginPaint_win32_BeginPaint and release it by using EndPaint_win32_EndPaint.
When a parent DC is retrieved, Windows searches the cache for the display DC previously used by the parent window. In most cases, the parent window retrieves and releases a display DC before the child window attempts to retrieve one, so the parent DC is usually in the cache. If Windows finds the display DC, Windows uses it and its corresponding clipping region, which was previously calculated for the parent DC. The time saved is in skipping the calculation required to define the clipping region. If Windows does not find the parent DC, it retrieves a common DC for the window and calculates the clipping region for the window rather than for the parent window.
Windows sets the device origin for the parent DC to the upper left corner of the child window. Attribute values set by the parent window are not preserved for the child window; for example, the parent window cannot set the brush for its child windows. The only property preserved is the clipping region. The window must clip its own output to the limits of the window. Because the clipping region for the parent DC is identical to the parent window, the child window can potentially draw over the entire parent window, but the parent DC must not be used in this way.
Windows ignores the CS_PARENTDC style if the parent window uses a private or class DC, if the parent window clips its child windows, or if the child window clips its child windows or sibling windows. In these cases, Windows always retrieves a common DC for the window.
+$#A Window Update Lock
A window update lock is a temporary suspension of drawing in a window. Windows uses the lock to prevent other windows from drawing over the tracking rectangle whenever the user moves or sizes a window. Applications can use the lock to prevent drawing if they carry out similar moving or sizing operations with their own windows.
An application uses the LockWindowUpdate_win32_LockWindowUpdate function to set or clear a window update lock, specifying the window to lock. The lock applies to the given window and all of its child windows. When the lock is set, the GetDC_win32_GetDC and BeginPaint_win32_BeginPaint functions return a display DC with a visible region that is empty. Given this, the application can continue to draw in the window, but all output is clipped. The lock persists until the application clears it by calling LockWindowUpdate, specifying NULL for the window. Although LockWindowUpdate forces a window's visible region to be empty, the function does not make the given window invisible and does not clear the WS_VISIBLE style bit.
After the lock is set, the application can use the GetDCEx_win32_GetDCEx function, with the DCX_LOCKWINDOWUPDATE value, to retrieve a display DC to draw over the locked window. This allows the application to draw a tracking rectangle when processing keyboard or mouse messages. Windows uses this method when the user moves and sizes windows. GetDCEx retrieves the display DC from the display DC cache, so the application must release the DC as soon as possible after drawing.
While a window update lock is set, the system creates an accumulated bounding rectangle for each locked window. When the lock is cleared, Windows uses this bounding rectangle to set the update region for the window and its child windows, forcing an eventual WM_PAINT_win32_WM_PAINT message. If the accumulated bounding rectangle is empty (that is, if no drawing has occurred while the lock was set), the update region is not set.
+$#A Accumulated Bounding Rectangle
The accumulated bounding rectangle is the smallest rectangle enclosing the portion of a window or client area affected by recent drawing operations. An application can use this rectangle to conveniently determine the extent of changes caused by drawing operations. It is sometimes used in conjunction with LockWindowUpdate_win32_LockWindowUpdate to determine which portion of the client area must be redrawn after the update lock is cleared.
An application uses the SetBoundsRect_win32_SetBoundsRect function (specifying DCB_ENABLE) to begin accumulating the bounding rectangle. Windows subsequently accumulates points for the bounding rectangle as the application uses the specified display DC. The application can retrieve the current bounding rectangle at any time by using the GetBoundsRect_win32_GetBoundsRect function. The application stops the accumulation by calling SetBoundsRect again, specifying the DCB_DISABLE value.
+$#A Using the WM_PAINT Message
You can use the WM_PAINT_win32_WM_PAINT message to carry out the drawing necessary for displaying information. Because Windows sends WM_PAINT messages to your application when your window must be updated or when you explicitly request an update, you can consolidate the code for drawing in your application's window procedure. You can then use this code whenever your application must draw either new or existing information.
The following sections show a variety of ways to use the WM_PAINT message to draw in a window.
· Drawing in the client area_win32_Drawing_in_the_Client_Area
· Redrawing the entire client area_win32_Redrawing_the_Entire_Client_Area
· Redrawing in the update region_win32_Redrawing_in_the_Update_Region
· Invalidating the client area_win32_Invalidating_the_Client_Area
· Drawing a minimized window_win32_Drawing_a_Minimized_Window
· Drawing a custom window background_win32_Drawing_a_Custom_Window_Background

+$#A Drawing in the Client Area
You use the BeginPaint_win32_BeginPaint and EndPaint_win32_EndPaint functions to prepare for and complete the drawing in the client area. BeginPaint returns a handle of the display DC used for drawing in the client area; EndPaint ends the paint request and releases the DC.
In the following example, the window procedure writes the message "Hello, Windows!" in the client area. To make sure the string is visible when the window is first created, the WinMain_win32_WinMain function calls UpdateWindow_win32_UpdateWindow immediately after creating and showing the window. This causes a WM_PAINT_win32_WM_PAINT message to be sent immediately to the window procedure.
LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
PAINTSTRUCT ps;
HDC hdc;

switch (message) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 0, 0, "Hello, Windows!", 15);
EndPaint(hwnd, &ps);
return 0L;

.
.
.
}
}

int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hInstance; /* handle of current instance */
HINSTANCE hPrevInstance; /* handle of previous instance */
LPSTR lpCmdLine; /* address of command line */
int nCmdShow; /* show-window type (open/icon) */
{
HWND hwnd;

hwnd = CreateWindowEx( /* parameters */ );

ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);

.
.
.

return msg.wParam;
}

+$#A Redrawing the Entire Client Area
You can have your application redraw the entire contents of the client area whenever the window changes size by setting the CS_HREDRAW and CS_VREDRAW styles for the window class. Applications that adjust the size of the drawing based on the size of the window use these styles to ensure that they start with a completely empty client area when drawing.
In the following example, the window procedure draws a five-pointed star that fits neatly in the client area. It uses a common DC and must set the mapping mode as well as window and viewport extents each time the WM_PAINT_win32_WM_PAINT message is processed.
LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
POINT aptStar[6] = {50,2, 2,98, 98,33, 2,33, 98,98, 50,2};

.
.
.

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 100, 100, NULL);
SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);
Polyline(hdc, aptStar, 6);
EndPaint(hwnd, &ps);
return 0L;

.
.
.
}

int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hInstance; /* handle of current instance */
HINSTANCE hPrevInstance; /* handle of previous instance */
LPSTR lpCmdLine; /* address of command line */
int nCmdShow; /* show-window type (open/icon) */
{
WNDCLASS wc;

.
.
.

wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) WndProc;

.
.
.

RegisterClass(&wc);

.
.
.

return msg.wParam;
}

+$#A Redrawing in the Update Region
You can limit the amount of drawing your application carries out when processing the WM_PAINT_win32_WM_PAINT message by determining the size and location of the update region. Because Windows uses the update region when creating the clipping region for the window's display DC, you can indirectly determine the update region by examining the clipping region.
In the following example, the window procedure draws a triangle, a rectangle, a pentagon, and a hexagon, but only if all or a portion of each figure lies within the update region. The window procedure uses the RectVisible_win32_RectVisible function and a 100-by-100 rectangle to determine whether a figure is within the clipping region (and therefore the update region) for the common DC retrieved by BeginPaint_win32_BeginPaint.
POINT aptTriangle[4] = {50,2, 98,86, 2,86, 50,2},
aptRectangle[5] = { 2,2, 98,2, 98,98, 2,98, 2,2},
aptPentagon[6] = {50,2, 98,35, 79,90, 21,90, 2,35, 50,2},
aptHexagon[7] = {50,2, 93,25, 93,75, 50,98, 7,75, 7,25, 50,2};
.
.
.

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetRect(&rc, 0, 0, 100, 100);

if (RectVisible(hdc, &rc))
Polyline(hdc, aptTriangle, 4);

SetViewportOrgEx(hdc, 100, 0, NULL);
if (RectVisible(hdc, &rc))
Polyline(hdc, aptRectangle, 5);

SetViewportOrgEx(hdc, 0, 100, NULL);
if (RectVisible(hdc, &rc))
Polyline(hdc, aptPentagon, 6);

SetViewportOrgEx(hdc, 100, 100, NULL);
if (RectVisible(hdc, &rc))
Polyline(hdc, aptHexagon, 7);
EndPaint(hwnd, &ps);
return 0L;

.
.
.

The coordinates of each figure in this example lie within the same 100-by-100 rectangle. Before drawing a figure, the window procedure sets the viewport origin to a different part of the client area by using the SetViewportOrgEx_win32_SetViewportOrgEx function. This prevents figures from being drawn one on top of the other. Changing the viewport origin does not affect the clipping region, but does affect how the coordinates of the rectangle passed to RectVisible_win32_RectVisible are interpreted. Changing the origin also allows you to use a single rectangle to check the update region rather than individual rectangles for each figure.
+$#A Invalidating the Client Area
Windows is not the only source of WM_PAINT_win32_WM_PAINT messages. The InvalidateRect_win32_InvalidateRect or InvalidateRgn_win32_InvalidateRgn function can indirectly generate WM_PAINT messages for your windows. These functions mark all or part of a client area as invalid (that must be redrawn).
In the following example, the window procedure invalidates the entire client area when processing WM_CHAR_win32_WM_CHAR messages. This allows the user to change the figure by typing a number and view the results; these results are drawn as soon as there are no other messages in the application's message queue.

POINT aptPentagon[6] = {50,2, 98,35, 79,90, 21,90, 2,35, 50,2},
aptHexagon[7] = {50,2, 93,25, 93,75, 50,98, 7,75, 7,25, 50,2};
POINT *ppt = aptPentagon;
int cpt = 6;

.
.
.

case WM_CHAR:
switch (wParam) {
case '5':
ppt = aptPentagon;
cpt = 6;
break;
case '6':
ppt = aptHexagon;
cpt = 7;
break;
}
InvalidateRect(hwnd, NULL, TRUE);
return 0L;

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 100, 100, NULL);
SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);
Polyline(hdc, ppt, cpt);
EndPaint(hwnd, &ps);
return 0L;

In this example, the NULL argument used by InvalidateRect_win32_InvalidateRect specifies the entire client area; the TRUE argument causes the background to be erased. If you do not want the application to wait until the application's message queue has no other messages, use the UpdateWindow_win32_UpdateWindow function to force the WM_PAINT_win32_WM_PAINT message to be sent immediately. If there is any invalid part of the client area, UpdateWindow sends the WM_PAINT message for the given window directly to the window procedure.
+$#A Drawing a Minimized Window
You can draw your own minimized windows rather than having Windows draw them for you. Most applications define a class icon when registering the window class for the window, and Windows draws the icon when the window is minimized. If you set the class icon to NULL, however, Windows sends a WM_PAINT_win32_WM_PAINT message to your window procedure whenever the window is minimized, enabling the window procedure to draw in the minimized window.
In the following example, the window procedure draws a star in the minimized window. The procedure uses the IsIconic_win32_IsIconic function to determine when the window is minimized. This ensures that the star is drawn only when the window is minimized.

POINT aptStar[6] = {50,2, 2,98, 98,33, 2,33, 98,98, 50,2};

.
.
.

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

/* Determine whether the window is minimized. */

if (IsIconic(hwnd)) {
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 100, 100, NULL);
SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);
Polyline(hdc, aptStar, 6);
} else {
TextOut(hdc, 0,0, "Hello, Windows!", 15);
}
EndPaint(hwnd, &ps);
return 0L;

You set the class icon to NULL by setting the hIcon member of the WNDCLASS_win32_WNDCLASS_str structure to NULL before calling the RegisterClass_win32_RegisterClass function for the window class.
+$#A Drawing a Custom Window Background
You can draw your own window background rather than having Windows draw it for you. Most applications specify a brush handle or system color value for the class background brush when registering the window class; Windows uses the brush or color to draw the background. If you set the class background brush to NULL, however, Windows sends a WM_ERASEBKGND_win32_WM_ERASEBKGND message to your window procedure whenever the window background must be drawn, letting you draw a custom background.
In the following example, the window procedure draws a large checkerboard pattern that fits neatly in the window. The procedure fills the client area with a white brush and then draws thirteen 20-by-20 rectangles using a gray brush. The display DC to use when drawing the background is specified in the wParam parameter for the message.

HBRUSH hbrWhite, hbrGray;

.
.
.

case WM_CREATE:
hbrWhite = GetStockObject(WHITE_BRUSH);
hbrGray = GetStockObject(GRAY_BRUSH);
return 0L;

case WM_ERASEBKGND:
hdc = (HDC) wParam;
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 100, 100, NULL);
SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);
FillRect(hdc, &rc, hbrWhite);

for (i = 0; i < 13; i++) {
x = (i * 40) % 100;
y = ((i * 40) / 100) * 20;
SetRect(&rc, x, y, x + 20, y + 20);
FillRect(hdc, &rc, hbrGray);
}
return 1L;

If the application draws its own minimized window, Windows also sends the WM_ERASEBKGND_win32_WM_ERASEBKGND message to the window procedure to draw the background for the minimized window. You can use the same technique used by WM_PAINT_win32_WM_PAINT to determine whether the window is minimized ¾ that is, call the IsIconic_win32_IsIconic function and check for the return value TRUE.
+$#A Using the GetDC Function
You use the GetDC_win32_GetDC function to carry out drawing that must occur instantly rather than when a WM_PAINT_win32_WM_PAINT message is processing. Such drawing is usually in response to an action by the user, such as making a selection or drawing with the mouse. In such cases, the user should receive instant feedback and must not be forced to stop selecting or drawing in order for the application to display the result. The following sections show how to use GetDC to draw in a window.
+$#A Drawing with the Mouse
You can permit the user to draw lines with the mouse by having your window procedure draw while processing the WM_MOUSEMOVE_win32_WM_MOUSEMOVE message. Windows sends the WM_MOUSEMOVE message to the window procedure whenever the user moves the cursor within the window. To draw lines, the window procedure can retrieve a display DC and draw a line in the window between the current and previous cursor positions.
In the following example, the window procedure prepares for drawing when the user presses and holds the left mouse button (sending the WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message). As the user moves the cursor within the window, the window procedure receives a series of WM_MOUSEMOVE messages. For each message, the window procedure draws a line connecting the previous position and the current position. To draw the line, the procedure uses GetDC_win32_GetDC to retrieve a display DC; then, as soon as drawing is complete and before returning from the message, the procedure uses the ReleaseDC_win32_ReleaseDC function to release the display DC. As soon as the user releases the mouse button, the window procedure clears the flag, and the drawing stops (which sends the WM_LBUTTONUP_win32_WM_LBUTTONUP message).

BOOL fDraw = FALSE;
POINT ptPrevious;

.
.
.

case WM_LBUTTONDOWN:
fDraw = TRUE;
ptPrevious.x = LOWORD(lParam);
ptPrevious.y = HIWORD(lParam);
return 0L;

case WM_LBUTTONUP:
if (fDraw) {
hdc = GetDC(hwnd);
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, LOWORD(lParam), HIWORD(lParam));
ReleaseDC(hwnd, hdc);
}
fDraw = FALSE;
return 0L;

case WM_MOUSEMOVE:
if (fDraw) {
hdc = GetDC(hwnd);
MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);
LineTo(hdc, ptPrevious.x = LOWORD(lParam),
ptPrevious.y = HIWORD(lParam));
ReleaseDC(hwnd, hdc);
}
return 0L;

An application that enables drawing, as in this example, typically records either the points or lines so that the lines can be redrawn whenever the window is updated. Drawing applications often use a memory DC and an associated bitmap to store lines that were drawn by using a mouse.
+$#A Drawing at Timed Intervals
You can draw at timed intervals by creating a timer with the SetTimer_win32_SetTimer function. By using a timer to send WM_TIMER_win32_WM_TIMER messages to the window procedure at regular intervals, an application can carry out simple animation in the client area while other applications continue running.
In the following example, the application bounces a star from side to side in the client area. Each time the window procedure receives a WM_TIMER message, the procedure erases the star at the current position, calculates a new position, and draws the star within the new position. The procedure starts the timer by calling SetTimer while processing the WM_CREATE_win32_WM_CREATE message.
RECT rcCurrent = {0,0,20,20};
POINT aptStar[6] = {10,1, 1,19, 19,6, 1,6, 19,19, 10,1};
int X = 2, Y = -1, idTimer = -1;
BOOL fVisible = FALSE;
HDC hdc;

LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
PAINTSTRUCT ps;
RECT rc;

switch (message) {
case WM_CREATE:

/* Calculate the starting point. */

GetClientRect(hwnd, &rc);
OffsetRect(&rcCurrent, rc.right / 2, rc.bottom / 2);

/* Initialize the private DC. */

hdc = GetDC(hwnd);
SetViewportOrgEx(hdc, rcCurrent.left,
rcCurrent.top, NULL);
SetROP2(hdc, R2_NOT);

/* Start the timer. */

SetTimer(hwnd, idTimer = 1, 10, NULL);
return 0L;

case WM_DESTROY:
KillTimer(hwnd, 1);
PostQuitMessage(0);
return 0L;

case WM_SIZE:
switch (wParam) {
case SIZE_MINIMIZED:

/* Stop the timer if the window is minimized. */

KillTimer(hwnd, 1);
idTimer = -1;
break;

case SIZE_RESTORED:

/*
* Move the star back into the client area
* if necessary.
*/

if (rcCurrent.right > (int) LOWORD(lParam))
rcCurrent.left =
(rcCurrent.right =
(int) LOWORD(lParam)) - 20;
if (rcCurrent.bottom > (int) HIWORD(lParam))
rcCurrent.top =
(rcCurrent.bottom =
(int) HIWORD(lParam)) - 20;

/* Fall through to the next case. */

case SIZE_MAXIMIZED:

/* Start the timer if it had been stopped. */

if (idTimer == -1)
SetTimer(hwnd, idTimer = 1, 10, NULL);
break;
}
return 0L;

case WM_TIMER:

/* Hide the star if it is visible. */

if (fVisible)
Polyline(hdc, aptStar, 6);

/* Bounce the star off a side if necessary. */

GetClientRect(hwnd, &rc);
if (rcCurrent.left + X < rc.left ||
rcCurrent.right + X > rc.right)
X = -X;
if (rcCurrent.top + Y < rc.top ||
rcCurrent.bottom + Y > rc.bottom)
Y = -Y;

/* Show the star in its new position. */

OffsetRect(&rcCurrent, X, Y);
SetViewportOrgEx(hdc, rcCurrent.left,
rcCurrent.top, NULL);
fVisible = Polyline(hdc, aptStar, 6);

return 0L;

case WM_ERASEBKGND:

/* Erase the star. */

fVisible = FALSE;
return DefWindowProc(hwnd, message, wParam, lParam);

case WM_PAINT:

/*
* Show the star if it is not visible. Use BeginPaint
* to clear the update region.
*/

BeginPaint(hwnd, &ps);
if (!fVisible)
fVisible = Polyline(hdc, aptStar, 6);
EndPaint(hwnd, &ps);
return 0L;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

This application uses a private DC to minimize the time required to prepare the DC for drawing. The window procedure retrieves and initializes the private DC when processing the WM_CREATE_win32_WM_CREATE message, setting the binary raster operation mode to allow the star to be erased and drawn using the same call to the Polyline_win32_Polyline function. The window procedure also sets the viewport origin to allow the star to be drawn using the same set of points regardless of the star's position in the client area.
The application uses the WM_PAINT_win32_WM_PAINT message to draw the star whenever the window must be updated. The window procedure draws the star only if it is not visible; that is, only if it has been erased by the WM_ERASEBKGND_win32_WM_ERASEBKGND message. The window procedure intercepts the WM_ERASEBKGND message to set the fVisible variable, but passes the message to DefWindowProc_win32_DefWindowProc so that Windows can draw the window background.
The application uses the WM_SIZE_win32_WM_SIZE message to stop the timer when the window is minimized and to restart the timer when the minimized window is restored. The window procedure also uses the message to update the current position of the star if the size of the window has been reduced so that the star is no longer in the client area. The application keeps track of the star's current position by using the structure specified by rcCurrent, which defines the bounding rectangle for the star. Keeping all corners of the rectangle in the client area keeps the star in the area. The window procedure initially centers the star in the client area when processing the WM_CREATE message.
+$#AK Painting and Drawing Reference
The following functions and messages are associated with painting and drawing.
+$#A Painting and Drawing Functions
Following are the functions used with painting and drawing.
BeginPaint_win32_BeginPaintDrawAnimatedRects_win32_DrawAnimatedRectsDrawCaption_win32_DrawCaptionDrawEdge_win32_DrawEdgeDrawFocusRect_win32_DrawFocusRectDrawFrameControl_win32_DrawFrameControlDrawState_win32_DrawStateDrawStateProc_win32_DrawStateProcDrawTextEx_win32_DrawTextExEndPaint_win32_EndPaintExcludeUpdateRgn_win32_ExcludeUpdateRgnGdiFlush_win32_GdiFlushGdiGetBatchLimit_win32_GdiGetBatchLimitGdiSetBatchLimit_win32_GdiSetBatchLimitGetBkColor_win32_GetBkColorGetBkMode_win32_GetBkModeGetBoundsRect_win32_GetBoundsRectGetROP2_win32_GetROP2GetUpdateRect_win32_GetUpdateRectGetUpdateRgn_win32_GetUpdateRgnGetWindowDC_win32_GetWindowDCGrayString_win32_GrayStringInvalidateRect_win32_InvalidateRectInvalidateRgn_win32_InvalidateRgnLockWindowUpdate_win32_LockWindowUpdateOutputProc_win32_OutputProcPaintDesktop_win32_PaintDesktopRedrawWindow_win32_RedrawWindowResetDisplay_win32_ResetDisplaySetBkColor_win32_SetBkColorSetBkMode_win32_SetBkModeSetBoundsRect_win32_SetBoundsRectSetRectRgn_win32_SetRectRgnSetROP2_win32_SetROP2UpdateWindow_win32_UpdateWindowValidateRect_win32_ValidateRectValidateRgn_win32_ValidateRgnWindowFromDC_win32_WindowFromDC
+$#A Painting and Drawing Messages
Following are the messages used with painting and drawing.
WM_DISPLAYCHANGE_win32_WM_DISPLAYCHANGEWM_ERASEBKGND_win32_WM_ERASEBKGNDWM_ICONERASEBKGND_win32_WM_ICONERASEBKGNDWM_NCPAINT_win32_WM_NCPAINTWM_PAINT_win32_WM_PAINTWM_PAINTICON_win32_WM_PAINTICONWM_PRINT_win32_WM_PRINTWM_PRINTCLIENT_win32_WM_PRINTCLIENT WM_SETREDRAW_win32_WM_SETREDRAW

+$#AK Rectangles
Applications written for Microsoft® Windows® use rectangles to specify rectangular areas on the screen or in a window.
+$#A About Rectangles
In Windows applications, rectangles are used for the cursor clipping region, the invalid portion of the client area, an area for displaying formatted text, or the scroll area. Your applications can also use rectangles to fill, frame, or invert a portion of the client area with a given brush, and to retrieve the coordinates of a window or a window's client area.
+$#A Rectangle Coordinates
An application must use a RECT_win32_RECT_str structure to define a rectangle. The structure specifies the coordinates of two points: the upper left and lower right corners of the rectangle. The sides of the rectangle extend from these two points and are parallel to the x- and y-axes.
The coordinate values for a rectangle are expressed as signed integers. The coordinate value of a rectangle's right side must be greater than that of its left side. Likewise, the coordinate value of the bottom must be greater than that of the top.
Because applications can use rectangles for many different purposes, the Windows rectangle functions do not use an explicit unit of measure. Instead, all rectangle coordinates and dimensions are given in signed, logical values. The mapping mode and the function in which the rectangle is used determine the units of measure. For more information about coordinates and mapping modes, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.
+$#A Rectangle Operations
The Microsoft® Win32® application programming interface (API) provides several functions for working with rectangles.
The SetRect_win32_SetRect function creates a rectangle, the CopyRect_win32_CopyRect function makes a copy of a given rectangle, and the SetRectEmpty_win32_SetRectEmpty function creates an empty rectangle. An empty rectangle is any rectangle that has zero width, zero height, or both. The IsRectEmpty_win32_IsRectEmpty function determines whether a given rectangle is empty. The EqualRect_win32_EqualRect function determines whether two rectangles are identical ¾ that is, whether they have the same coordinates.
The InflateRect_win32_InflateRect function increases or decreases the width or height of a rectangle, or both. It can add or remove width from both ends of the rectangle; it can add or remove height from both the top and bottom of the rectangle.
The OffsetRect_win32_OffsetRect function moves a rectangle by a given amount. It moves the rectangle by adding the given x-amount, y-amount, or x- and y-amounts to the corner coordinates.
The PtInRect_win32_PtInRect function determines whether a given point lies within a given rectangle. The point is in the rectangle if it lies on the left or top side or is completely within the rectangle. The point is not in the rectangle if it lies on the right or bottom side.
The IntersectRect_win32_IntersectRect function creates a new rectangle that is the intersection of two existing rectangles, as shown in the following figure.
{ewc msdncd, EWGraphic, group10811 0 /a "SDK_01.bmp"}
The UnionRect_win32_UnionRect function creates a new rectangle that is the union of two existing rectangles, as shown in the following figure.
{ewc msdncd, EWGraphic, group10811 1 /a "SDK_02.bmp"}
For information about functions that draw ellipses and polygons, see Filled Shapes_win32_Filled_Shapes.
+$#A Using Rectangles
The example in this section illustrates how to use the rectangle functions. It consists of the main window procedure from an application that enables the user to move and size a bitmap.
When the application starts, it draws a 32-pixel by 32-pixel bitmap in the upper left corner of the screen. The user can move the bitmap by dragging it. To size the bitmap, the user creates a target rectangle by dragging the mouse, then drags the bitmap and "drops" it on the target rectangle. The application responds by copying the bitmap into the target rectangle.
The window procedure that allows the user to move and size the bitmap is given in the following example.
LRESULT CALLBACK MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd; /* handle of window */
UINT uMsg; /* message */
WPARAM wParam; /* first message parameter */
LPARAM lParam; /* second message parameter */
{
HDC hdc; /* device context (DC) for window */
RECT rcTmp; /* temporary rectangle */
PAINTSTRUCT ps; /* paint data for Begin/EndPaint */
POINT ptClientUL; /* client area upper left corner */
POINT ptClientLR; /* client area lower right corner */
static HDC hdcCompat; /* DC for copying bitmap */
static POINT pt; /* x and y coordinates of cursor */
static RECT rcBmp; /* rectangle that encloses bitmap */
static RECT rcTarget; /* rectangle to receive bitmap */
static RECT rcClient; /* client-area rectangle */
static BOOL fDragRect; /* TRUE if bitmap rect. is dragged */
static HBITMAP hbmp; /* handle of bitmap to display */
static HBRUSH hbrBkgnd; /* handle of background-color brush */
static COLORREF crBkgnd; /* color of client-area background */
static HPEN hpenDot; /* handle of dotted pen */

switch (uMsg) {
case WM_CREATE:

/* Load the bitmap resource. */

hbmp = LoadBitmap(hinst, MAKEINTRESOURCE(1));

/*
* Create a device context (DC) to hold the bitmap.
* The bitmap is copied from this DC to the window's DC
* whenever it must be drawn.
*/

hdc = GetDC(hwnd);
hdcCompat = CreateCompatibleDC(hdc);
SelectObject(hdcCompat, hbmp);

/*
* Create a brush of the same color as the background
* of the client area. The brush is used later to erase
* the old bitmap before copying the bitmap into the
* target rectangle.
*/

crBkgnd = GetBkColor(hdc);
hbrBkgnd = CreateSolidBrush(crBkgnd);
ReleaseDC(hwnd, hdc);

/*
* Create a dotted pen. The pen is used to draw the
* bitmap rectangle as the user drags it.
*/

hpenDot = CreatePen(PS_DOT, 1, RGB(0, 0, 0));

/*
* Set the initial rectangle for the bitmap. Note that
* this application supports only a 32- by 32-pixel
* bitmap. The rectangle is slightly larger than the
* bitmap.
*/

SetRect(&rcBmp, 1, 1, 34, 34);
return 0;

case WM_PAINT:

/*
* Draw the bitmap rectangle and copy the bitmap into
* it. The 32-pixel by 32-pixel bitmap is centered
* in the rectangle by adding 1 to the left and top
* coordinates of the bitmap rectangle, and subtracting
* 2 from the right and bottom coordinates.
*/

BeginPaint(hwnd, &ps);
Rectangle(ps.hdc, rcBmp.left, rcBmp.top,
rcBmp.right, rcBmp.bottom);
StretchBlt(ps.hdc, rcBmp.left + 1, rcBmp.top + 1,
(rcBmp.right - rcBmp.left) - 2,
(rcBmp.bottom - rcBmp.top) - 2, hdcCompat,
0, 0, 32, 32, SRCCOPY);
EndPaint(hwnd, &ps);
break;

case WM_MOVE:
case WM_SIZE:

/*
* Convert the client coordinates of the client-area
* rectangle to screen coordinates and save them in a
* rectangle. The rectangle is passed to the ClipCursor
* function during WM_LBUTTONDOWN processing.
*/

GetClientRect(hwnd, &rcClient);
ptClientUL.x = rcClient.left;
ptClientUL.y = rcClient.top;
ptClientLR.x = rcClient.right;
ptClientLR.y = rcClient.bottom;
ClientToScreen(hwnd, &ptClientUL);
ClientToScreen(hwnd, &ptClientLR);
SetRect(&rcClient, ptClientUL.x, ptClientUL.y,
ptClientLR.x, ptClientLR.y);
return 0;

case WM_LBUTTONDOWN:

/*
* Restrict the mouse cursor to the client area. This
* ensures that the window receives a matching
* WM_LBUTTONUP message.
*/

ClipCursor(&rcClient);

/* Save the coordinates of the mouse cursor. */

pt.x = (LONG) LOWORD(lParam);
pt.y = (LONG) HIWORD(lParam);

/*
* If the user has clicked the bitmap rectangle, redraw
* it using the dotted pen. Set the fDragRect flag to
* indicate that the user is about to drag the
* rectangle.
*/

if (PtInRect(&rcBmp, pt)) {
hdc = GetDC(hwnd);
SelectObject(hdc, hpenDot);
Rectangle(hdc, rcBmp.left, rcBmp.top, rcBmp.right,
rcBmp.bottom);
fDragRect = TRUE;
ReleaseDC(hwnd, hdc);
}
return 0;

case WM_MOUSEMOVE:

/*
* Draw a target rectangle or drag the bitmap
* rectangle, depending on the status of the fDragRect
* flag.
*/

if ((wParam && MK_LBUTTON)
&& !fDragRect) { /* draw a target rectangle */

/*
* Set the mix mode so that the pen color is the
* inverse of the background color. The previous
* rectangle can then be erased by drawing
* another rectangle on top of it.
*/

hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOTXORPEN);

/*
* If a previous target rectangle exists, erase
* it by drawing another rectangle on top of it.
*/

if (!IsRectEmpty(&rcTarget))
Rectangle(hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);

/*
* Save the coordinates of the target rectangle.
* Avoid invalid rectangles by ensuring that the
* value of the left coordinate is greater than
* that of the right coordinate, and that the
* value of the bottom coordinate is greater than
* that of the top.
*/

if ((pt.x < (LONG) LOWORD(lParam)) &&
(pt.y > (LONG) HIWORD(lParam)))
SetRect(&rcTarget, pt.x, HIWORD(lParam),
LOWORD(lParam), pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&
(pt.y > (LONG) HIWORD(lParam)))
SetRect(&rcTarget, LOWORD(lParam),
HIWORD(lParam), pt.x, pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&
(pt.y < (LONG) HIWORD(lParam)))
SetRect(&rcTarget, LOWORD(lParam), pt.y,
pt.x, HIWORD(lParam));
else
SetRect(&rcTarget, pt.x, pt.y, LOWORD(lParam),
HIWORD(lParam));

/* Draw the new target rectangle. */

Rectangle(hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);
ReleaseDC(hwnd, hdc);
}
else if ((wParam && MK_LBUTTON)
&& fDragRect) { /* drag the bitmap rectangle */

/*
* Set the mix mode so that the pen color is the
* inverse of the background color.
*/

hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOTXORPEN);

/*
* Select the dotted pen into the DC and erase
* the previous bitmap rectangle by drawing
* another rectangle on top of it.
*/

SelectObject(hdc, hpenDot);
Rectangle(hdc, rcBmp.left, rcBmp.top,
rcBmp.right, rcBmp.bottom);

/*
* Set the new coordinates of the bitmap
* rectangle, then redraw it.
*/

OffsetRect(&rcBmp, LOWORD(lParam) - pt.x,
HIWORD(lParam) - pt.y);
Rectangle(hdc, rcBmp.left, rcBmp.top,
rcBmp.right, rcBmp.bottom);
ReleaseDC(hwnd, hdc);

/* Save the coordinates of the mouse cursor. */

pt.x = (LONG) LOWORD(lParam);
pt.y = (LONG) HIWORD(lParam);
}
return 0;

case WM_LBUTTONUP:

/*
* If the bitmap rectangle and target rectangle
* intersect, copy the bitmap into the target
* rectangle. Otherwise, copy the bitmap into the
* rectangle bitmap at its new location.
*/

if (IntersectRect(&rcTmp, &rcBmp, &rcTarget)) {

/*
* Erase the bitmap rectangle by filling it with
* the background color.
*/

hdc = GetDC(hwnd);
FillRect(hdc, &rcBmp, hbrBkgnd);

/*
* Redraw the target rectangle because the part
* that intersected with the bitmap rectangle was
* erased by the call to FillRect.
*/

Rectangle(hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);

/* Copy the bitmap into the target rectangle. */

StretchBlt(hdc, rcTarget.left + 1, rcTarget.top + 1,
(rcTarget.right - rcTarget.left) - 2,
(rcTarget.bottom - rcTarget.top) - 2, hdcCompat,
0, 0, 32, 32, SRCCOPY);

/*
* Copy the target rectangle to the bitmap
* rectangle, set the coordinates of the target
* rectangle to 0, then reset the fDragRect flag.
*/

CopyRect(&rcBmp, &rcTarget);
SetRectEmpty(&rcTarget);
ReleaseDC(hwnd, hdc);
fDragRect = FALSE;
}

else if (fDragRect) {

/*
* Draw the bitmap rectangle, copy the bitmap into
* it, and reset the fDragRect flag.
*/

hdc = GetDC(hwnd);
Rectangle(hdc, rcBmp.left, rcBmp.top,
rcBmp.right, rcBmp.bottom);
StretchBlt(hdc, rcBmp.left + 1, rcBmp.top + 1,
(rcBmp.right - rcBmp.left) - 2,
(rcBmp.bottom - rcBmp.top) - 2, hdcCompat,
0, 0, 32, 32, SRCCOPY);
ReleaseDC(hwnd, hdc);
fDragRect = FALSE;
}

/* Release the mouse cursor. */

ClipCursor((LPRECT) NULL);
return 0;

case WM_DESTROY:

/*
* Destroy the background brush, compatible bitmap,
* and the bitmap.
*/

DeleteObject(hbrBkgnd);
DeleteDC(hdcCompat);
DeleteObject(hbmp);
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}

+$#A Rectangle Functions
Following are the functions used with rectangles.
CopyRect_win32_CopyRectEqualRect_win32_EqualRectInflateRect_win32_InflateRectIntersectRect_win32_IntersectRectIsRectEmpty_win32_IsRectEmptyOffsetRect_win32_OffsetRectPtInRect_win32_PtInRectSetRect_win32_SetRectSetRectEmpty_win32_SetRectEmptySubtractRect_win32_SubtractRectUnionRect_win32_UnionRect

+$#AK Timers
A timer is an internal routine that repeatedly measures a specified interval, in milliseconds. Each time the interval (or time-out value) elapses, the system notifies the window associated with the timer. Because the accuracy of a timer depends on the system clock rate and how often the application retrieves messages from the message queue, the time-out value is only approximate.
+$#A About Timers
An application written for Microsoft® Windows® creates a timer by using the SetTimer_win32_SetTimer function. If a window handle is given in this function, the application creates the timer for that window. Then, whenever the time-out value elapses, the system posts a WM_TIMER_win32_WM_TIMER message to the window. If no window handle is given in SetTimer, the application that created the timer must monitor its message queue for WM_TIMER messages and dispatch them to the appropriate window.
A new timer starts timing as soon as it is created. An application can change a timer's time-out value by using SetTimer and can destroy a timer by using the KillTimer_win32_KillTimer function. To use system resources efficiently, applications should destroy timers that are no longer necessary.
Each timer has a unique identifier. When creating a timer, an application can either specify an identifier or have the system create a unique value. The first parameter of a WM_TIMER message contains the identifier of the timer that posted the message.
+$#A Using Timers
· Creating a timer_win32_Creating_a_Timer
· Destroying a timer_win32_Destroying_a_Timer
· Using timer functions to create a mousetrap_win32_Using_Timer_Functions_to_Create_a_Mousetrap
· Process the WM_TIMER_win32_WM_TIMER message.
· Use a TimerProc_win32_TimerProc callback function.

+$#A Creating a Timer
The following example uses the SetTimer_win32_SetTimer function to create two timers. The first timer is set for every 10 seconds, the second for every 5 minutes.
/* Set two timers. */

SetTimer(hwnd, /* handle of main window */
IDT_TIMER1, /* timer identifier */
10000, /* 10-second interval */
(TIMERPROC) NULL); /* no timer callback */

SetTimer(hwnd, /* handle of main window */
IDT_TIMER2, /* timer identifier */
300000, /* 5-minute interval */
(TIMERPROC) NULL); /* no timer callback */

To process the WM_TIMER_win32_WM_TIMER messages generated by these timers, add a WM_TIMER case statement to the window procedure for the hwnd parameter.
case WM_TIMER:

switch (wParam) {

case IDT_TIMER1:

.
. /* Process the 10-second timer. */
.

return 0;

case IDT_TIMER2:

.
. /* Process the 5-minute timer. */
.

return 0;
}

An application can also create a timer whose WM_TIMER_win32_WM_TIMER messages are processed not by the main window procedure but by an application-defined callback function, as in the following code sample, which creates a timer and uses the callback function MyTimerProc to process the timer's WM_TIMER messages.
/* Set the timer. */

SetTimer(hwnd, /* handle of main window */
IDT_TIMER3, /* timer identifier */
5000, /* 5-second interval */
(TIMERPROC) MyTimerProc); /* timer callback */

The calling convention for MyTimerProc must be based on the Microsoft® Win32® callback function, TimerProc_win32_TimerProc.
If your application creates a timer without specifying a window handle, your application must monitor the message queue for WM_TIMER_win32_WM_TIMER messages and dispatch them to the appropriate window.
HWND hwndTimer; /* handle of window for timer messages */
MSG msg; /* message structure */

while (GetMessage(&msg, /* message structure */
NULL, /* handle of window receiving msg. */
NULL, /* lowest message to examine */
NULL)) /* highest message to examine */
{

/* Post WM_TIMER messages to the hwndTimer procedure. */

if (msg.message == WM_TIMER) {
msg.hwnd = hwndTimer;
}

TranslateMessage(&msg); /* translates virtual-key codes */
DispatchMessage(&msg); /* dispatches message to window */
}

+$#A Destroying a Timer
Applications should use the KillTimer_win32_KillTimer function to destroy timers that are no longer necessary. The following example destroys the timers identified by the constants IDT_TIMER1, IDT_TIMER2, and IDT_TIMER3.
/* Destroy the timers. */

KillTimer(hwnd, IDT_TIMER1);
KillTimer(hwnd, IDT_TIMER2);
KillTimer(hwnd, IDT_TIMER3);

+$#A Using Timer Functions to Create a Mousetrap
Sometimes it is necessary to prevent more input while you have a cursor on the screen. One way to accomplish this is to create a special routine that traps mouse input until a specific event occurs. Many developers refer to this routine as "building a mousetrap."
The following example uses the SetTimer_win32_SetTimer and KillTimer_win32_KillTimer functions to create a simple mousetrap. SetTimer creates a timer that sends a WM_TIMER_win32_WM_TIMER message every 10 seconds. Each time the application receives a WM_TIMER message, it records the cursor location. If the current location is the same as the previous location and the application's main window is minimized, the application moves the cursor to the icon. When the application closes, KillTimer stops the timer.
HICON hIcon1; /* icon handle */
POINT ptOld; /* previous cursor location */
UINT uResult; /* SetTimer's return value */
HINSTANCE hinstance; /* handle of current instance */

.
. /* Perform application initialization here. */
.

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400));
wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200));

/* Record the initial cursor position. */

GetCursorPos(&ptOld);

/* Set the timer for the mousetrap. */

uResult = SetTimer(hwnd, /* handle of main window */
IDT_MOUSETRAP, /* timer identifier */
10000, /* 10-second interval */
(TIMERPROC) NULL); /* no timer callback */

if (uResult == 0) {
ErrorHandler("No timer is available.");
}

LONG APIENTRY MainWndProc(
HWND hwnd, /* handle of main window */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{

HDC hdc; /* handle of device context */
POINT pt; /* current cursor location */
RECT rc; /* location of minimized window */

switch (message) {

.
. /* Process other messages. */
.

case WM_TIMER:

/*
* If the window is minimized, compare the current
* cursor position with the one from 10 seconds
* earlier. If the cursor position has not changed,
* move the cursor to the icon.
*/

if (IsIconic(hwnd)) {
GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {
GetWindowRect(hwnd, &rc);
SetCursorPos(rc.left, rc.top);
}
else {
ptOld.x = pt.x;
ptOld.y = pt.y;
}
}

return 0;

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_MOUSETRAP);
PostQuitMessage(0);
break;

.
. /* Process other messages. */
.

}

Although the following example also creates a mousetrap, it processes the WM_TIMER_win32_WM_TIMER message through the application-defined callback function MyTimerProc, rather than through the application's message queue.
UINT uResult; /* SetTimer's return value */
HICON hIcon1; /* icon handle */
POINT ptOld; /* previous cursor location */
HINSTANCE hinstance; /* handle of current instance */

.
. /* Perform application initialization here. */
.

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400));
wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200));

/* Record the current cursor position. */

GetCursorPos(&ptOld);

/* Set the timer for the mousetrap. */

uResult = SetTimer(hwnd, /* handle of main window */
IDT_MOUSETRAP, /* timer identifier */
10000, /* 10-second interval */
(TIMERPROC) MyTimerProc); /* timer callback */

if (uResult == 0) {
ErrorHandler("No timer is available.");
}

LONG APIENTRY MainWndProc(
HWND hwnd, /* handle of main window */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{

HDC hdc; /* handle of device context */

switch (message) {

.
. /* Process other messages. */
.

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_MOUSETRAP);
PostQuitMessage(0);
break;

.
. /* Process other messages. */
.

}

/*
* MyTimerProc is an application-defined callback function that
* processes WM_TIMER messages.
*/

VOID CALLBACK MyTimerProc(
HWND hwnd, /* handle of window for timer messages */
UINT message, /* WM_TIMER message */
UINT idTimer, /* timer identifier */
DWORD dwTime) /* current system time */
{

RECT rc;
POINT pt;

/*
* If the window is minimized, compare the current
* cursor position with the one from 10 seconds earlier.
* If the cursor position has not changed, move the
* cursor to the icon.
*/

if (IsIconic(hwnd)) {
GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {
GetWindowRect(hwnd, &rc);
SetCursorPos(rc.left, rc.top);
}
else {
ptOld.x = pt.x;
ptOld.y = pt.y;
}
}
}

+$#A Timer Functions
The following functions are used with timers.
KillTimer_win32_KillTimerSetTimer_win32_SetTimerTimerProc_win32_TimerProc

+$#AK Window Classes
Every window is a member of a window class. A window class is a set of attributes that Microsoft® Windows® uses as a template to create a window in an application. This overview describes the types of window classes, how Windows locates them, and the elements that define the default behavior of windows that belong to them.
+$#A About Window Classes
Each window class has an associated window procedure shared by all windows of the same class. The window procedure processes messages for all windows of that class and therefore controls their behavior and appearance. For more information about window procedures, see Window Procedures_win32_Window_Procedures.
An application must register a window class before it can create a window of that class. Registering a window class associates a window procedure, class styles, and other class attributes with a class name. When an application specifies a class name in the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function, the operating system creates a window with the window procedure, styles, and other attributes associated with that class name.
+$#A Types of Window Classes
There are three types of window classes: system global, application global, and application local. These types differ in scope and in when and how they are registered and destroyed.
+$#A System Global Classes
When Windows starts, it registers a set of system global classes for controls, including buttons, combo boxes, list boxes, scroll bars, edit controls, and static controls.
Any application can use a system global class at any time. Because Windows registers system global classes on behalf of all applications, an application cannot destroy any of these classes.
+$#A Application Global Classes
An application global class is a window class registered by a dynamic-link library (DLL) and available to all applications in the system. For example, you can register a window class that defines a custom control as an application global class so that all applications can create instances of the custom control.
In Windows, all window classes are process specific. An application can create a global class by creating the window class in a DLL and listing the name of the DLL in the registry under the appropriate keys.
Whenever a process starts, the system loads the specified DLL in the context of the newly started process before calling the main function in that process. The DLL must register the class during its initialization procedure and must specify the CS_GLOBALCLASS style. (For more information about class styles, see Class Styles_win32_Class_Styles.) After a class has been registered, any application can use it to create any number of windows belonging to that class.
Windows does not automatically destroy a class when the DLL that registered it is unloaded. For this reason, the Windows exit procedure of the DLL should call the UnregisterClass_win32_UnregisterClass function to remove the class.
+$#A Application Local Classes
An application local class is any window class that an application registers for its exclusive use. Although an application can register any number of local classes, most applications register only one. This window class supports the window procedure of the application's main window.
Registering a local class is similar to registering an application global class, except that the CS_GLOBALCLASS style is not specified in the style member of the WNDCLASSEX_win32_WNDCLASSEX_str structure.
Windows destroys a local class when the application that registered it closes. An application can also use the UnregisterClass_win32_UnregisterClass function to remove a local class and free the storage associated with it.
+$#A How Windows Locates a Class
Windows maintains a list of WNDCLASSEX_win32_WNDCLASSEX_str structures for each of the three types of window classes. When an application calls the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function to create a window with a specified class, Windows uses the following procedure to locate the class:
1. Windows searches the list of application local classes for a class with the specified name. Note that several application instances can use the same name to register local classes. Windows uses instance handles to differentiate among local classes that have the same name.
2. If the name is not in the application local class list, Windows searches the list of application global classes.
3. If the name is not in the application global class list, Windows searches the list of system global classes.

All windows created by the application use this procedure, including windows created by Windows on the application's behalf, such as dialog boxes. It is possible to override system global classes without affecting other applications. That is, an application can register an application local class having the same name as a system global class. This replaces the system global class in the context of the application but does not prevent other applications from using the system global class.
+$#A Class Ownership
The owner of a class is the application or DLL that registered the class. Windows determines class ownership from the hInstance member of the WNDCLASSEX_win32_WNDCLASSEX_str structure passed to the RegisterClassEx_win32_RegisterClassEx function when the class is registered. For Windows DLLs, the hInstance member must be the instance handle of the DLL. The class is destroyed when the owner closes or is unloaded. For this reason, the application must destroy all windows using the class before it closes or the DLL is unloaded.
+$#A Elements of a Window Class
The elements of a window class define the default behavior of windows belonging to the class. The application that registers a window class assigns elements to the class by setting appropriate members in a WNDCLASSEX_win32_WNDCLASSEX_str structure and passing the structure to the RegisterClassEx_win32_RegisterClassEx function. The GetClassInfoEx_win32_GetClassInfoEx and GetClassLong_win32_GetClassLong functions retrieve information about a given window class. The SetClassLong_win32_SetClassLong function changes elements of a local or global class that the application has already registered.
Although a complete window class consists of many elements, Windows requires only that an application supply a class name, the window-procedure address, and an instance handle. Use the other elements to define default attributes for windows of the class, such as the shape of the cursor and the content of the menu for the window. You must initialize any unused members of the WNDCLASSEX structure to zero or NULL. The window class elements are as follows.
Element Purpose
Class name Distinguishes the class from other registered classes.
Window-procedure address Points to the function that processes all messages sent to windows in the class and defines the behavior of the window.
Instance handle Identifies the application or DLL that registered the class.
Class cursor Defines the mouse cursor that Windows displays for a window of the class.
Class icon Defines the large icon.
Small class icon Defines the small icon (Windows version 4.0 and higher).
Class background brush Defines the color and pattern that fill the client area when the window is opened or painted.
Class menu Specifies the default menu for windows that do not explicitly define a menu.
Class styles Defines how to update the window after moving or resizing it, how to process double-clicks of the mouse, how to allocate space for the device context, and other aspects of the window.
Extra class memory Specifies the amount of extra memory, in bytes, that Windows should reserve for the class. All windows in the class share the extra memory and can use it for any application-defined purpose. Windows initializes this memory to zero.
Extra window memory Specifies the amount of extra memory, in bytes, that Windows should reserve for each window belonging to the class. The extra memory can be used for any application-defined purpose. Windows initializes this memory to zero.

+$#A Class Name
Every window class needs a class name to distinguish one class from another. Assign a class name by setting the lpszClassName member of the WNDCLASSEX_win32_WNDCLASSEX_str structure to the address of a null-terminated string that specifies the name. Because window classes are process specific in Windows, window class names need to be unique only within the same process. Also, because class names occupy space in the system's private atom table, you should keep class name strings as short a possible.
The GetClassName_win32_GetClassName function retrieves the name of the class to which a given window belongs.
+$#A Window-Procedure Address
Every class needs a window-procedure address to define the entry point of the window procedure used to process all messages for windows in the class. Windows passes messages to the procedure when it requires the window to carry out tasks, such as painting its client area or responding to input from the user. An application assigns a window procedure to a class by copying its address to the lpfnWndProc member of the WNDCLASSEX_win32_WNDCLASSEX_str structure. For more information about window procedures, see Window Procedures_win32_Window_Procedures.
+$#A Instance Handle
Every window class requires an instance handle to identify the application or DLL that registered the class. As a multitasking system, Windows lets several applications or DLLs run at the same time, so it requires instance handles to keep track of all of them. Windows assigns a handle to each copy of a running application or DLL.
Multiple instances of the same application or DLL all use the same code segment, but each has its own data segment. Windows uses an instance handle to identify the data segment that corresponds to a particular instance of an application or DLL.
Windows passes an instance handle to an application or DLL when the application starts. The application or DLL assigns this instance handle to the class by copying it to the hInstance member of the WNDCLASSEX_win32_WNDCLASSEX_str structure.
+$#A Class Cursor
The class cursor defines the shape of the cursor when it is in the client area of a window in the class. Windows automatically sets the cursor to the given shape when the cursor enters the window's client area and ensures it keeps that shape while it remains in the client area. To assign a cursor shape to a window class, load a predefined cursor shape by using the LoadCursor_win32_LoadCursor function and then assign the returned cursor handle to the hCursor member of the WNDCLASSEX_win32_WNDCLASSEX_str structure. Alternatively, provide a custom cursor resource and use the LoadCursor function to load it from the application's resources.
Windows does not require a class cursor. If an application sets the hCursor member of the WNDCLASSEX structure to NULL, a class cursor is not defined. Windows assumes the window sets the cursor shape each time the cursor moves into the window. A window can set the cursor shape by calling the SetCursor_win32_SetCursor function whenever the window receives the WM_MOUSEMOVE_win32_WM_MOUSEMOVE message. For more information about cursors, see Cursors_win32_Cursors.
+$#A Class Icons
A class icon is a picture that the system uses to represent a window of a particular class. A class can have one or two icons, depending on the operating system version. You can use the GetVersionEx_win32_GetVersionEx function to retrieve the version number.
Class Icons in Version 4.0 or Later
With version 4.0 or later, an application can have two class icons ¾ one large and one small. The system displays a window's large class icon in the task-switch window that appears when the user presses ALT+TAB, and in the large icon views of the task bar and explorer. The small class icon appears in a window's title bar and in the small icon views of the task bar and explorer.
To assign a large and small icon to a window class, specify the handles of the icons in the hIcon and hIconSm members of the WNDCLASSEX_win32_WNDCLASSEX_str structure. The icon dimensions must conform to required dimensions for large and small class icons. For a large class icon, you can determine the required dimensions by specifying the SM_CXICON and SM_CYICON values in a call to the GetSystemMetrics_win32_GetSystemMetrics function. For a small class icon, specify the SM_CXSMICON and SM_CYSMICON values. For information about creating icons and loading icon resources, see Icons_win32_Icons.
If an application sets the hIcon and hIconSm members of the WNDCLASSEX structure to NULL, the system uses the default application icon as the large and small class icons for the window class. If you specify a large class icon but not a small one, the system creates a small class icon based on the large one. However, if you specify a small class icon but not a large one, the system uses the default application icon as the large class icon and the specified icon as the small class icon.
You can override the large or small class icon for a particular window by using the WM_SETICON_win32_WM_SETICON message. You can retrieve the current large or small class icon by using the WM_GETICON_win32_WM_GETICON message.
Class Icons in Versions Earlier Than 4.0
In operating system versions earlier than 4.0, the class icon defines the icon used when a window of the given class is minimized. To assign an icon to a window class, specify an icon handle in the hIcon member of the WNDCLASS_win32_WNDCLASS_str structure. If an application sets the hIcon member of WNDCLASS to NULL, a class icon is not defined. In this case, Windows sends the WM_ICONERASEBKGND_win32_WM_ICONERASEBKGND message to a window of the class whenever it must paint the icon background. If the window does not process the WM_ICONERASEBKGND message, Windows draws an image of the contents of the window's client area when the window is minimized. For more information about drawing minimized windows, see Painting and Drawing_win32_Painting_and_Drawing.
+$#A Class Background Brush
A class background brush prepares the client area of a window for subsequent drawing by the application. Windows uses the brush to fill the client area with a solid color or pattern, thereby removing all previous images from that location whether they belonged to the window or not. Windows notifies a window that its background should be painted by sending the WM_ERASEBKGND_win32_WM_ERASEBKGND message to the window. For more information about brushes, see Brushes_win32_Brushes.
To assign a background brush to a class, create a brush by using the appropriate GDI functions and assign the returned brush handle to the hbrBackground member of the WNDCLASSEX_win32_WNDCLASSEX_str structure.
Instead of creating a brush, an application can set the hbrBackground member to one of the standard system color values. For a list of the standard system color values, see SetSysColors_win32_SetSysColors.
To use a standard system color, the application must increase the background-color value by one. For example, COLOR_BACKGROUND + 1 is the system background color. Alternatively, you can use the GetSysColorBrush_win32_GetSysColorBrush function to retrieve the handle of a brush that corresponds to a standard system color, and then specify the handle in the hbrBackground member of the WNDCLASSEX structure.
Windows does not require that a window class have a class background brush. If this parameter is set to NULL, the window must paint its own background whenever it receives the WM_ERASEBKGND_win32_WM_ERASEBKGND message.
+$#A Class Menu
A class menu defines the default menu to be used by the windows in the class if no explicit menu is given when the windows are created. A menu is a list of commands from which a user can choose actions for the application to carry out.
You can assign a menu to a class by setting the lpszMenuName member of the WNDCLASSEX_win32_WNDCLASSEX_str structure to the address of a null-terminated string that specifies the resource name of the menu. The menu is assumed to be a resource in the given application. Windows automatically loads the menu when it is needed. Note that if the menu resource is identified by an integer and not by a name, the application can set the lpszMenuName member to that integer by applying the MAKEINTRESOURCE_win32_MAKEINTRESOURCE macro before assigning the value.
Windows does not require a class menu. If an application sets the lpszMenuName member of the WNDCLASSEX structure to NULL, Windows assumes the windows in the class have no menu bars. Even if no class menu is given, an application can still define a menu bar for a window when it creates the window.
Windows does not allow menu bars with child windows. If a menu is given for a class and a child window of that class is created, the menu is ignored. For more information about menus, see Menus (Windows NT)_win32_Menus_Windows_NT_.
+$#A Class Styles
The class styles define additional elements of the window class. Two or more styles can be combined by using the bitwise OR (|) operator. To assign a style to a window class, assign the style to the style member of the WNDCLASSEX_win32_WNDCLASSEX_str structure. The class styles are as follows.
Style Action
CS_BYTEALIGNCLIENT Aligns the window's client area on a byte boundary (in the x direction) to enhance performance during drawing operations. This style affects the width of the window and its horizontal placement on the display.
CS_BYTEALIGNWINDOW Aligns the window on a byte boundary (in the x direction) to enhance performance during operations that involve moving or sizing the window. This style affects the width of the window and its horizontal placement on the display.
CS_CLASSDC Allocates one device context to be shared by all windows in the class. For more information about device contexts, see Class and Private Device Contexts_win32_Class_and_Private_Device_Contexts and Device Contexts_win32_Device_Contexts.
CS_DBLCLKS Instructs Windows to send a double-click message to the window procedure when the user double-clicks the mouse while the cursor is within a window belonging to the class. For more information about double-clicks, see Mouse Input_win32_Mouse_Input.
CS_GLOBALCLASS Specifies that the window class is an application global class. For more information, see Application Global Classes_win32_Application_Global_Classes.
CS_HREDRAW Specifies that the entire window is to be redrawn if a movement or size adjustment changes the width of the client area.
CS_NOCLOSE Disables the Close command on the System menu.
CS_OWNDC Allocates a unique device context for each window in the class. For more information about device contexts, see Class and Private Device Contexts_win32_Class_and_Private_Device_Contexts and Device Contexts_win32_Device_Contexts.
CS_PARENTDC Sets the clipping rectangle of the child window to that of the parent window so that the child can draw on the parent. A window with the CS_PARENTDC style bit receives a regular device context from the system's cache of device contexts. It does not give the child the parent's device context or device context settings. Specifying CS_PARENTDC enhances an application's performance. For more information about device contexts, see Class and Private Device Contexts_win32_Class_and_Private_Device_Contexts and Device Contexts_win32_Device_Contexts.
CS_SAVEBITS Saves, as a bitmap, the portion of the screen image obscured by a window. Windows uses the saved bitmap to re-create the screen image when the window is removed. Windows displays the bitmap at its original location and does not send WM_PAINT_win32_WM_PAINT messages to windows obscured by the window if other screen actions have not invalidated the stored image. Use this style for small windows that are displayed briefly and then removed before other screen activity takes place (for example, menus or dialog boxes). This style increases the time required to display the window, because the operating system must first allocate memory to store the bitmap.
CS_VREDRAW Specifies that the entire window is to be redrawn if a movement or size adjustment changes the height of the client area.

+$#A Extra Class Memory
Windows maintains a WNDCLASSEX_win32_WNDCLASSEX_str structure internally for each window class in the system. When an application registers a window class, it can direct Windows to allocate and append a number of additional bytes of memory to the end of the WNDCLASSEX structure. This memory is called extra class memory and is shared by all windows belonging to the class. Use the extra class memory to store any information pertaining to the class.
Because extra memory is allocated from the system's local heap, an application should use extra class memory sparingly. With operating system version 4.0 or later, the RegisterClassEx_win32_RegisterClassEx function fails if the amount of extra class memory requested is greater than 40 bytes. If an application requires more than 40 bytes, it should allocate its own memory and store a pointer to the memory in the extra class memory.
The SetClassWord_win32_SetClassWord and SetClassLong_win32_SetClassLong functions copy a value to the extra class memory. To retrieve a value from the extra class memory, use the GetClassWord_win32_GetClassWord and GetClassLong_win32_GetClassLong functions. The cbClsExtra member of the WNDCLASSEX structure specifies the amount of extra class memory to allocate. An application that doesn't use extra class memory must initialize the cbClsExtra member to zero.
+$#A Extra Window Memory
Windows maintains an internal data structure for each window. When registering a window class, an application can specify a number of additional bytes of memory, called extra window memory. When creating a window of the class, Windows allocates and appends the specified amount of extra window memory to the end of the window's structure. An application can use this memory to store window-specific data.
Because extra memory is allocated from the system's local heap, an application should use extra window memory sparingly. With operating system version 4.0 or later, the RegisterClassEx_win32_RegisterClassEx function fails if the amount of extra window memory requested is greater than 40 bytes. If an application requires more than 40 bytes, it should allocate its own memory and store a pointer to the memory in the extra window memory.
The SetWindowWord_win32_SetWindowWord and SetWindowLong_win32_SetWindowLong functions copy a value to the extra memory. The GetWindowWord_win32_GetWindowWord and GetWindowLong_win32_GetWindowLong functions retrieve a value from the extra memory. The cbWndExtra member of the WNDCLASSEX_win32_WNDCLASSEX_str structure specifies the amount of extra window memory to allocate. An application that doesn't use the memory must initialize cbWndExtra to zero.
+$#A Class and Private Device Contexts
A device context is a special set of values that applications use for drawing in the client area of their windows. Windows requires a device context for each window on the display but allows some flexibility in how the operating system stores and treats that device context.
If no device-context style is explicitly given, Windows assumes each window uses a device context retrieved from a pool of contexts maintained by Windows. In such cases, each window must retrieve and initialize the device context before painting and free it after painting.
To avoid retrieving a device context each time it needs to paint inside a window, an application can specify the CS_OWNDC style for the window class. This class style directs Windows to create a private device context ¾ that is, to allocate a unique device context for each window in the class. The application need only retrieve the context once and then use it for all subsequent painting. Although the CS_OWNDC style is convenient, use it carefully, because each device context uses a significant portion of system resources.
By specifying the CS_CLASSDC style, an application can create a class device context. A class device context is a rarely used feature that allows multiple windows created from the same window class within a process to use exactly the same device context for drawing.
An application can specify the CS_PARENTDC style to create child windows that inherit the device context of their parent window. This style allows a child window to draw in the client area of its parent.
An application can specify the CS_PARENTDC style to set the clipping rectangle of the child window to that of the parent window so the child can draw in the parent. A window with the CS_PARENTDC style receives a regular device context from the system's cache of device contexts. It does not give a child window's device context or device context settings to the parent window. Specifying CS_PARENTDC enhances an application's performance because Windows doesn't need to keep recalculating the visible region for each child window.
For more information about device contexts, see Device Contexts_win32_Device_Contexts. For more information about painting, see Painting and Drawing_win32_Painting_and_Drawing.
+$#A Using Window Classes
In Windows, each application must register its own window classes. Your application can register an application local class at any time by using the RegisterClassEx_win32_RegisterClassEx function. You must define the window procedure in the application, fill the members of the WNDCLASSEX_win32_WNDCLASSEX_str structure, and then pass a pointer to the structure to the RegisterClassEx function.
The following example shows how to register a local window class and use it to create your application's main window.
#include <windows.h>

/* Global variable */

HINSTANCE hinst;

/* Function prototypes */

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
InitApplication(HINSTANCE);
InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

/* Application entry point */

int WINAPI WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hinstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
MSG msg;

if (!InitApplication(hinstance))
return FALSE;

if (!InitInstance(hinstance, nCmdShow))
return FALSE;

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(lpCmdLine);
}

BOOL InitApplication(hinstance)
HINSTANCE hinstance;
{
WNDCLASSEX wcx;

/*
* Fill in the window class structure with parameters
* that describe the main window.
*/

wcx.cbSize = sizeof(wcx); /* size of structure */
wcx.style = CS_HREDRAW |
CS_VREDRAW; /* redraw if size changes */
wcx.lpfnWndProc = MainWndProc; /* points to window proc. */
wcx.cbClsExtra = 0; /* no extra class memory */
wcx.cbWndExtra = 0; /* no extra window memory */
wcx.hInstance = hinstance; /* handle of instance */
wcx.hIcon = LoadIcon(NULL,
IDI_APPLICATION); /* predefined app. icon */
wcx.hCursor = LoadCursor(NULL,
IDC_ARROW); /* predefined arrow */
wcx.hbrBackground = GetStockObject(
WHITE_BRUSH); /* white background brush */
wcx.lpszMenuName = "MainMenu"; /* name of menu resource */
wcx.lpszClassName = "MainWClass"; /* name of window class */
wcx.hIconSm = LoadImage(hinstance, /* small class icon */
MAKEINTRESOURCE(5),
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR);

/* Register the window class. */

return RegisterClassEx(&wcx);
}

BOOL InitInstance(hinstance, nCmdShow)
HINSTANCE hinstance;
int nCmdShow;
{
HWND hwnd;

/* Save the application-instance handle. */

hinst = hinstance;

/* Create the main window. */

hwnd = CreateWindow(
"MainWClass", /* name of window class */
"Sample", /* title-bar string */
WS_OVERLAPPEDWINDOW, /* top-level window */
CW_USEDEFAULT, /* default horizontal position */
CW_USEDEFAULT, /* default vertical position */
CW_USEDEFAULT, /* default width */
CW_USEDEFAULT, /* default height */
(HWND) NULL, /* no owner window */
(HMENU) NULL, /* use class menu */
hinstance, /* handle of app. instance */
(LPVOID) NULL); /* no window-creation data */

if (!hwnd)
return FALSE;

/*
* Show the window and send a WM_PAINT message to the window
* procedure.
*/

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return TRUE;

}

Registering an application global class is similar to registering an application local class, with the following exceptions:
· The style parameter of the WNDCLASSEX_win32_WNDCLASSEX_str structure must specify the CS_GLOBALCLASS style.
· The class can be registered by an application or a DLL. If registered by an application, the class is global only within the application. If the class is registered by a DLL and listed in the registry, the system loads the DLL for every application.
· The application or DLL need not check for a previous instance of the application or DLL before registering the class.
+$#A Window Class Functions
Following are the functions used with window classes.
GetClassInfo_win32_GetClassInfoGetClassInfoEx_win32_GetClassInfoExGetClassLong_win32_GetClassLongGetClassName_win32_GetClassNameGetClassWord_win32_GetClassWordGetWindowLong_win32_GetWindowLongGetWindowWord_win32_GetWindowWordRegisterClass_win32_RegisterClassRegisterClassEx_win32_RegisterClassExSetClassLong_win32_SetClassLongSetClassWord_win32_SetClassWordSetWindowLong_win32_SetWindowLongSetWindowWord_win32_SetWindowWordUnregisterClass_win32_UnregisterClass

+$#AK Window Procedures
In Microsoft® Windows®, every window has an associated window procedure ¾ a function that processes all messages sent or posted to all windows of the class. All aspects of a window's appearance and behavior depend on the window procedure's response to these messages.
+$#A About Window Procedures
Each window is a member of a particular window class. The window class determines the default window procedure that an individual window uses to process its messages. All windows belonging to the same class use the same default window procedure. For example, the system defines a window procedure for the combo box class (COMBOBOX); all combo boxes then use that window procedure.
An application typically registers at least one new window class and an associated window procedure. After registering a class, the application can create many windows of that class, all of which use the same default window procedure. Because this means several sources could simultaneously call the same piece of code, the developer must be careful when modifying shared resources from a window procedure. For more information about window classes, see Window Classes_win32_Window_Classes.
Window procedures for dialog boxes (called dialog box procedures) have a similar structure and function as regular window procedures. All points referring to window procedures in this section also apply to dialog box procedures. For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes.
+$#A Structure of a Window Procedure
A window procedure is a function that has four parameters and returns a 32-bit signed value. The parameters consist of a window handle, a UINT message identifier, and two message parameters declared with the WPARAM and LPARAM data types. The operating system defines a WPARAM value as a 32-bit unsigned integer and an LPARAM value as a 32-bit signed integer.
The window procedure parameters are described in the following table.
Parameter Description
hwnd Handle of the window receiving the message.
uMsg Message identifier. The identifier corresponds to one of the predefined constants (for example, WM_CREATE_win32_WM_CREATE) defined in the system header files or to an application-defined message identifier.
wParam, lParam Message parameters. Their interpretation depends on the value of the uMsg parameter.

Message parameters often contain information in both their low-order and high-order words. The Microsoft® Win32® application programming interface (API) includes several macros an application can use to extract information from the message parameters. The LOWORD_win32_LOWORD macro, for example, extracts the low-order word (bits 0 through 15) from a message parameter. Other macros include HIWORD_win32_HIWORD, LOBYTE_win32_LOBYTE, and HIBYTE_win32_HIBYTE.
The return value of a window procedure is declared as an LRESULT data type, which is defined as a 32-bit signed value. The interpretation of the return value depends on the particular message. Consult the description of each message to determine the appropriate return value.
Because it is possible to call a window procedure recursively, it is important to minimize the number of local variables that it uses. When processi ng individual messages, an application should call functions outside the window procedure to avoid excessive use of local variables, possibly causing the stack to overflow during deep recursion.
+$#A Default Window Procedure
The default window procedure function DefWindowProc_win32_DefWindowProc defines certain fundamental behavior shared by all windows. The default window procedure provides the minimal functionality for a window. An application-defined window procedure should pass any messages that it does not process to the DefWindowProc function for default processing.
+$#A Window Procedure Subclassing
When an application creates a window, the operating system allocates a block of memory for storing information specific to the window, including the address of the window procedure that processes messages for the window. When Windows needs to pass a message to the window, it searches the window-specific information for the address of the window procedure and passes the message to that procedure.
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. By subclassing a window, an application can augment, modify, or monitor the behavior of the window. Although it is not recommended, an application can subclass any window, including those belonging to a system global class, such as an edit control or a list box. For example, an application could subclass an edit control to prevent the control from accepting certain characters. For an explanation of the risks involved, see the following section.
An application subclasses a window by replacing the address of the window's original window procedure with the address of a new window procedure, called the subclass procedure. Thereafter, the subclass procedure receives any messages sent or posted to the window.
The subclass procedure can take three actions upon receiving a message: it can pass the message to the original window procedure, modify the message and pass it to the original window procedure, or process the message and not pass it to the original window procedure. If the subclass procedure processes a message, it can do so before, after, or both before and after it passes the message to the original window procedure.
Windows provides two types of subclassing: instance and global. In instance subclassing, an application replaces the window procedure address of a single instance of a window. An application must use instance subclassing to subclass an existing window. In global subclassing, an application replaces the address of the window procedure in the WNDCLASS_win32_WNDCLASS_str structure of a window class. All subsequent windows created with the class have the address of the subclass procedure, but existing windows of the class are not affected.
+$#A Instance Subclassing
An application subclasses an instance of a window by using the SetWindowLong_win32_SetWindowLong function. The application passes the GWL_WNDPROC flag, the handle of the window to subclass, and the address of the subclass procedure to SetWindowLong. The subclass procedure can reside in either the application's module or a dynamic-link library (DLL). An application must list the name of the subclass procedure in the EXPORTS statement of the application's or DLL's module-definition (.DEF) file.
SetWindowLong returns the address of the window's original window procedure. The application must save the address, using it in subsequent calls to the CallWindowProc_win32_CallWindowProc function, to pass intercepted messages to the original window procedure. The application must also have the original window procedure address to remove the subclass from the window. To remove the subclass, the application calls SetWindowLong again, passing the address of the original window procedure with the GWL_WNDPROC flag and the handle of the window.
An application can subclass any window in the system; however, when subclassing a window it does not own, the application must ensure that the subclass procedure does not destroy the original behavior of the window. Because the application does not control the window, it must not depend on information about the window that the owner might change in the future.
An application should not use the extra window bytes or the class bytes for a window without knowing exactly what the bytes mean and how the original window procedure uses them. Even so, the application should not use them unless it owns the window. If the application uses the extra window bytes of a window that another application owns and the owner changes some aspect of the extra bytes, the subclass procedure may fail. For this reason, an application should not subclass a window that belongs to a system global control class. Windows owns the system global classes, and aspects of the controls might change from one version of Windows to the next. If the application must subclass a window that belongs to a system global class, the developer may need to update the application when a new version of Windows is released.
Because instance subclassing occurs after a window is created, the application subclassing the window cannot add any extra bytes to the window. Applications that subclass a window should use the window's property list to store any data needed for an instance of the subclassed window. For more information about window properties, see Window Properties_win32_Window_Properties.
When an application subclasses a subclassed window, it must remove the subclasses in the reverse order they were performed. If the removal order is not reversed, an unrecoverable system error may occur.
+$#A Global Subclassing
To globally subclass a window class, the application must have a handle to a window of the class. The application also needs the handle to remove the subclass. To get the handle, an application typically creates a hidden window of the class to be subclassed. After obtaining the handle, the application calls the SetClassLong_win32_SetClassLong function, specifying the handle, the GCL_WNDPROC flag, and the address of the subclass procedure. SetClassLong returns the address of the original window procedure for the class.
The original window procedure address is used in global subclassing in the same way it is used in instance subclassing. The subclass procedure passes messages to the original window procedure by calling CallWindowProc_win32_CallWindowProc. The application removes the subclass from the window class by calling SetClassLong again, specifying the address of the original window procedure, the GCL_WNDPROC flag, and the handle to a window of the class being subclassed. An application that globally subclasses a control class must remove the subclass when the application terminates; otherwise, an unrecoverable system error may occur.
Global subclassing has the same limitations as instance subclassing, plus some additional restrictions. An application should not use the extra bytes for either the class or the window instance without knowing exactly how the original window procedure uses them. If the application must associate data with a window, it should use window properties.
An application must not globally subclass a system global class. An unrecoverable system error may occur if more than one application globally subclasses a control class. If the application could benefit from globally subclassing a control class, use the technique called superclassing.
+$#A Window Procedure Superclassing
Superclassing is a technique that allows an application to create a new window class with the basic functionality of the existing class, plus enhancements provided by the application. A superclass is based on an existing window class called the base class. Frequently, the base class is a system global window class such as an edit control, but it can be any window class.
A superclass has its own window procedure, called the superclass procedure. The superclass procedure can take three actions upon receiving a message: It can pass the message to the original window procedure, modify the message and pass it to the original window procedure, or process the message and not pass it to the original window procedure. If the superclass procedure processes a message, it can do so before, after, or both before and after it passes the message to the original window procedure.
Unlike a subclass procedure, a superclass procedure can process window creation messages (WM_NCCREATE_win32_WM_NCCREATE, WM_CREATE_win32_WM_CREATE, and so on), but it must also pass them to the original base-class window procedure so that the base-class window procedure can perform its initialization procedure.
To superclass a window class, an application first calls the GetClassInfo_win32_GetClassInfo function to retrieve information about the base class. GetClassInfo fills a WNDCLASS_win32_WNDCLASS_str structure with the values from the WNDCLASS structure of the base class. Next, the application copies its own instance handle into the hInstance member of the WNDCLASS structure and copies the name of the superclass into the lpszClassName member. If the base class has a menu, the application must provide a new menu with the same menu identifiers and copy the menu name into the lpszMenuName member. If the superclass procedure processes the WM_COMMAND_win32_WM_COMMAND message and does not pass it to the window procedure of the base class, the menu need not have corresponding identifiers. GetClassInfo does not return the lpszMenuName, lpszClassName, or hInstance member of the WNDCLASS structure.
An application must also set the lpfnWndProc member of the WNDCLASS structure. The GetClassInfo function fills this member with the address of the original window procedure for the class. The application must save this address, to pass messages to the original window procedure, and then copy the address of the superclass procedure into the lpfnWndProc member. The application can, if necessary, modify any other members of the WNDCLASS structure. After it fills the WNDCLASS structure, the application registers the superclass by passing the address of the structure to the RegisterClass_win32_RegisterClass function. The superclass can then be used to create windows.
Because superclassing registers a new window class, an application can add to both the extra class bytes and the extra window bytes. The superclass must not use the original extra bytes for the base class or the window for the same reasons that an instance subclass or a global subclass should not use them. Also, if the application adds extra bytes for its use to either the class or the window instance, it must reference the extra bytes relative to the number of extra bytes used by the original base class. Because the number of bytes used by the base class may vary from one version of the base class to the next, the starting offset for the superclass's own extra bytes may also vary from one version of the base class to the next.
+$#A Using Window Procedures
· Designing a window procedure_win32_Designing_a_Window_Procedure
· Associating a window procedure with a window class_win32_Associating_a_Window_Procedure_with_a_Window_Class
· Subclassing a window_win32_Subclassing_a_Window

+$#A Designing a Window Procedure
The following example shows the structure of a typical window procedure. The window procedure uses the message argument in a switch statement with individual messages handled by separate case statements. Notice that each case returns a specific value for each message. For messages that it does not process, the window procedure calls the DefWindowProc_win32_DefWindowProc function.
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd; // handle of window
UINT uMsg; // message identifier
WPARAM wParam; // first message parameter
LPARAM lParam; // second message parameter
{

switch (uMsg) {
case WM_CREATE:

// Initialize the window.
return 0;

case WM_PAINT:

// Paint the window's client area.
return 0;

case WM_SIZE:

// Set the size and position of the window.
return 0;

case WM_DESTROY:

// Clean up window-specific data objects.
return 0;

.
. // Process other messages.
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

The WM_NCCREATE_win32_WM_NCCREATE messageis sent just after your window is created, but if an application responds to this message by returning FALSE, CreateWindowEx_win32_CreateWindowEx function fails. The WM_CREATE_win32_WM_CREATE message is sent after your window is already created.
The WM_DESTROY_win32_WM_DESTROY message is sent when your window is about to be destroyed. The DestroyWindow_win32_DestroyWindow function takes care of destroying any child windows of the window being destroyed. The WM_NCDESTROY_win32_WM_NCDESTROY message is sent just before a window is destroyed.
At the very least, a window procedure should process the WM_PAINT_win32_WM_PAINT message to draw itself. Typically, it should handle mouse and keyboard messages as well. Consult the descriptions of individual messages to determine whether your window procedure should handle them.
Your application can call the DefWindowProc_win32_DefWindowProc function as part of the processing of a message. In such a case, the application can modify the message parameters before passing the message to DefWindowProc, or it can continue with the default processing after performing its own operations.
A dialog box procedure receives a WM_INITDIALOG_win32_WM_INITDIALOG message instead of a WM_CREATE_win32_WM_CREATE message and does not pass unprocessed messages to the DefDlgProc_win32_DefDlgProc function. Otherwise, a dialog box procedure is exactly the same as a window procedure. For more information about dialog box procedures, see Dialog Boxes_win32_Dialog_Boxes.
+$#A Associating a Window Procedure with a Window Class
You associate a window procedure with a window class when registering the class. You must fill a WNDCLASS_win32_WNDCLASS_str structure with information about the class, and the lpfnWndProc member must specify the address of the window procedure. To register the class, pass the address of WNDCLASS structure to the RegisterClass_win32_RegisterClass function. Once the window class is registered, the window procedure is automatically associated with each new window created with that class.
The following example shows how to associate the window procedure in the previous example with a window class.
int APIENTRY WinMain(hinstance, hinstPrev, lpCmdLine, nCmdShow)
HINSTANCE hinstance; // handle of current instance
HINSTANCE hinstPrev; // handle of previous instance
LPSTR lpCmdLine; // address of command-line string
int nCmdShow; // show-window type
{
WNDCLASS wc;

// Register the main window class.
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWindowClass";

if (!RegisterClass(&wc))
return FALSE;

.
. // Process other messages.
.

}

+$#A Subclassing a Window
To subclass an instance of a window, call the SetWindowLong_win32_SetWindowLong function and specify the handle of the window to subclass the GWL_WNDPROC flag and a pointer to the subclass procedure. SetWindowLong returns a pointer to the original window procedure; use this pointer to pass messages to the original procedure. The subclass window procedure must use the CallWindowProc_win32_CallWindowProc function to call the original window procedure.
The following example shows how to subclass an instance of an edit control in a dialog box. The subclass window procedure enables the edit control to receive all keyboard input, including the ENTER and TAB keys, whenever the control has the input focus.
WNDPROC wpOrigEditProc;

LRESULT APIENTRY EditBoxProc(hwndDlg, uMsg, wParam, lParam)
HWND hwndDlg;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
HWND hwndEdit;

switch(uMsg) {
case WM_INITDIALOG:

// Retrieve the handle of the edit control.
hwndEdit = GetDlgItem(hwndDlg, ID_EDIT);

// Subclass the edit control.
wpOrigEditProc = (WNDPROC) SetWindowLong(hwndEdit,
GWL_WNDPROC, (LONG) EditSubclassProc);
.
. // Continue the initialization procedure.
.
return TRUE;

case WM_DESTROY:

// Remove the subclass from the edit control.
SetWindowLong(hwndEdit, GWL_WNDPROC,
(LONG) wpOrigEditProc);
.
. // Continue the cleanup procedure.
.
break;
}
return FALSE;
UNREFERENCED_PARAMETER(lParam);
}

// Subclass procedure
LRESULT APIENTRY EditSubclassProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
if (uMsg == WM_GETDLGCODE)
return DLGC_WANTALLKEYS;

return CallWindowProc(wpOrigEditProc, hwnd, uMsg,
wParam, lParam);
}

+$#A Window Procedure Functions
Following are the functions used with window procedures.
CallWindowProc_win32_CallWindowProcDefWindowProc_win32_DefWindowProcWindowProc_win32_WindowProc

+$#AK Window Properties
A window property is any data assigned to a window by the SetProp_win32_SetProp function. A window property is usually a handle of the window-specific data, but it may be any 32-bit value. Each window property is identified by a string name.
+$#A About Window Properties
Window properties are typically used to associate data with a subclassed window or a window in a multiple document interface (MDI) application. In either case, it is not convenient to use the extra bytes specified in the CreateWindow_win32_CreateWindow function or class structure for the following two reasons:
· An application might not know how many extra bytes are available or how the space is being used. By using window properties, the application can associate data with a window without accessing the extra bytes.
· An application must access the extra bytes by using offsets. Window properties, however, are accessed by their string identifiers, not by offsets.

For more information about subclassing, see Window Procedures_win32_Window_Procedures. For more information about MDI windows, see Multiple Document Interface_win32_Multiple_Document_Interface.
The Microsoft® Win32® application programming interface (API) provides several functions that enable applications to use window properties. The SetProp_win32_SetProp function assigns a window property and its string identifier to a window. The GetProp_win32_GetProp function retrieves the window property identified by a given string. The RemoveProp_win32_RemoveProp function destroys the association between a window and a window property but does not destroy the data itself. The EnumProps_win32_EnumProps and EnumPropsEx_win32_EnumPropsEx functions enumerate all of a window's properties by using an application-defined callback function. EnumPropsEx includes an extra parameter for application-defined data used by the callback function.
+$#A Using Window Properties
· Adding a window property_win32_Adding_a_Window_Property
· Retrieving a window property_win32_Retrieving_a_Window_Property
· Listing window properties for a given window_win32_Listing_Window_Properties_for_a_Given_Window
· Deleting a window property_win32_Deleting_a_Window_Property

+$#A Adding a Window Property
The following example loads an icon and then a cursor and allocates memory for a buffer. The example then uses the SetProp_win32_SetProp function to assign the resulting icon, cursor, and memory handles as window properties for the window identified by the application-defined hwndSubclass variable. The properties are identified by the strings PROP_ICON, PROP_CURSOR, and PROP_BUFFER.
#define BUFFER 4096

HINSTANCE hinst; /* handle of current instance */
HWND hwndSubclass; /* handle of a subclassed window */
HANDLE hIcon, hCursor;
HGLOBAL hMem;
char *lpMem;
TCHAR tchPath[] = "c:\\winnt\\samples\\winprop.c";

/* Load resources. */

hIcon = LoadIcon(hinst, MAKEINTRESOURCE(400));
hCursor = LoadCursor(hinst, MAKEINTRESOURCE(220));

/* Allocate and fill a memory buffer. */

hMem = GlobalAlloc(GPTR, BUFFER);
lpMem = GlobalLock(hMem);
lstrcpy(lpMem, tchPath);
GlobalUnlock(hMem);

/* Set the window properties for hwndSubclass. */

SetProp(hwndSubclass, "PROP_ICON", hIcon);
SetProp(hwndSubclass, "PROP_CURSOR", hCursor);
SetProp(hwndSubclass, "PROP_BUFFER", hMem);

+$#A Retrieving a Window Property
A window can create handles to its window property data and use the data for any purpose. The following example uses GetProp_win32_GetProp to obtain handles to the window properties identified by PROP_ICON, PROP_CURSOR, and PROP_BUFFER. The example then displays the contents of the newly obtained memory buffer, cursor, and icon in the window's client area.
#define PATHLENGTH 256

HWND hwndSubclass; /* handle of a subclassed window */
HANDLE hIconProp, hCursProp;
HGLOBAL hMemProp;
char *lpFilename;
TCHAR tchBuffer[PATHLENGTH];
int nSize;
HDC hdc;

/* Get the window properties, then use the data. */

hIconProp = (HICON) GetProp(hwndSubclass, "PROP_ICON");
TextOut(hdc, 10, 40, "PROP_ICON", 9);
DrawIcon(hdc, 90, 40, hIconProp);

hCursProp = (HCURSOR) GetProp(hwndSubclass, "PROP_CURSOR");
TextOut(hdc, 10, 85, "PROP_CURSOR", 9);
DrawIcon(hdc, 110, 85, hCursProp);

hMemProp = (HGLOBAL) GetProp(hwndSubclass, "PROP_BUFFER");
lpFilename = GlobalLock(hMemProp);
nSize = sprintf(tchBuffer,
"Path to file: %s", lpFilename);
TextOut(hdc, 10, 10, tchBuffer, nSize);

+$#A Listing Window Properties for a Given Window
In the following example, the EnumPropsEx_win32_EnumPropsEx function lists the string identifiers of the window properties for the window identified by the application-defined hwndSubclass variable. This function relies on the application-defined callback function WinPropProc to display the strings in the window's client area.
EnumPropsEx(hwndSubclass, WinPropProc, NULL);

/*
* WinPropProc is an application-defined callback function
* that lists a window property.
*/

BOOL CALLBACK WinPropProc(
HWND hwndSubclass, /* handle of window with property */
LPCSTR lpszString, /* property string or atom */
HANDLE hData) /* data handle */
{
static int nProp = 1; /* property counter */
TCHAR tchBuffer[BUFFER]; /* expanded-string buffer */
int nSize; /* size of string in buffer */
HDC hdc; /* device-context handle */

hdc = GetDC(hwndSubclass);

/* Display window property string in client area. */

nSize = sprintf(tchBuffer, "WinProp %d: %s", nProp++,
lpszString);
TextOut(hdc, 10, nProp * 20, tchBuffer, nSize);

ReleaseDC(hwndSubclass, hdc);

return TRUE;
}

+$#A Deleting a Window Property
When a window is destroyed, it must destroy any window properties it set. The following example uses the EnumPropsEx_win32_EnumPropsEx function and the application-defined callback function DelPropProc to destroy the properties associated with the window identified by the application-defined hwndSubclass variable. The callback function, which uses the RemoveProp_win32_RemoveProp function, is also shown.
case WM_DESTROY:

EnumPropsEx(hwndSubclass, DelPropProc, NULL);

PostQuitMessage(0);
break;

.
.
.

/*
* DelPropProc is an application-defined callback function
* that deletes a window property.
*/

BOOL CALLBACK DelPropProc(
HWND hwndSubclass, /* handle of window with property */
LPCSTR lpszString, /* property string or atom */
HANDLE hData) /* data handle */
{
RemoveProp(hwndSubclass, lpszString);

return TRUE;
}

+$#A Window Property Functions
Following are the functions used with window properties.
EnumProps_win32_EnumPropsEnumPropsEx_win32_EnumPropsExGetProp_win32_GetPropPropEnumProc_win32_PropEnumProcPropEnumProcEx_win32_PropEnumProcExRemoveProp_win32_RemovePropSetProp_win32_SetProp

+$#AK Common Controls
Common controls are a set of windows that are supported by the common control library, which is a dynamic-link library (DLL) included with the Microsoft® Windows® operating system. Like other control windows, a common control is a child window that an application uses in conjunction with another window to perform input and output (I/O) tasks.
+$#A About Common Controls
Most common controls belong to a window class defined in the common control DLL. The window class and the corresponding window procedure define the properties, appearance, and behavior of the control. To ensure that the common control DLL is loaded, include the InitCommonControls_win32_InitCommonControls function in your application. You create a common control by specifying the name of the window class when calling the CreateWindowEx_win32_CreateWindowEx function or by specifying the appropriate class name in a dialog box template.
For more detailed information about common controls, see the following topics.
· Animation Controls_win32_Animation_Controls
· Drag List Boxes_win32_Drag_List_Boxes
· Header Controls_win32_Header_Controls
· Hot-Key Controls_win32_Hot_Key_Controls
· Image Lists_win32_Image_Lists
· List View Controls_win32_List_View_Controls
· Progress Bars_win32_Progress_Bars
· Property Sheets_win32_Property_Sheets
· Rich Edit Controls_win32_Rich_Edit_Controls
· Status Windows_win32_Status_Windows
· Tab Controls_win32_Tab_Controls
· Toolbars_win32_Toolbars
· Tooltip Controls_win32_Tooltip_Controls
· Trackbars_win32_Trackbars
· Tree View Controls_win32_Tree_View_Controls
· Up-Down Controls_win32_Up_Down_Controls

+$#A About Common Control Styles
Each type of common control has a set of control styles that you can use to vary the appearance and behavior of the control. The common control library also includes a set of control styles that apply to two or more types of common controls. The common control styles are described in the following topics.
+$#A About Common Control Messages
Because common controls are windows, an application can manipulate them by using messages, such as WM_GETFONT_win32_WM_GETFONT or WM_SETTEXT_win32_WM_SETTEXT. In addition, the window class of each common control supports a set of control-specific messages that an application can use to manipulate the control. An application can use any of the message sending or posting functions to pass messages to the control. In addition, some common controls have a set of macros that an application can use instead of the sending or posting functions. The macros are typically easier to use than the functions.
When a change is made to the system color settings, Windows sends a WM_SYSCOLORCHANGE_win32_WM_SYSCOLORCHANGE message to all top level windows. Your top level window must forward the WM_SYSCOLORCHANGE message to its common controls; otherwise, the controls will not be notified of the color change. This ensures that the colors used by your common controls are consistent with those used by other user interface objects. For example, a toolbar control uses the "3D Objects" color to draw its buttons. If the user changes the 3D Objects color but the WM_SYSCOLORCHANGE message is not forwarded to the toolbar, the toolbar buttons will remain in their original color while the color of other buttons in the system changes.
+$#A About Common Control Notification Messages
Common controls are child windows that send notification messages to the parent window when events, such as input from the user, occur in the control. The application relies on these notification messages to determine what action the user wants it to take. Except for trackbars, which use WM_HSCROLL_win32_WM_HSCROLL message, common controls send notification messages as WM_NOTIFY_win32_WM_NOTIFY messages. The lParam parameter of WM_NOTIFY is either the address of an NMHDR_win32_NMHDR_str structure or the address of a larger structure that includes NMHDR as its first member. The structure contains the notification code and identifies the common control that sent the notification message. The meaning of the remaining structure members, if any, varies depending on the notification code.
Each type of common control has a corresponding set of notification codes. The common control library also provides notification codes that can be sent by more than one type of common control. Values for common notification values are described in the following topics.
+$#A Common Control Reference
The following functions, messages, notification messages, macros, and structures apply to all common controls.
Notification Processing
FORWARD_WM_NOTIFY_win32_FORWARD_WM_NOTIFYHANDLE_WM_NOTIFY_win32_HANDLE_WM_NOTIFYNMHDR_win32_NMHDR_str
Mouse Click Notifications
NM_CLICK_win32_NM_CLICKNM_DBLCLK_win32_NM_DBLCLKNM_RCLICK_win32_NM_RCLICKNM_RDBLCLK_win32_NM_RDBLCLK
Operation-Related Notifications
NM_OUTOFMEMORY_win32_NM_OUTOFMEMORY
Focus Notifications
NM_KILLFOCUS_win32_NM_KILLFOCUSNM_RETURN_win32_NM_RETURNNM_SETFOCUS_win32_NM_SETFOCUS
Miscellaneous
GetEffectiveClientRect_win32_GetEffectiveClientRectINDEXTOSTATEIMAGEMASK_win32_INDEXTOSTATEIMAGEMASKInitCommonControls_win32_InitCommonControlsShowHideMenuCtl_win32_ShowHideMenuCtl
+$#A Common Control Functions
The following functions are used with common controls.
GetEffectiveClientRect_win32_GetEffectiveClientRectInitCommonControls_win32_InitCommonControlsShowHideMenuCtl_win32_ShowHideMenuCtl
+$#A Common Control Message
Common controls send the following message to notify the parent window of events that affect the control.
WM_NOTIFY_win32_WM_NOTIFY
+$#A Common Control Notification Messages
Common controls send notification messages to the parent window to inform it about events. The following notifications are used with common controls.
NM_CLICK_win32_NM_CLICKNM_DBLCLK_win32_NM_DBLCLKNM_KILLFOCUS_win32_NM_KILLFOCUSNM_OUTOFMEMORY_win32_NM_OUTOFMEMORY NM_RCLICK_win32_NM_RCLICKNM_RDBLCLK_win32_NM_RDBLCLK NM_RETURN_win32_NM_RETURNNM_SETFOCUS_win32_NM_SETFOCUS
+$#A Common Control Macros
The following macros are used with common controls.
FORWARD_WM_NOTIFY_win32_FORWARD_WM_NOTIFYHANDLE_WM_NOTIFY_win32_HANDLE_WM_NOTIFYINDEXTOSTATEIMAGEMASK_win32_INDEXTOSTATEIMAGEMASK
+$#A Common Control Structure
The following structure is used with common controls.
NMHDR_win32_NMHDR_str
+$#A Common Control Constants
The following window classes and control styles are used with common controls.
+$#A Common Control Window Classes
The following window class names are provided by the common control library.
Class name Description
ANIMATE_CLASS Creates animation controls. These controls silently display an Audio Video Interleaved (AVI) clip.
HOTKEY_CLASS Creates hot-key controls. These controls make it easy for the user to define hot keys.
PROGRESS_CLASS Creates progress bars. These controls indicate the progress of a lengthy operation.
STATUSCLASSNAME Creates status windows. These controls display status information in a horizontal window.
TOOLBARCLASSNAME Creates toolbars. These controls contain buttons that carry out menu commands.
TOOLTIPS_CLASS Creates tooltip controls. These controls display a small pop-up window containing a line of text that describes the purpose of a tool in an application.
TRACKBAR_CLASS Creates trackbars. These controls let the user select from a range of values by moving a slider.
UPDOWN_CLASS Creates up-down controls. These controls combine a pair of arrows with an edit control. Clicking the arrows increments or decrements the value in the edit control.
WC_HEADER Creates header controls. These controls display headings at the top of columns of information and let the user sort the information by clicking the headings.
WC_LISTVIEW Creates list-view controls. These controls display a collection of items, each consisting of an icon and a label, and provide several ways to arrange the items.
WC_TABCONTROL Creates tab controls. These controls define multiple pages for the same area of a window or dialog box. Each page consists of a set of information or a group of controls that an application displays when the user selects the corresponding tab.
WC_TREEVIEW Creates tree-view controls. These controls display a hierarchical list of items. Each item consists of a label and an optional bitmap.

+$#A Common Control Styles
Following are the common control styles. Except where noted, these styles apply to header controls, toolbar controls, and status windows.
Common control style Description
CCS_ADJUSTABLE Enables a toolbar's built-in customization features, which allow the user to drag a button to a new position or to remove a button by dragging it off the toolbar. In addition, the user can double-click the toolbar to display the Customize Toolbar dialog box, allowing the user to add, delete, and rearrange toolbar buttons.
CCS_BOTTOM Causes the control to position itself at the bottom of the parent window's client area and sets the width to be the same as the parent window's width. Status windows have this style by default.
CCS_NODIVIDER Prevents a two-pixel highlight from being drawn at the top of the control.
CCS_NOHILITE Prevents a one-pixel highlight from being drawn at the top of the control.
CCS_NOMOVEY Causes the control to resize and move itself horizontally, but not vertically, in response to a WM_SIZE_win32_WM_SIZE message. If CCS_NORESIZE is used, this style does not apply. Header windows have this style by default.
CCS_NOPARENTALIGN Prevents the control from automatically moving to the top or bottom of the parent window. Instead, the control keeps its position within the parent window despite changes to the size of the parent. If CCS_TOP or CCS_BOTTOM is also used, the height is adjusted to the default, but the position and width remain unchanged.
CCS_NORESIZE Prevents the control from using the default width and height when setting its initial size or a new size. Instead, the control uses the width and height specified in the request for creation or sizing.
CCS_TOP Causes the control to position itself at the top of the parent window's client area and sets the width to be the same as the parent window's width. Toolbars have this style by default.

+$#AK Drag List Boxes
A drag list box is a special type of list box that enables the user to drag items from one position to another. An application can use a drag list box to display strings in a particular sequence and allow the user to change the sequence.
+$#A About Drag List Boxes
To create a drag list box, you first create a standard list box and then call the MakeDragList_win32_MakeDragList function. To convert a list box in a dialog box to a drag list box, you can call MakeDragList when the WM_INITDIALOG_win32_WM_INITDIALOG message is processed.
Drag list boxes have the same window styles and process the same messages as standard list boxes.
+$#A About Drag List Box Messages
A drag list box notifies its parent window of drag events by sending it a drag list message. The parent window must process the drag list message.
The drag list box registers the drag list message when the MakeDragList_win32_MakeDragList function is called. To get the message identifer (numeric value) of the drag list message, you must call the RegisterWindowMessage_win32_RegisterWindowMessage function, specifying the DRAGLISTMSGSTRING value.
The wParam parameter of the drag list message is the control identifier for the drag list box. The lParam parameter is the pointer to a DRAGLISTINFO_win32_DRAGLISTINFO_str structure, which contains the notification code for the drag event and other information. The return value of the message depends on the notification.
+$#A About Drag List Box Notification Messages
A drag list box sends its parent window notification messages in the form of drag list messages. The specific notification, which is identified by the uNotification member of the specified DRAGLISTINFO_win32_DRAGLISTINFO_str structure, can be DL_BEGINDRAG_win32_DL_BEGINDRAG, DL_DRAGGING_win32_DL_DRAGGING, DL_CANCELDRAG_win32_DL_CANCELDRAG, or DL_DROPPED_win32_DL_DROPPED.
The DL_BEGINDRAG notification message is sent when the user clicks the left mouse button with the cursor on a list item. The parent window can return TRUE to begin the drag operation or FALSE to disallow dragging. In this way, the parent window can enable dragging for some list items and disable it for others. You can determine which list item is at the specified location by using the LBItemFromPt_win32_LBItemFromPt function.
If dragging is in effect, the DL_DRAGGING_win32_DL_DRAGGING notification message is sent whenever the mouse is moved, or at regular intervals if the mouse is not being moved. The parent window should determine the list item under the cursor by using LBItemFromPt and then draw the insert icon by using the DrawInsert_win32_DrawInsert function. By specifying TRUE for the bAutoScroll parameter of LBItemFromPt, you can cause the list box to scroll by one line if the cursor is above or below its client area. The value you return for this notification specifies the type of mouse cursor that the drag list box should set.
The DL_CANCELDRAG_win32_DL_CANCELDRAG notification message is sent if the user cancels a drag operation by clicking the right mouse button or pressing the ESC key. The DL_DROPPED_win32_DL_DROPPED notification message is sent if the user completes a drag operation by releasing the left mouse button, even if the cursor is not over a list item. The drag list box releases the mouse capture before sending either notification. The return value of these two notifications is ignored.
+$#A Drag List Box Reference
The following functions, notification messages, and structures are associated with drag list boxes.
+$#A Drag List Box Functions
The following functions are used with drag list boxes.
DrawInsert_win32_DrawInsertLBItemFromPt_win32_LBItemFromPtMakeDragList_win32_MakeDragList
+$#A Drag List Box Notification Messages
A drag list box sends notification messages to its parent window by using a registered window message, which is called a drag list message. There are the following drag list notification messages.
DL_BEGINDRAG_win32_DL_BEGINDRAGDL_CANCELDRAG_win32_DL_CANCELDRAGDL_DRAGGING_win32_DL_DRAGGINGDL_DROPPED_win32_DL_DROPPED
+$#A Drag List Box Structure
The following structure is used with drag list boxes.
DRAGLISTINFO_win32_DRAGLISTINFO_str

+$#AK Header Controls
A header control is a window that is usually positioned above columns of text or numbers. It contains a title for each column, and it can be divided into parts. The user can drag the dividers that separate the parts to set the width of each column. The following illustration shows a header control that has labeled columns giving detailed information about files in a directory.
{ewc msdncd, EWGraphic, group10597 0 /a "SDK_01.bmp"}
+$#A About Header Controls
You can create a header control by using the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_HEADER window class. This window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls_win32_InitCommonControls function. After you create a header control, you can divide it into parts, set the text in each part, and control the appearance of the window by using header window messages.
+$#A About Header Control Styles
Header controls have a number of styles that determine the control's appearance and behavior. You set the initial styles when you create the header control. To retrieve and change the styles after creating the control, use the GetWindowLong_win32_GetWindowLong and SetWindowLong_win32_SetWindowLong functions.
A header control can be divided into two or more parts called header items. If a header control has the HDS_BUTTONS style, each item in the control looks and behaves like a push button. This style is useful if an application carries out a task when the user clicks an item in the header control. For example, an application could sort information in the columns differently depending on which item the user clicks.
The HDS_HORZ style creates a header control with a horizontal orientation.
+$#A Header Control Size and Position
Typically, you must set the size and position of a header control to fit within the boundaries of a particular rectangle, such as the client area of a window. By using the HDM_LAYOUT_win32_HDM_LAYOUT message, you can retrieve the appropriate size and position values from the header control.
When sending HDM_LAYOUT, you specify the address of an HD_LAYOUT_win32_HD_LAYOUT_str structure that contains the coordinates of the rectangle that the header control is to occupy and provides a pointer to a WINDOWPOS_win32_WINDOWPOS_str structure. The control fills the WINDOWPOS structure with size and position values appropriate for positioning the control along the top of the specified rectangle. The height value is the sum of the heights of the control's horizontal borders and the average height of characters in the font currently selected into the control's device context.
If you want to use HDM_LAYOUT_win32_HDM_LAYOUT to set the initial size and position of a header control, you should set the initial visibility state of the control so that it is hidden. After sending HDM_LAYOUT to retrieve the size and position values, you can use the SetWindowPos_win32_SetWindowPos function to set the new size, position, and visibility state.
+$#A Items
Each item in a header control can have a string, a bitmapped image, and an application-defined 32-bit value associated with it. The string and bitmap are displayed within the boundaries of the header item. If an item contains both a string and an image, the image is displayed above the string. If the string and image overlap, the string overwrites the overlapping portion of the image.
You add an item to a header control by sending the HDM_INSERTITEM_win32_HDM_INSERTITEM message to the control. The message includes the address of an HD_ITEM_win32_HD_ITEM_str structure, which defines the properties of the header item, including its string, bitmap, initial size, and application-defined value. The structure also specifies formatting flags that tell the control whether to center, left-align, or right-align the string or bitmap in the item's rectangle.
HDM_INSERTITEM returns the index of the newly added item. You can use the index in other messages to set properties or retrieve information about the item. You can delete an item by using the HDM_DELETEITEM_win32_HDM_DELETEITEM message, specifying the index of the item to delete.
You can use the HDM_SETITEM_win32_HDM_SETITEM message to set the properties of an existing header item and the HDM_GETITEM_win32_HDM_GETITEM message to retrieve the current properties of an item. To retrieve a count of the items in a header control, use the HDM_GETITEMCOUNT_win32_HDM_GETITEMCOUNT message.
You can use the HBT_SPRING value to give a header item a "springy" quality. Although an item has a minimum width, it can grow wider if there is extra room in the owner window. A header control can have multiple springy items; the extra room in the owner window is distributed among the springy items.
To make an item springy, send the WM_SETTEXT_win32_WM_SETTEXT message to the header control. In the wParam parameter, use the logical OR operator to combine the index of the header item with the HBT_SPRING value.
+$#A Owner-Drawn Header Controls
You can define individual items of a header control to be owner-drawn items. Using this technique gives you more control than you would otherwise have over the appearance of a header item.
You can use the HDM_INSERTITEM_win32_HDM_INSERTITEM message to insert a new owner-drawn item into a header control or the HDM_SETITEM_win32_HDM_SETITEM message to change an existing item to an owner-drawn item. Both messages include the address of an HD_ITEM_win32_HD_ITEM_str structure, which should have the fmt member set to the HDF_OWNERDRAW value.
When a header control must draw an owner-drawn item, it sends the WM_DRAWITEM_win32_WM_DRAWITEM message to the parent window. The wParam parameter of the message is the child window identifier of the header control, and the lParam parameter is a pointer to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure. The parent window uses the information in the structure to draw the item. For an owner-drawn item in a header control, the DRAWITEMSTRUCT structure contains the following information.
Member Description
CtlType ODT_HEADER owner-drawn control type.
CtlID Child-window identifier of the header control.
itemID Index of the item to be drawn.
itemAction ODA_DRAWENTIRE drawing-action flag.
itemState ODS_SELECTED drawing-action flag if the cursor is on the item and the mouse button is down. Otherwise, this member is zero.
hwndItem Handle of the header control.
hDC Handle of the device context of the header control.
rcItem Coordinates of the header item to be drawn. The coordinates are relative to the upper left corner of the header control.
itemData Application-defined 32-bit value associated with the item.

+$#A About Header Control Notification Messages
A header control sends notification messages to its parent window when the user clicks or double-clicks an item, when the user drags an item divider, and when the attributes of an item change. The parent window receives the notifications in the form of WM_NOTIFY_win32_WM_NOTIFY messages. The following notifications are used with header controls.
Notification Description
HDN_BEGINTRACK_win32_HDN_BEGINTRACK Signals the start of divider dragging.
HDN_DIVIDERDBLCLICK_win32_HDN_DIVIDERDBLCLICK Indicates that the user double-clicked a divider.
HDN_ENDTRACK_win32_HDN_ENDTRACK Signals the end of divider dragging.
HDN_ITEMCHANGED_win32_HDN_ITEMCHANGED Indicates a change in the attributes of an item.
HDN_ITEMCHANGING_win32_HDN_ITEMCHANGING Indicates that the attributes of an item are about to change.
HDN_ITEMCLICK_win32_HDN_ITEMCLICK Indicates that the user clicked an item.
HDN_ITEMDBLCLICK_win32_HDN_ITEMDBLCLICK Indicates that the user double-clicked an item.
HDN_TRACK_win32_HDN_TRACK Indicates that the user dragged a divider.

+$#A Default Header Control Message Processing
This section describes the window messages handled by the window procedure for the WC_HEADER window class.
Message Processing performed
WM_CREATE_win32_WM_CREATE Initializes the header control.
WM_DESTROY_win32_WM_DESTROY Frees resources allocated for the header control.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the background of the header control using the current background color for the control.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLGC_WANTTAB and DLGC_WANTARROWS values.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the current font, which is used by the header control to draw its text.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Captures mouse input. If the mouse cursor is on a divider, the control sends a HDN_BEGINTRACK_win32_HDN_BEGINTRACK notification message and begins tracking mouse movement. If the cursor is on a header item, the control redraws the item in the style that indicates the item is pressed.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Same as the WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK message.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse capture. If the control was tracking mouse movement, it sends the HDN_ENDTRACK_win32_HDN_ENDTRACK notification message and redraws the header control. Otherwise, the control sends the HDN_ITEMCLICK_win32_HDN_ITEMCLICK notification message and redraws the header item that was clicked.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE If a divider is being dragged, the control sends the HDN_TRACK_win32_HDN_TRACK notification message and redraws itself. If the left mouse button is down and the cursor is on an item, the control redraws the item in the style that indicates the item is pressed.
WM_NCCREATE_win32_WM_NCCREATE Allocates and initializes an internal data structure.
WM_NCDESTROY_win32_WM_NCDESTROY Frees resources allocated by the header control.
WM_PAINT_win32_WM_PAINT Paints the invalid region of the header control.
WM_SETCURSOR_win32_WM_SETCURSOR Sets the cursor shape, depending on whether the cursor is on a divider or in a header item.
WM_SETFONT_win32_WM_SETFONT Selects a new font handle into the device context for the header control.

+$#A Using Header Controls
· Creating a header control_win32_Creating_a_Header_Control
· Adding an item to a header control_win32_Adding_an_Item_to_a_Header_Control
+$#A Creating a Header Control
The following example creates a header control and positions it along the top of the parent window's client area. The control is initially hidden. The example uses the HDM_LAYOUT_win32_HDM_LAYOUT message to determine the appropriate size and position of the control given the bounding rectangle of the parent window. Then the example sets the size and position of the control and makes it visible.
// DoCreateHeader - creates a header control that is positioned along
// the top of the parent window's client area.
// Returns the handle of the header control.
// hwndParent - handle of the parent window
//
// Global variable
// g_hinst - handle of the application instance
extern HINSTANCE g_hinst;

HWND DoCreateHeader(HWND hwndParent)
{
HWND hwndHeader;
RECT rcParent;
HD_LAYOUT hdl;
WINDOWPOS wp;

// Ensure that the common control DLL is loaded, and then create
// the header control.
InitCommonControls();

if ((hwndHeader = CreateWindowEx(0, WC_HEADER, (LPCTSTR) NULL,
WS_CHILD | WS_BORDER | HDS_BUTTONS | HDS_HORZ,
0, 0, 0, 0, hwndParent, (HMENU) ID_HEADER, g_hinst,
(LPVOID) NULL)) == NULL)
return (HWND) NULL;

// Retrieve the bounding rectangle of the parent window's
// client area, and then request size and position values
// from the header control.
GetClientRect(hwndParent, &rcParent);

hdl.prc = &rcParent;
hdl.pwpos = &wp;
if (!SendMessage(hwndHeader, HDM_LAYOUT, 0, (LPARAM) &hdl))
return (HWND) NULL;

// Set the size, position, and visibility of the header control.
SetWindowPos(hwndHeader, wp.hwndInsertAfter, wp.x, wp.y,
wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);

return hwndHeader;
}

+$#A Adding an Item to a Header Control
The following example demonstrates how to use the HDM_INSERTITEM_win32_HDM_INSERTITEM message and the HD_ITEM_win32_HD_ITEM_str structure to add an item to a header control. The new item consists of a string that is left-justified within the item rectangle.
// DoInsertItem - inserts items into a header control.
// Returns the index of the newly added item.
// hwndHeader - handle of the header control
// iInsertAfter - index of the previous item
// nWidth - width of the new item
// lpsz - address of the item string
int DoInsertItem(HWND hwndHeader, int iInsertAfter,
int nWidth, LPSTR lpsz)
{
HD_ITEM hdi;
int index;

hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
hdi.pszText = lpsz;
hdi.cxy = nWidth;
hdi.cchTextMax = lstrlen(hdi.pszText);
hdi.fmt = HDF_LEFT | HDF_STRING;

index = SendMessage(hwndHeader, HDM_INSERTITEM,
(WPARAM) iInsertAfter, (LPARAM) &hdi);

return index;

}

+$#A Header Control Reference
The following messages, notification messages, and structures are associated with header controls. These elements can be grouped as follows.
Size and Position
HDM_LAYOUT_win32_HDM_LAYOUTHD_LAYOUT_win32_HD_LAYOUT_str
Inserting and Deleting Items
HDM_DELETEITEM_win32_HDM_DELETEITEMHDM_INSERTITEM_win32_HDM_INSERTITEM
Item Attributes
HDM_SETITEM_win32_HDM_SETITEMHDN_ITEMCHANGED_win32_HDN_ITEMCHANGEDHDN_ITEMCHANGING_win32_HDN_ITEMCHANGINGHDN_TRACK_win32_HDN_TRACK
Item Information
HDM_GETITEM_win32_HDM_GETITEMHDM_GETITEMCOUNT_win32_HDM_GETITEMCOUNTHDN_ITEMCLICK_win32_HDN_ITEMCLICKHDN_ITEMDBLCLICK_win32_HDN_ITEMDBLCLICKHD_ITEM_win32_HD_ITEM_strHD_NOTIFY_win32_HD_NOTIFY_str
Divider Tracking
HDN_BEGINTRACK_win32_HDN_BEGINTRACKHDN_DIVIDERDBLCLICK_win32_HDN_DIVIDERDBLCLICKHDN_ENDTRACK_win32_HDN_ENDTRACK
Hit Testing
HD_HITTESTINFO_win32_HD_HITTESTINFO_strHDM_HITTEST_win32_HDM_HITTEST
+$#A Header Control Messages
An application sends messages to add items and to control the appearance and behavior of a header control. Each message has a corresponding macro, which you can use instead of sending the message explicitly. There are the following header control messages.
HDM_DELETEITEM_win32_HDM_DELETEITEMHDM_GETITEM_win32_HDM_GETITEMHDM_GETITEMCOUNT_win32_HDM_GETITEMCOUNTHDM_HITTEST_win32_HDM_HITTEST HDM_INSERTITEM_win32_HDM_INSERTITEM
+$#A Header Control Notification Messages
A header control sends notification messages, in the form of WM_NOTIFY_win32_WM_NOTIFY messages, to its parent window to notify it about events. The following notification messages are used with header controls.
HDN_BEGINTRACK_win32_HDN_BEGINTRACKHDN_DIVIDERDBLCLICK_win32_HDN_DIVIDERDBLCLICKHDN_ENDTRACK_win32_HDN_ENDTRACK HDN_ITEMCHANGED_win32_HDN_ITEMCHANGEDHDN_ITEMCHANGING_win32_HDN_ITEMCHANGINGHDN_ITEMCLICK_win32_HDN_ITEMCLICKHDN_ITEMDBLCLICK_win32_HDN_ITEMDBLCLICK HDN_TRACK_win32_HDN_TRACK
+$#A Header Control Structures
The following structures are used with header controls.
HD_HITTESTINFO_win32_HD_HITTESTINFO_strHD_ITEM_win32_HD_ITEM_strHD_LAYOUT_win32_HD_LAYOUT_str HD_NOTIFY_win32_HD_NOTIFY_str
+$#A Header Control Styles
The following window styles are used with header controls.
HDS_BUTTONS Header items behave like buttons.
HDS_HORZ The header control is horizontal.

+$#AK Hot-Key Controls
A hot-key control is a window that enables the user to enter a combination of keystrokes to be used as a hot key. A hot key is a key combination that the user can press to perform an action quickly. (For example, a user can create a hot key that activates a given window and brings it to the top of the Z order.) The hot-key control displays the user's choices and ensures that the user selects a valid key combination.
+$#A About Hot-Key Controls
When the user enters a key combination to be used as a hot key, the names of the keys appear in the hot-key control. A key combination can consist of a modifier key (such as CTRL, ALT, or SHIFT) and an accompanying key (such as a character key, an arrow key, a function key, and so on).
After the user has chosen a key combination, the application retrieves the key combination from the hot-key control and uses it to set up a hot key in the system. The information retrieved from the hot-key control includes a flag indicating the modifier key and the virtual-key code of the accompanying key.
The application can use the information provided by a hot-key control to set up a global hot key or a thread-specific hot key. A global hot key is associated with a particular window; it allows the user to activate the window from any part of the system. An application sets a global hot key by using the WM_SETHOTKEY_win32_WM_SETHOTKEY message. Whenever the user uses a global hot key, the window specified in WM_SETHOTKEY receives a WM_SYSCOMMAND_win32_WM_SYSCOMMAND message that specifies the SC_HOTKEY value. This message activates the window that receives it. The hot key remains valid until the application that called WM_SETHOTKEY exits.
A thread-specific hot key generates a WM_HOTKEY_win32_WM_HOTKEY message that is posted to the beginning of a particular thread so that it is removed by the next iteration of the message loop. An application sets a thread-specific hot key by using the RegisterHotKey_win32_RegisterHotKey function.
+$#A Hot-Key Control Creation
You create a hot-key control by using the CreateWindowEx_win32_CreateWindowEx function, specifying the HOTKEY_CLASS window class. When the function returns a handle to the hot-key control, an application typically sets some rules about invalid hot-key combinations and provides perhaps a default key combination. If an application does not set any rules, the user can choose any key or key combination as a hot key. Most applications, however, do not allow the user to use a common key (for example, the letter A) as a hot key.
+$#A About Hot-Key Control Messages
After creating a hot-key control, an application interacts with it by using three messages: HKM_SETRULES_win32_HKM_SETRULES, HKM_SETHOTKEY_win32_HKM_SETHOTKEY, and HKM_GETHOTKEY_win32_HKM_GETHOTKEY.
An application can send the HKM_SETRULES message to specify a set of CTRL, ALT, and SHIFT key combinations that are considered invalid hot keys. If the application specifies an invalid key combination, it should also specify a default modifier combination to use when the user selects the invalid combination. When the user enters the invalid combination, the system performs a logical-OR operation on the invalid combination entered by the user and the default combination. The result is considered a valid combination; it is converted to a string and displayed in the control.
The HKM_SETHOTKEY_win32_HKM_SETHOTKEY message allows an application to set the hot key combination for a hot-key control. This message is also typically used when the hot-key control is created.
Applications use the HKM_GETHOTKEY_win32_HKM_GETHOTKEY message to retrieve the virtual-key code and modifier flags of the hot key chosen by the user of a hot-key control.
+$#A Default Hot-Key Message Processing
This section describes the window messages handled by the window procedure for the predefined HOTKEY_CLASS window class used with hot-key controls.
Message Processing performed
WM_CHAR_win32_WM_CHAR Retrieves the virtual-key code.
WM_CREATE_win32_WM_CREATE Initializes the hot-key control, sets no hot-key rules, and uses the system font.
WM_ERASEBKGND_win32_WM_ERASEBKGND Hides the caret, calls the DefWindowProc_win32_DefWindowProc function, and shows the caret again.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLGC_WANTCHARS and DLGC_WANTARROWS values.
WM_GETFONT_win32_WM_GETFONT Retrieves the font.
WM_KEYDOWN_win32_WM_KEYDOWN Calls the DefWindowProc_win32_DefWindowProc function if the key is ENTER, TAB, SPACE BAR, DEL, ESC, or BACKSPACE. If the key is SHIFT, CTRL, or ALT, it checks whether the combination is valid and, if it is, sets the hot key using the combination. Other keys are set as hot keys without their validity being checked first.
WM_KEYUP_win32_WM_KEYUP Retrieves the virtual-key code.
WM_KILLFOCUS_win32_WM_KILLFOCUS Destroys the caret.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Sets the focus to the window.
WM_NCCREATE_win32_WM_NCCREATE Sets the WS_EX_CLIENTEDGE window style.
WM_PAINT_win32_WM_PAINT Paints the hot-key control.
WM_SETFOCUS_win32_WM_SETFOCUS Creates and shows the caret.
WM_SETFONT_win32_WM_SETFONT Sets the font.
WM_SYSCHAR_win32_WM_SYSCHAR Retrieves the virtual-key code.
WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN Calls the DefWindowProc_win32_DefWindowProc function if the key is ENTER, TAB, SPACE BAR, DEL, ESC, or BACKSPACE. If the key is SHIFT, CTRL, or ALT, it checks whether the combination is valid and, if it is, sets the hot key using the combination. Other keys are set as hot keys without their validity being checked first.
WM_SYSKEYUP_win32_WM_SYSKEYUP Retrieves the virtual-key code.

+$#A Using Hot-Key Controls
· Creating a hot-key control_win32_Creating_a_Hot_Key_Control
· Retrieving and setting a hot key_win32_Retrieving_and_Setting_a_Hot_Key

+$#A Creating a Hot-Key Control
The following function creates a hot-key control, uses the HKM_SETRULES_win32_HKM_SETRULES and HKM_SETHOTKEY_win32_HKM_SETHOTKEY messages to initialize it, and returns a handle to the control. This hot-key control does not allow the user to choose a hot key that is a single unmodified key, nor does it permit the user to choose only SHIFT and a key. (These rules effectively prevent the user from choosing a hot key that might be accidentally entered while typing text.)
// InitializeHotkey - creates a hot-key control and sets rules
// and default settings for it.
// Returns the handle of the hot-key control.
// hwndDlg - handle of the parent window (dialog box)
//
// Global variable
// g_hinst - handle of the application instance
extern HINSTANCE g_hinst;
HWND WINAPI InitializeHotkey(HWND hwndDlg)
{
// Ensure that the common control DLL is loaded.
InitCommonControls();

hwndHot = CreateWindowEx(
0, // no extended styles
HOTKEY_CLASS, // class name
"", // no title (caption)
WS_CHILD | WS_VISIBLE, // style
10, 10, // position
200, 20, // size
hwndDlg, // parent window
NULL, // uses class menu
g_hinst, // instance
NULL // no WM_CREATE parameter
);

SetFocus(hwndHot);

// Set rules for invalid key combinations. If the user
// does not supply a modifier key, use ALT as a modifier.
// If the user supplies SHIFT as a modifier key, use
// SHIFT + ALT instead.
SendMessage(hwndHot, HKM_SETRULES,
(WPARAM) HKCOMB_NONE | HKCOMB_S, // invalid key combinations
MAKELPARAM(HOTKEYF_ALT, 0)); // add ALT to invalid entries

// Set CTRL + ALT + A as the default hot key for this window.
// 0x41 is the virtual-key code for 'A'.
SendMessage(hwndHot, HKM_SETHOTKEY,
MAKEWORD(0x41, HOTKEYF_CONTROL | HOTKEYF_ALT), 0);

return hwndHot;
}
+$#A Retrieving and Setting a Hot Key
After the user has chosen a hot key, an application should retrieve the hot key from the hot-key control by using the HKM_GETHOTKEY_win32_HKM_GETHOTKEY message. This message retrieves a 16-bit value that contains the virtual-key code and modifier keys describing the hot key.
The following function retrieves a key combination from a hot-key control and then uses the WM_SETHOTKEY_win32_WM_SETHOTKEY message to set a global hot key. Note that you cannot set a global hot key for a window that has the WS_CHILD window style.
// ProcessHotkey - retrieves the hot key from the hot-key control and
// sets it as the hot key for the application's main window.
// Returns TRUE if successful or FALSE otherwise.
// hwndHot - handle of the hot-key control
// hwndMain - handle of the main window
BOOL WINAPI ProcessHotkey(HWND hwndHot, HWND hwndMain)
{
WORD wHotkey;
UINT iSetResult;

// Retrieve the hot key (virtual-key code and modifiers).
wHotkey = SendMessage(hwndHot, HKM_GETHOTKEY, 0, 0);

// Use the result as wParam for WM_SETHOTKEY.
iSetResult = SendMessage(hwndMain, WM_SETHOTKEY, wHotkey, 0);

switch (iSetResult) {
case 2: // WM_SETHOTKEY succeeded
MessageBox(NULL, "Hot key previously assigned",
"Okay", MB_OK);
return TRUE;

case 1: // WM_SETHOTKEY succeeded
return TRUE;

case 0:
MessageBox(NULL, "Invalid window for hot key",
"Error", MB_OK);
return FALSE;

case -1:
MessageBox(NULL, "Invalid hot key",
"Error", MB_OK);
return FALSE;

default:
MessageBox(NULL, "Unknown error", "Error", MB_OK);
return FALSE;
}
}
+$#A Hot-Key Control Messages
The following messages are associated with hot-key controls.
HKM_GETHOTKEY_win32_HKM_GETHOTKEYHKM_SETHOTKEY_win32_HKM_SETHOTKEYHKM_SETRULES_win32_HKM_SETRULES

+$#AK Image Lists
An image list is a collection of same-sized images, each of which can be referred to by its index. Image lists are used to efficiently manage large sets of icons or bitmaps. All images in an image list are contained in a single, wide bitmap in screen device format. An image list may also include a monochrome bitmap that contains masks used to draw images transparently (icon style).
+$#A About Image Lists
The Microsoft® Win32® application programming interface (API) provides image list functions, which enable you to draw images, create and destroy image lists, add and remove images, replace images, merge images, and drag images.
To use the image list functions, you must include the common control header file in your source code files and link with the common control export library. In addition, before calling any image list function, you must use the InitCommonControls_win32_InitCommonControls function to ensure that the common control dynamic-link library (DLL) is loaded.
+$#A Types
There are two types of image lists: nonmasked and masked. A nonmasked image list consists of a color bitmap that contains one or more images. A masked image list consists of two bitmaps of equal size. The first is a color bitmap that contains the images, and the second is a monochrome bitmap that contains a series of masks ¾ one for each image in the first bitmap.
When a nonmasked image is drawn, it is simply copied into the target device context; that is, it is drawn over the existing background color of the device context. When a masked image is drawn, the bits of the image are combined with the bits of the mask, typically producing transparent areas in the bitmap where the background color of the target device context shows through. There are several drawing styles that you can specify when drawing a masked image. For example, you can specify that the image be dithered to indicate a selected object. For more information about drawing images, see Drawing Images_win32_Drawing_Images.
+$#A Image List Creation
You create an image list by calling the ImageList_Create_win32_ImageList_Create function. The parameters include the type of image list to create, the dimensions of each image, and the number of images you intend to add to the list. For a nonmasked image list, the function creates a single bitmap large enough to hold the specified number of images of the given dimensions. Then it creates a screen-compatible device context and selects the bitmap into it. For a masked image list, the function creates two bitmaps and two screen-compatible device contexts. It selects the image bitmap into one device context and the mask bitmap into the other.
The initial size of an image list is determined by the size values you specify in ImageList_Create. If you attempt to add more images than you initially specified, the image list automatically grows to accommodate the additional images. In ImageList_Create, you specify the amount of images by which the image list can grow.
If ImageList_Create_win32_ImageList_Create succeeds, it returns a handle of the HIMAGELIST type. You use this handle in other image list functions to access the image list and manage the images. You can add and remove images, copy images from one image list to another, and merge images from two different image lists. When you no longer need an image list, you can destroy it by specifying its handle in a call to the ImageList_Destroy_win32_ImageList_Destroy function.
+$#A Adding and Removing Images
You can add bitmapped images, icons, or cursors to an image list. You add bitmapped images by specifying the handles of two bitmaps in a call to the ImageList_Add_win32_ImageList_Add function. The first bitmap contains one or more images to add to the image bitmap, and the second bitmap contains the masks to add to the mask bitmap. For nonmasked image lists, the second bitmap handle is ignored; it can be set to NULL.
The ImageList_AddMasked_win32_ImageList_AddMasked function also adds bitmapped images to a masked image list. This function is similar to ImageList_Add, except that you do not specify a mask bitmap. Instead, you specify a color that the system combines with the image bitmap to automatically generate the masks. Each pixel of the specified color in the image bitmap is changed to black, and the corresponding bit in the mask is set to one. As a result, any pixel in the image that matches the specified color is transparent when the image is drawn.
The ImageList_AddIcon_win32_ImageList_AddIcon function adds an icon or cursor to an image list. If the image list is masked, ImageList_AddIcon adds the mask provided with the icon or cursor to the mask bitmap. If the image list is nonmasked, the mask for the icon or cursor is not used when drawing the image.
You can create an icon based on an image and mask in an image list by using the ImageList_GetIcon_win32_ImageList_GetIcon function. The function returns the handle of the new icon.
ImageList_Add_win32_ImageList_Add, ImageList_AddMasked_win32_ImageList_AddMasked, and ImageList_AddIcon_win32_ImageList_AddIcon assign an index to each image as it is added to an image list. The indexes are zero-based; that is, the first image in the list has an index of zero, the next has an index of one, and so on. After adding a single image, the functions return the index of the image. When more than one image is added at a time, the functions return the index of the first image.
The ImageList_Remove_win32_ImageList_Remove function removes an image from an image list.
+$#A Replacing and Merging Images
The ImageList_Replace_win32_ImageList_Replace and ImageList_ReplaceIcon_win32_ImageList_ReplaceIcon functions replace an image in an image list with a new image. ImageList_Replace replaces an image with a bitmapped image and mask, and ImageList_ReplaceIcon replaces an image with an icon or cursor.
The ImageList_Merge_win32_ImageList_Merge function merges two images, storing the new image in a new image list. The new image is created by drawing the second image transparently over the first. The mask for the new image is the result of performing a logical OR operation on the bits of the masks for the two existing images.
+$#A Drawing Images
To draw an image, you use the ImageList_Draw_win32_ImageList_Draw or ImageList_DrawEx_win32_ImageList_DrawEx function. You specify the handle of an image list, the index of the image to draw, the handle of the destination device context, a location within the device context, and one or more drawing styles.
When you specify the ILD_TRANSPARENT style, ImageList_Draw or ImageList_DrawEx uses a two-step process to draw a masked image. First, it performs a logical AND operation on the bits of the image and the bits of the mask. Then it performs a logical XOR operation on the results of the first operation and the background bits of the destination device context. This process creates transparent areas in the resulting image; that is, each white bit in the mask causes the corresponding bit in the resulting image to be transparent.
Before drawing a masked image on a solid color background, you should use the ImageList_SetBkColor_win32_ImageList_SetBkColor function to set the background color of the image list to the same color as the destination. Setting the color eliminates the need to create transparent areas in the image and enables ImageList_Draw or ImageList_DrawEx to simply copy the image to the destination device context, resulting in a significant increase in performance. To draw the image, specify the ILD_NORMAL style in a call to ImageList_Draw or ImageList_DrawEx.
You can set the background color for a masked image list at any time so that it draws correctly on any solid background. Setting the background color to CLR_NONE causes images to be drawn transparently by default. To retrieve the background color of an image list, use the ImageList_GetBkColor_win32_ImageList_GetBkColor function.
The ILD_BLEND25 and ILD_BLEND50 styles dither the image with the system highlight color. These styles are useful if you use a masked image to represent an object that the user can select. For example, you can use the ILD_BLEND50 style to draw the image when the user selects it.
A nonmasked image is copied to the destination device context using the SRCCOPY raster operation. The colors in the image appear the same regardless of the background color of the device context. The drawing styles specified in ImageList_Draw_win32_ImageList_Draw or ImageList_DrawEx_win32_ImageList_DrawEx also have no effect on the appearance of a nonmasked image.
+$#A Overlay Mask
Every image list includes a list of images to use as overlay masks. An overlay mask is an image drawn transparently over another image. Any image can be used as an overlay mask. You can specify up to four overlay masks per image list.
You add the index of an image to the list of overlay masks by using the ImageList_SetOverlayImage_win32_ImageList_SetOverlayImage function, specifying the handle of the image list, the index of an image, and the index of an overlay mask. Note that the indices for the overlay masks are one-based rather than zero-based.
You draw an overlay mask over an image using a single call to the ImageList_Draw_win32_ImageList_Draw or ImageList_DrawEx_win32_ImageList_DrawEx function. The parameters include the index of the image to draw and the index of an overlay mask. You must use the INDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK macro to specify the index of the overlay mask.
+$#A Dragging Images
The Win32 API includes functions for dragging an image on the screen. The dragging functions move an image smoothly, in color, and without any flashing of the cursor. Both masked and unmasked images can be dragged.
The ImageList_BeginDrag_win32_ImageList_BeginDrag function begins a drag operation. The parameters include the handle of the image list, the index of the image to drag, and the location of the hot spot within the image. The hot spot is a single pixel that the dragging functions recognize as the exact screen location of the image. Typically, an application sets the hot spot so that it coincides with the hot spot of the mouse cursor. The ImageList_DragMove_win32_ImageList_DragMove function moves the image to a new location.
The ImageList_DragEnter_win32_ImageList_DragEnter function sets the initial position of the drag image within a window and draws the image at the position. The parameters include the handle of the window in which to draw the image and the coordinates of the initial position within the window. The coordinates are relative to the window's upper-left corner, not the client area. The same is true for all of the image dragging functions that take coordinates as parameters. This means you must compensate for the widths of window elements, such as the border, title bar, and menu bar, when specifying the coordinates. If you specify a NULL window handle when calling ImageList_DragEnter, the dragging functions draw the image in the device context associated with the desktop window, and the coordinates are relative to the upper-left corner of the screen.
ImageList_DragEnter locks all other updates to the given window during the drag operation. If you need to do any drawing during a drag operation, such as highlighting the target of a drag and drop operation, you can temporarily hide the dragged image by using the ImageList_DragLeave_win32_ImageList_DragLeave function. Another method is to use the GetDCEx_win32_GetDCEx function with the DCX_LOCKWINDOWUPDATE value to retrieve a device context that allows you to draw. You must be careful, however, not to obliterate the dragged image.
The ImageList_SetDragCursorImage_win32_ImageList_SetDragCursorImage creates a new drag image by combining the given image (typically a mouse cursor image) with the current drag image. Because the dragging functions use the new image during a drag operation, you should use the ShowCursor_win32_ShowCursor function to hide the actual mouse cursor after calling ImageList_SetDragCursorImage. Otherwise, the system may appear to have two mouse cursors for the duration of the drag operation.
When an application calls ImageList_BeginDrag_win32_ImageList_BeginDrag, the system creates an temporary, internal image list can copies the specified drag image to the internal list. You can retrieves the handle of the temporary drag image list by using the ImageList_GetDragImage_win32_ImageList_GetDragImage function. The function also retrieves the current drag position, and the offset of the drag image relative to the drag position.
+$#A Image Information
The Win32 API includes a number of functions that retrieve information from an image list. The ImageList_GetImageInfo_win32_ImageList_GetImageInfo function fills an IMAGEINFO_win32_IMAGEINFO_str structure with information about a single image, including the handles of the image and mask bitmaps, the number of color planes and bits per pixel, and the bounding rectangle of the image within the image bitmap. You can use this information to directly manipulate the bitmaps for the image.
The ImageList_GetImageCount_win32_ImageList_GetImageCount function retrieves the number of images in an image list.
+$#A Using Image Lists
· Creating an image list_win32_Creating_an_Image_List.
· Dragging an image_win32_Dragging_an_Image.

+$#A Creating an Image List
To create an image list, use the ImageList_Create_win32_ImageList_Create function, specifying the type of image list to create (unmasked or masked); the width and height, in pixels, of the images; the number of images you intend to add to the image list; and the amount of images by which the list can grow when it is resized to accommodate additional images. The common control DLL contains the executable code for the image list functions. You must ensure that the library is loaded by using the InitCommonControls_win32_InitCommonControls function before making any calls to image list functions.
The following example creates a masked image list and uses the ImageList_AddIcon_win32_ImageList_AddIcon function to add two icons to the list.
// AddIconsToImageList - creates a masked image list and adds some
// icons to it.
// Returns the handle of the new image list.
// hinst - handle of the application instance
//
// Global variables and constants
// g_nBird and g_nTree - indexes of the images
// CX_ICON and CY_ICON - width and height of the icon
// NUM_ICONS - number of icons to add to the image list
extern int g_nBird, g_nTree;

#define CX_ICON 32
#define CY_ICON 32
#define NUM_ICONS 3

HIMAGELIST AddIconsToImageList(HINSTANCE hinst)
{
HIMAGELIST himlIcons; // handle of new image list
HICON hicon; // handle of icon

// Ensure that the common control DLL is loaded.
InitCommonControls();

// Create a masked image list large enough to hold the icons.
himlIcons = ImageList_Create(CX_ICON, CY_ICON, TRUE, NUM_ICONS, 0);

// Load the icon resources, and add the icons to the image list.
hicon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BIRD));
g_nBird = ImageList_AddIcon(himlIcons, hicon);

hicon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_TREE));
g_nTree = ImageList_AddIcon(himlIcons, hicon);

return himlIcons;
}

+$#A Dragging an Image
Dragging an image involves calls to the ImageList_BeginDrag_win32_ImageList_BeginDrag, ImageList_DragMove_win32_ImageList_DragMove, and ImageList_EndDrag_win32_ImageList_EndDrag functions. ImageList_BeginDrag begins a drag operation by combining an image with the current mouse cursor, setting a hot spot in the image, and drawing the image in its initial position. The function also prevents the system from updating any other parts of the display. ImageList_DragMove drags the image to a new location. ImageList_EndDrag ends a drag operation, permitting the system to update any part of the display.
The remainder of this section provides an example using four functions that demonstrates how to drag an image. The first function shows how to draw an image in a window's client area, and subsequent functions show how to drag the image.
+$#A Drawing the Image
The following function draws an image and saves the client coordinates of the image's bounding rectangle. A subsequent function uses the bounding rectangle to determine whether the user has clicked the image.
// DrawTheImage - draws an image transparently and saves
// the bounding rectangle of the drawn image
// Returns TRUE if successful or FALSE otherwise.
// hwnd - handle of the window in which to draw the image
// himl - handle of the image list that contains the image
// cx and cy - client coordinates for the upper-left corner of the image
//
// Global variables and constants
// g_nImage - index of the image to draw
// g_rcImage - bounding rectangle of the image
// CX_IMAGE and CY_IMAGE - width and height of the image
extern int g_nImage;
extern RECT g_rcImage;

#define CX_IMAGE 32
#define CY_IMAGE 32

BOOL DrawTheImage(HWND hwnd, HIMAGELIST himl, int cx, int cy)
{
HDC hdc;

if ((hdc = GetDC(hwnd)) == NULL)
return FALSE;
if (!ImageList_Draw(himl, g_nImage, hdc, cx, cy, ILD_TRANSPARENT))
return FALSE;
ReleaseDC(hwnd, hdc);

SetRect(&g_rcImage, cx, cy, CX_IMAGE + cx, CY_IMAGE + cy);

return TRUE;
}

+$#A Beginning the Drag Operation
The following function is intended to be called in response to a mouse button-down message, such as WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN. The function determines whether the user has clicked within the bounding rectangle of the image. If the user has clicked, the function captures the mouse input, erases the image from the client area, and calculates the position for the hot spot within the image. The function sets the hot spot to coincide with the hot spot of the mouse cursor. Then the function begins the drag operation by calling ImageList_BeginDrag_win32_ImageList_BeginDrag.
// StartDragging - begins dragging a bitmap.
// Returns TRUE if successful or FALSE otherwise.
// hwnd - handle of the window in which the bitmap is dragged
// ptCur - coordinates of the cursor
// himl - handle of the image list
//
// Global variables
// g_rcImage - bounding rectangle of the image to drag
// g_nImage - index of the image
// g_ptHotSpot - location of the image's hot spot
// g_cxBorder and g_cyBorder - width and height of border
// g_cyCaption and g_cyMenu - height of title bar and menu bar
extern RECT g_rcImage;
extern int g_nImage;
extern POINT g_ptHotSpot;

BOOL StartDragging(HWND hwnd, POINT ptCur, HIMAGELIST himl)
{
// Return if the cursor is not in the bounding rectangle of
// the image.
if (!PtInRect(&g_rcImage, ptCur))
return FALSE;

// Capture the mouse input.
SetCapture(hwnd);

// Erase the image from the client area.
InvalidateRect(hwnd, &g_rcImage, TRUE);
UpdateWindow(hwnd);

// Calculate the location of the hot spot, and save it.
g_ptHotSpot.x = ptCur.x - g_rcImage.left;
g_ptHotSpot.y = ptCur.y - g_rcImage.top;

// Begin the drag operation.
if (!ImageList_BeginDrag(himl, g_nImage,
g_ptHotSpot.x, g_ptHotSpot.y))
return FALSE;

// Set the initial location of the image, and make it visible.
// Because the ImageList_DragEnter function expects coordinates to
// be relative to the upper-left corner of the given window, the
// width of the border, title bar, and menu bar need to be taken
// into account.
ImageList_DragEnter(hwnd, ptCur.x + g_cxBorder,
ptCur.y + g_cyBorder + g_cyCaption + g_cyMenu);

g_fDragging = TRUE;

return TRUE;
}

+$#A Moving the Image
The following function, which drags the image to a new location, is intended to be called in response to the WM_MOUSEMOVE_win32_WM_MOUSEMOVE message.
// MoveTheImage - drags an image to the specified coordinates.
// Returns TRUE if successful or FALSE otherwise.
// ptCur - new coordinates for the image
BOOL MoveTheImage(POINT ptCur)
{
if (!ImageList_DragMove(ptCur.x, ptCur.y))
return FALSE;

return TRUE;
}

+$#A Ending the Drag Operation
The following function ends the drag operation and draws the image in its final location. It also releases the mouse capture.
// StopDragging - ends a drag operation and draws the image
// at its final location.
// Returns TRUE if successful or FALSE otherwise.
// hwnd - handle of the window in which the bitmap is dragged
// himl - handle of the image list
// ptCur - coordinates of the cursor
//
// Global variable
// g_ptHotSpot - location of the image's hot spot

extern POINT g_ptHotSpot;

BOOL StopDragging(HWND hwnd, HIMAGELIST himl, POINT ptCur)
{
ImageList_EndDrag();
ImageList_DragLeave(hwnd)

g_fDragging = FALSE;

DrawTheImage(hwnd, himl, ptCur.x - g_ptHotSpot.x,
ptCur.y - g_ptHotSpot.y);

ReleaseCapture();
return TRUE;
}

+$#A Image List Reference
The following functions, macros, and structures are associated with image lists. These elements can be grouped as follows.
Creating Image Lists
ImageList_Create_win32_ImageList_CreateImageList_Destroy_win32_ImageList_DestroyImageList_LoadBitmap_win32_ImageList_LoadBitmapImageList_LoadImage_win32_ImageList_LoadImage
Adding and Removing Images
ImageList_Add_win32_ImageList_AddImageList_AddIcon_win32_ImageList_AddIconImageList_AddMasked_win32_ImageList_AddMaskedImageList_Remove_win32_ImageList_Remove
Replacing and Merging Images
ImageList_Replace_win32_ImageList_ReplaceImageList_ReplaceIcon_win32_ImageList_ReplaceIconImageList_Merge_win32_ImageList_Merge
Drawing Images
ImageList_Draw_win32_ImageList_DrawImageList_DrawEx_win32_ImageList_DrawExImageList_ExtractIcon_win32_ImageList_ExtractIconImageList_GetBkColor_win32_ImageList_GetBkColorImageList_GetIcon_win32_ImageList_GetIconImageList_SetBkColor_win32_ImageList_SetBkColorINDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK
Dragging Images
ImageList_BeginDrag_win32_ImageList_BeginDragImageList_DragEnter_win32_ImageList_DragEnterImageList_DragLeave_win32_ImageList_DragLeaveImageList_DragMove_win32_ImageList_DragMoveImageList_DragShowNolock_win32_ImageList_DragShowNolockImageList_EndDrag_win32_ImageList_EndDragImageList_GetDragImage_win32_ImageList_GetDragImageImageList_SetDragCursorImage_win32_ImageList_SetDragCursorImageImageList_SetOverlayImage_win32_ImageList_SetOverlayImage
Image Information
ImageList_GetIconSize_win32_ImageList_GetIconSizeImageList_GetImageCount_win32_ImageList_GetImageCountImageList_GetImageInfo_win32_ImageList_GetImageInfoImageList_SetIconSize_win32_ImageList_SetIconSizeIMAGEINFO_win32_IMAGEINFO_str
Storing Image Lists
ImageList_Read_win32_ImageList_ReadImageList_Write_win32_ImageList_Write
+$#A Image List Functions
The following functions are used with image lists.
ImageList_Add_win32_ImageList_AddImageList_AddMasked_win32_ImageList_AddMaskedImageList_BeginDrag_win32_ImageList_BeginDragImageList_Create_win32_ImageList_CreateImageList_Destroy_win32_ImageList_DestroyImageList_DragEnter_win32_ImageList_DragEnterImageList_DragLeave_win32_ImageList_DragLeaveImageList_DragMove_win32_ImageList_DragMoveImageList_DragShowNolock_win32_ImageList_DragShowNolockImageList_Draw_win32_ImageList_DrawImageList_DrawEx_win32_ImageList_DrawExImageList_EndDrag_win32_ImageList_EndDragImageList_ExtractIcon_win32_ImageList_ExtractIconImageList_GetBkColor_win32_ImageList_GetBkColorImageList_GetDragImage_win32_ImageList_GetDragImageImageList_GetIcon_win32_ImageList_GetIconImageList_GetIconSize_win32_ImageList_GetIconSizeImageList_GetImageCount_win32_ImageList_GetImageCountImageList_GetImageInfo_win32_ImageList_GetImageInfoImageList_LoadBitmap_win32_ImageList_LoadBitmapImageList_LoadImage_win32_ImageList_LoadImageImageList_Merge_win32_ImageList_MergeImageList_Read_win32_ImageList_ReadImageList_Remove_win32_ImageList_RemoveImageList_Replace_win32_ImageList_ReplaceImageList_ReplaceIcon_win32_ImageList_ReplaceIconImageList_SetBkColor_win32_ImageList_SetBkColorImageList_SetDragCursorImage_win32_ImageList_SetDragCursorImageImageList_SetIconSize_win32_ImageList_SetIconSizeImageList_SetOverlayImage_win32_ImageList_SetOverlayImageImageList_Write_win32_ImageList_Write
+$#A Image List Macro
The following macros are used with image lists.
ImageList_AddIcon_win32_ImageList_AddIcon INDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK
+$#A Image List Structure
The following structure is used with image lists.
IMAGEINFO_win32_IMAGEINFO_str

+$#AK List View Controls
A list view control is a window that displays a collection of items, each item consisting of an icon and a label. List view controls provide several ways of arranging items and displaying individual items. For example, additional information about each item can be displayed in columns to the right of the icon and label.
+$#A About List View Controls
You can create a list view control by calling the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_LISTVIEW window class. This window class is registered when the common controls dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls_win32_InitCommonControls function.
An application sends messages to a list view control to add, remove, arrange, and otherwise manipulate items. Each message has a macro that you can use instead of sending the message explicitly.
Like most common controls, a list view control sends notification messages to its parent window in the form of WM_NOTIFY_win32_WM_NOTIFY messages. For more information about common controls, see Common Controls_win32_Common_Controls.
+$#A Views and Styles
List view controls can display their contents in four different views. The current view is specified by the control's window style. Additional window styles specify the alignment of items and control-specific functionality of the list view control. Information about the four views follows.
View Description
Icon view Specified by the LVS_ICON window style. Each item appears as a full-sized icon with a label below it. The user can drag the items to any location in the list view window.
Small icon view Specified by the LVS_SMALLICON window style. Each item appears as a small icon with the label to the right of it. The user can drag the items to any location.
List view Specified by the LVS_LIST window style. Each item appears as a small icon with a label to the right of it. Items are arranged in columns and cannot be dragged to any arbitrary location by the user.
Report view Specified by the LVS_REPORT window style. Each item appears on its own line with information arranged in columns. The leftmost column contains the small icon and label, and subsequent columns contain subitems as specified by the application. Unless the LVS_NOCOLUMNHEADER window style is also specified, each column has a header.

You can change the view type after a list view control is created. To retrieve and change the window style, use the GetWindowLong_win32_GetWindowLong and SetWindowLong_win32_SetWindowLong functions. To determine the window styles that correspond to the current view, use the LVS_TYPEMASK value.
You can control the way items are arranged in icon or small icon view by specifying either the LVS_ALIGNTOP (default) or LVS_ALIGNLEFT window style. You can change the alignment after a list view control is created. To isolate the window styles that specify the alignment of items, use the LVS_ALIGNMASK value.
Additional window styles control other options ¾ for example, whether a user can edit labels in place, whether more than one item can be selected at a time, and so on.
+$#A List View Image Lists
The icons for list view items are contained in image lists, which you create and assign to the list view control. One image list contains the full-sized icons used in icon view, and a separate image list contains smaller versions of the same icons for use in other views. You can also specify a third image list that contains state images, which are displayed next to an item's icon to indicate an application-defined state.
You assign an image list to a list view control by using the LVM_SETIMAGELIST_win32_LVM_SETIMAGELIST message, specifying whether the image list contains full-sized icons, small icons, or state images. You can use the GetSystemMetrics_win32_GetSystemMetrics function to determine appropriate dimensions for the full-sized and small icons and the ImageList_Create_win32_ImageList_Create function to create the image lists. For more information about image lists, see Image Lists_win32_Image_Lists.
You can retrieve the handle of an image list currently assigned to a list view control by using the LVM_GETIMAGELIST_win32_LVM_GETIMAGELIST message.
The full-sized and small icon image lists typically contain icons for each type of list view item. You do not need to create both of these image lists if only one is used ¾ for example, if a list view control is never in icon view. If you create both image lists, they must contain the same images in the same order because a single value is used to identify a list view item's icon in both image lists.
The full-sized and small icon image lists can also contain overlay images, which are designed to be superimposed on item icons. A nonzero value in bits 8 through 11 of a list view item's state specifies the one-based index of an overlay image (zero indicates no overlay image). Because a 4-bit, one-based index is used, overlay images must be among the first 15 images in the image lists. For more information about list view item states, see Item States_win32_Item_States.
If a state image list is specified, a list view control reserves space to the left of each item's icon for a state image. An application can use state images, such as checked and cleared check boxes, to indicate application-defined item states. A nonzero value in bits 12 through 15 specifies the one-based index of a state image (zero indicates no state image). State images are typically not used in icon view.
By default, a list view control destroys the image lists assigned to it when it is destroyed. However, if a list view control has the LVS_SHAREIMAGELISTS window style, the application is responsible for destroying the image lists when they are no longer in use. You should specify this style if you assign the same image lists to multiple list view controls; otherwise, more than one control might try to destroy the same image list.
+$#A Items and Subitems
Each item in a list view control consists of an icon, a label, a current state, and an application-defined value. One or more subitems can also be associated with each item. A subitem is a string that, in report view, can be displayed in a column to the right of an item's icon and label. All items in a list view control have the same number of subitems. By using list view messages, you can add, modify, retrieve information about, and delete items. You can also find items with specific attributes.
The LV_ITEM_win32_LV_ITEM_str structure defines a list view item or subitem. The iItem member is the zero-based index of the item. The iSubItem member is the one-based index of a subitem or zero if the structure contains information about an item. Additional members specify the item's text, icon, state, and item data. Item data is an application-defined value associated with a list view item.
To add an item to a list view control, use the LVM_INSERTITEM_win32_LVM_INSERTITEM message, specifying the address of an LV_ITEM structure. Before adding multiple items, you can send the control an LVM_SETITEMCOUNT_win32_LVM_SETITEMCOUNT message, specifying the number of items the control will ultimately contain. This message enables the list view control to reallocate its internal data structures only once rather than every time you add an item. You can determine the number of items in a list view control by using the LVM_GETITEMCOUNT_win32_LVM_GETITEMCOUNT message.
To change the attributes of a list view item, use the LVM_SETITEM_win32_LVM_SETITEM message, specifying the address of an LV_ITEM_win32_LV_ITEM_str structure. The mask member of this structure specifies the item attributes you want to change. To change only the text of an item or subitem, use the LVM_SETITEMTEXT_win32_LVM_SETITEMTEXT message.
To retrieve information about a list view item, use the LVM_GETITEM_win32_LVM_GETITEM message, specifying the address of the LV_ITEM structure to fill in. The mask member of this structure specifies the item attributes to be retrieved. To retrieve only an item or subitem's text, use the LVM_GETITEMTEXT_win32_LVM_GETITEMTEXT message.
To delete a list view item, use the LVM_DELETEITEM_win32_LVM_DELETEITEM message. You can delete all items in a list view control by using the LVM_DELETEALLITEMS_win32_LVM_DELETEALLITEMS message.
+$#A Item States
An item's state is a value that specifies the item's availablility, indicates user actions, or otherwise reflects the item's status. A list view control changes some state bits, such as when the user selects an item. An application might change other state bits to disable or hide the item or to specify an overlay image or state image. For more information about overlay images and state images, see List View Image Lists_win32_List_View_Image_Lists.
An item's state is specified by the state member of the LV_ITEM_win32_LV_ITEM_str structure. When you specify or change an item's state, the stateMask member specifies which state bits you want to change. You can change an item's state by using the LVM_SETITEMSTATE_win32_LVM_SETITEMSTATE message. You can specify an item's state when you create it or when you change its attributes by using the LMV_SETITEM message. To determine an item's current state, use the LVM_GETITEMSTATE_win32_LVM_GETITEMSTATE or LVM_GETITEM_win32_LVM_GETITEM message.
To set an item's overlay image, the stateMask member of the LV_ITEM structure must include the LVIS_OVERLAYMASK value, and the state member must include the one-based index of the overlay image shifted left 8 bits by using the INDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK macro. The index can be zero to specify no overlay image.
To set an item's state image, the stateMask member of the LV_ITEM_win32_LV_ITEM_str structure must include the LVIS_STATEIMAGEMASK value, and the state member must include the one-based index of the state image shifted left 12 bits by using the INDEXTOSTATEIMAGEMASK_win32_INDEXTOSTATEIMAGEMASK macro. The index can be zero to specify no state image.
+$#A Callback Items and the Callback Mask
A callback item is a list view item for which the application ¾ rather than the control ¾ stores the text, icon, or both. Although a list view control can store these attributes for you, you may want to use callback items if your application already maintains some of this information. The callback mask specifies which item state bits are maintained by the application, and it applies to the whole control rather than to a specific item. The callback mask is zero by default, meaning that the list view control tracks all item states. If an application uses callback items or specifies a nonzero callback mask, it must be able to supply list view item attributes on demand.
A list view control requests any information that it needs to display an item by sending its owner window an LVN_GETDISPINFO_win32_LVN_GETDISPINFO notification message. The specified LV_DISPINFO_win32_LV_DISPINFO_str structure identifies the item or subitem and indicates which attributes the application must supply. If item attributes or state bits that are maintained by the application change, the list view control sends its owner window an LVN_SETDISPINFO_win32_LVN_SETDISPINFO notification message. This notification enables the application to update its information.
If you change a callback item's attributes, you can use the LVM_UPDATE_win32_LVM_UPDATE message to force the control to repaint the item. This message also arranges the list view control if it has the LVS_AUTOARRANGE style. You can use the LVM_REDRAWITEMS_win32_LVM_REDRAWITEMS message to redraw a range of items by invaliding the corresponding portions of the list view control's client area.
You can define a callback item by specifying appropriate values for the pszText and iImage members of the LV_ITEM_win32_LV_ITEM_str structure. If the application maintains the item's or subitem's text, you should specify the LPSTR_TEXTCALLBACK value for the pszText member. If the application keeps track of the icon for the item, you should specify the I_IMAGECALLBACK value for the iImage member.
To set the callback mask for a list view control, use the LVM_SETCALLBACKMASK_win32_LVM_SETCALLBACKMASK message. A one bit in the callback mask means the application maintains the corresponding state bit for each list view item. To get the current callback mask, use the LVM_GETCALLBACKMASK_win32_LVM_GETCALLBACKMASK message.
By effectively using callback items and the callback mask, you can ensure that each item attribute is maintained in only one place. Doing this can simplify your application, but the only space saved is the memory that would otherwise be required to store item labels and subitem text.
+$#A Columns
Columns control the way items and their subitems are displayed in report view. Each column has a title and width and is associated with a specific subitem (subitem zero is the item's icon and label). The attributes of a column are defined by an LV_COLUMN_win32_LV_COLUMN_str structure.
To add a column to a list view control, use the LVM_INSERTCOLUMN_win32_LVM_INSERTCOLUMN message. To delete a column, use the LVM_DELETECOLUMN_win32_LVM_DELETECOLUMN message. You can retrieve and change the properties of an existing column by using the LVM_GETCOLUMN_win32_LVM_GETCOLUMN and LVM_SETCOLUMN_win32_LVM_SETCOLUMN messages.
To retrieve or change a column's width, use the LVM_GETCOLUMNWIDTH_win32_LVM_GETCOLUMNWIDTH and LVM_SETCOLUMNWIDTH_win32_LVM_SETCOLUMNWIDTH messages. You can determine the minimum column width necessary to display all of a string by using the LVM_GETSTRINGWIDTH_win32_LVM_GETSTRINGWIDTH message.
Unless the LVS_NOCOLUMNHEADER window style is specified, column headers appear in report view. The user can click a column header, causing an LVN_COLUMNCLICK_win32_LVN_COLUMNCLICK notification message to be sent to the parent window. Typically, the parent window sorts the list view control by the specified column when this clicking occurs. The user can also drag the column guides between the headers to size the columns.
+$#A Arranging, Sorting, and Finding
You can use list view messages to arrange and sort items and to find items based on their attributes or positions. Arranging repositions items to align on a grid, but the indexes of the items do not change. Sorting changes the sequence of items (and their corresponding indexes) and then repositions them accordingly. You can arrange items only in icon and small icon views, but you can sort items in any view.
To arrange items, use the LVM_ARRANGE_win32_LVM_ARRANGE message. You can ensure that items are arranged at all times by specifying the LVS_AUTOARRANGE window style.
To sort items, use the LVM_SORTITEMS_win32_LVM_SORTITEMS message. When you sort using this message, you specify an application-defined callback function that is called to compare the relative order of any two items. By supplying an appropriate comparison function, you can sort items by their label, by any subitem, or by any other property. Note that sorting items does not reorder the corresponding subitems. Thus, if any subitems are not callback items, you must regenerate the subitems after sorting.
You can ensure that a list view control is always sorted by specifying the LVS_SORTASCENDING or LVS_SORTDESCENDING window style. You cannot supply a comparison function when using these window styles. The list view sorts the items in ascending or descending order by label.
You can find a list view item with specific properties by using the LVM_FINDITEM_win32_LVM_FINDITEM message. You can find a list view item that is in a specified state and bears a specified geometrical relationship to a given item by using the LVM_GETNEXTITEM_win32_LVM_GETNEXTITEM message. For example, you can retrieve the next selected item to the right of a specified item.
+$#A List View Item Position
Every list view item has a position and size, which you can retrieve and set using messages. You can also determine which item, if any, is at a specified position. The position of list view items is specified in view coordinates, which are client coordinates offset by the scroll position.
To retrieve and set an item's position, use the LVM_GETITEMPOSITION_win32_LVM_GETITEMPOSITION and LVM_SETITEMPOSITION_win32_LVM_SETITEMPOSITION messages. LVM_GETITEMPOSITION works for all views, but LVM_SETITEMPOSITION works only for icon and small icon views.
You can determine which item, if any, is at a particular location by using the LVM_HITTEST_win32_LVM_HITTEST message.
To get the bounding rectangle for a list item or for only its icon or label, use the LVM_GETITEMRECT_win32_LVM_GETITEMRECT message.
+$#A Scroll Position
Unless the LVS_NOSCROLL window style is specified, a list view control can be scrolled to show more items than can fit in the client area of the control. You can get a list view control's scroll position and related information, scroll a list view control by a specified amount, or scroll a list view control such that a specified list item is visible.
In icon view or small icon view, the current scroll position is defined by the view origin. The view origin is the set of coordinates, relative to the visible area of the list view control, that correspond to the view coordinates (0, 0). To get the current view origin, use the LVM_GETORIGIN_win32_LVM_GETORIGIN message. This message should be used only in icon or small icon view; it returns an error in list or report view.
In list or report view, the current scroll position is defined by the top index. The top index is the index of the first visible item in the list view control. To get the current top index, use the LVM_GETTOPINDEX_win32_LVM_GETTOPINDEX message. This message returns a valid result only in list or report view; it returns zero in icon or small icon view.
You can use the LVM_GETVIEWRECT_win32_LVM_GETVIEWRECT message to get the bounding rectangle of all items in a list view control, relative to the visible area of the control.
The LVM_GETCOUNTPERPAGE_win32_LVM_GETCOUNTPERPAGE message returns the number of items that fit in one page of the list view control. This message returns a valid result only in list and report views; in icon and small icon views, it returns the total number of items.
To scroll a list view control by a specific amount, use the LVM_SCROLL_win32_LVM_SCROLL message. Using the LVM_ENSUREVISIBLE_win32_LVM_ENSUREVISIBLE message, you can scroll the list view control, if necessary, to ensure that a specified item is visible.
+$#A Label Editing
A list view control that has the LVS_EDITLABELS window style enables a user to edit item labels in place. The user begins editing by clicking the label of an item that has the focus. An application can begin editing automatically by using the LVM_EDITLABEL_win32_LVM_EDITLABEL message. The list view control notifies the parent window when editing begins and when it is canceled or completed. When editing is completed, the parent window is responsible for updating the item's label, if appropriate.
When label editing begins, a list view control sends its parent window an LVN_BEGINLABELEDIT_win32_LVN_BEGINLABELEDIT notification message. You can process this message to allow selective editing of specific labels; returning a nonzero value prevents label editing.
When label editing is canceled or completed, a list view control sends its parent window an LVN_ENDLABELEDIT_win32_LVN_ENDLABELEDIT notification message. The lParam parameter is the address of an LV_DISPINFO_win32_LV_DISPINFO_str structure identifying the item and specifying the edited text. The parent window is responsible for updating the item's label, if appropriate, perhaps after validating the edited string. If editing is canceled, the iItem member is - 1.
During label editing, you can get the handle of the edit control used for label editing by using the LVM_GETEDITCONTROL_win32_LVM_GETEDITCONTROL message. To limit the amount of text a user can enter, you can send the edit control an EM_LIMITTEXT_win32_EM_LIMITTEXT message. You can even subclass the edit control to intercept and discard invalid characters. The edit control is created after the LVN_BEGINLABELEDIT_win32_LVN_BEGINLABELEDIT notification message is sent.
+$#A List View Colors
An application can retrieve and set three colors for a list view control. To retrieve and set the text color, use the LVM_GETTEXTCOLOR_win32_LVM_GETTEXTCOLOR and LVM_SETTEXTCOLOR_win32_LVM_SETTEXTCOLOR messages. To retrieve and set the text background color, use the LVM_GETTEXTBKCOLOR_win32_LVM_GETTEXTBKCOLOR and LVM_SETTEXTBKCOLOR_win32_LVM_SETTEXTBKCOLOR messages. To retrieve and set the window background color, use the LVM_GETBKCOLOR_win32_LVM_GETBKCOLOR and LVM_SETBKCOLOR_win32_LVM_SETBKCOLOR messages.
+$#A List View Notification Messages
A list view control sends notification messages to its parent window in the form of WM_NOTIFY_win32_WM_NOTIFY messages. The following notification messages are sent by a list view control.
Notification message Description
LVN_BEGINDRAG_win32_LVN_BEGINDRAG Signals the start of a drag and drop operation.
LVN_BEGINLABELEDIT_win32_LVN_BEGINLABELEDIT Signals the start of in-place label editing.
LVN_BEGINRDRAG_win32_LVN_BEGINRDRAG Signals the start of a drag and drop operation, using the right mouse button.
LVN_COLUMNCLICK_win32_LVN_COLUMNCLICK Indicates that the user clicked a column header in report view.
LVN_DELETEALLITEMS_win32_LVN_DELETEALLITEMS Signals the deletion of all list view items.
LVN_DELETEITEM_win32_LVN_DELETEITEM Signals the deletion of a specific item.
LVN_ENDLABELEDIT_win32_LVN_ENDLABELEDIT Signals the end of label editing.
LVN_GETDISPINFO_win32_LVN_GETDISPINFO Requests information that the list view control requires to display an item.
LVN_INSERTITEM_win32_LVN_INSERTITEM Signals the insertion of a new list view item.
LVN_ITEMCHANGED_win32_LVN_ITEMCHANGED Indicates that an item has changed.
LVN_ITEMCHANGING_win32_LVN_ITEMCHANGING Indicates that an item is in the process of changing and enables the parent window to accept or deny the change.
LVN_KEYDOWN_win32_LVN_KEYDOWN Signals a keyboard event.
LVN_SETDISPINFO_win32_LVN_SETDISPINFO Notifies a parent window that it must update the information it maintains for an item.

+$#A Default List View Message Processing
This section describes the window message processing performed by a list view control. Messages specific to list view controls are discussed elsewhere and are, therefore, not included here.
Message Processing performed
WM_CHAR_win32_WM_CHAR Searches for a list view item that begins with the specified character and, if the item is found, selects and sets the focus to the item. Multiple characters received within a time-out interval are concatenated, and the list view control searches for an item that begins with the resulting string.
WM_COMMAND_win32_WM_COMMAND Processes the EN_UPDATE_win32_EN_UPDATE and EN_KILLFOCUS_win32_EN_KILLFOCUS notification messages and forwards all other edit control notifications to the parent window.
WM_CREATE_win32_WM_CREATE Performs additional initialization. If the LVS_SHAREIMAGELISTS window style is not specified, the list view control creates the icon and small icon image lists at this point.
WM_DESTROY_win32_WM_DESTROY Frees resources.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the window background using the current background color for the list view control. If the background color is the CLR_NONE value, the list view control forwards the message to the parent window.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLGC_WANTTAB and DLGC_WANTARROWS values.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the current label font.
WM_HSCROLL_win32_WM_HSCROLL Scrolls the list view control horizontally.
WM_KEYDOWN_win32_WM_KEYDOWN Processes the SPACEBAR, ENTER, and arrow keys and sends a LVN_KEYDOWN_win32_LVN_KEYDOWN notification message to the parent window.
WM_KILLFOCUS_win32_WM_KILLFOCUS Repaints the focused list item, if any, and sends a NM_KILLFOCUS_win32_NM_KILLFOCUS notification message to the parent window.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Sends the parent window an NM_DBLCLK_win32_NM_DBLCLK notification message.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Processed in different ways depending on whether a click or drag operation is being initiated. To determine which operation is involved, the list view control enters a modal message loop until either the button is released or the mouse is moved. In the case of a click, the list view control may change which item has the focus and which items are selected, taking into account the cursor position, the state of the SHIFT and CTRL keys, and so on. Then the list view control sends its parent window an NM_CLICK_win32_NM_CLICK notification message. If dragging begins over an item, the list view control selects and sets the focus to the item. Then it sends an LVN_BEGINDRAG_win32_LVN_BEGINDRAG notification message to the parent window. The parent window is responsible for actually carrying out the drag operation. If dragging begins over the window background, the list view control enters another modal message loop, enabling the user to form a rectangle by dragging the mouse. Items within the rectangle are selected.
WM_NCCREATE_win32_WM_NCCREATE Allocates and initializes an internal data structure and then calls the DefWindowProc_win32_DefWindowProc function.
WM_NCDESTROY_win32_WM_NCDESTROY Frees resources allocated by the list view control. Unless the LVS_SHAREIMAGELISTS style is used, this includes deleting the full-sized and small image lists.
WM_NOTIFY_win32_WM_NOTIFY Processes header control notification messages.
WM_PAINT_win32_WM_PAINT Displays any items in the update region. For callback items, the control first sends an LVN_GETDISPINFO_win32_LVN_GETDISPINFO notification message to the owner window to request display information.
WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN Processed the same way as the WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message, except that the control sends an NM_RCLICK_win32_NM_RCLICK notification message (instead of NM_CLICK_win32_NM_CLICK) and an LVN_BEGINRDRAG_win32_LVN_BEGINRDRAG notification message (instead of LVN_BEGINDRAG_win32_LVN_BEGINDRAG).
WM_SETFOCUS_win32_WM_SETFOCUS Repaints the focused list item, if any, and sends an NM_SETFOCUS_win32_NM_SETFOCUS notification message to the parent window.
WM_SETFONT_win32_WM_SETFONT Saves the specified font handle, forwards the message to the header window, if any, and repaints using the new font.
WM_SETREDRAW_win32_WM_SETREDRAW Turns redrawing on or off.
WM_TIMER_win32_WM_TIMER Begins editing of an item label. If the user clicks the label of the focused item, the list view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the list view control to not enter edit mode if the user double-clicks the label.
WM_VSCROLL_win32_WM_VSCROLL Scrolls the list view control vertically.
WM_WINDOWPOSCHANGED_win32_WM_WINDOWPOSCHANGED Updates the window scroll bars. If the current view is icon or small icon view and the LVS_AUTOARRANGE style is specified, the list view control also arranges the list items.
WM_WININICHANGE_win32_WM_WININICHANGE Processes changes to system metrics.

+$#A Using List View Controls
· Creating a list view control_win32_Creating_a_List_View_Control
· Initializing the image lists for a list view control_win32_Initializing_the_Image_Lists_for_a_List_View_Control
· Adding columns to a list view control_win32_Adding_Columns_to_a_List_View_Control
· Adding items to a list view control_win32_Adding_Items_to_a_List_View_Control
· Processing the WM_NOTIFY message_win32_Processing_the_WM_NOTIFY_Message
· Changing list view styles_win32_Changing_List_View_Styles

+$#A Creating a List View Control
To create a list view control, use the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_LISTVIEW window class. The list view window class is registered in the application's address space when the common controls dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls_win32_InitCommonControls function.
The following example creates a list view control and then calls application-defined functions that add image lists, columns, and list view items. The window style specifies the list view control's initial view and other options. This example specifies report view, which enables the user to edit the labels of list view items.
// CreateListView - creates a list view control.
// Returns the handle of the new control if successful or NULL
// otherwise.
// hwndParent - handle of the control's parent window
// pfData - file containing list view items
HWND WINAPI CreateListView(HWND hwndParent, FILE *pfData)
{
HWND hwndLV;

// Force the common controls DLL to be loaded.
InitCommonControls();

// Create the list view window.
hwndLV = CreateWindow(WC_LISTVIEW, "",
WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
hwndParent, NULL, g_hinst, NULL);
if (hwndLV == NULL)
return NULL;

// Call application-defined functions to initialize the
// image lists, add columns, and add some items.
if (!InitListViewImageLists(hwndLV) ||
!InitListViewColumns(hwndLV) ||
!InitListViewItems(hwndLV, pfData)) {
DestroyWindow(hwndLV);
return FALSE;
}
return hwndLV; // return the control's handle
}

+$#A Initializing the Image Lists for a List View Control
A list view control can have up to three image lists associated with it: one for item icons in icon view, one for item icons in other views, and one for application-defined item states. The following example creates two image lists, adds an icon to each, and assigns them to a list view control by using the LVM_SETIMAGELIST_win32_LVM_SETIMAGELIST message.
// InitListViewImageList - creates image lists for a list view.
// Returns TRUE if successful or FALSE otherwise.
// hwndLV - handle of the list view control
BOOL WINAPI InitListViewImageLists(HWND hwndLV)
{
HICON hiconItem; // icon for list view items
HIMAGELIST himlLarge; // image list for icon view
HIMAGELIST himlSmall; // image list for other views

// Create the full-sized and small icon image lists.
himlLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON), TRUE, 1, 1);
himlSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON), TRUE, 1, 1);

// Add an icon to each image list.
hiconItem = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_ITEM));
ImageList_AddIcon(himlLarge, hiconItem);
ImageList_AddIcon(himlSmall, hiconItem);
DeleteObject(hiconItem);

// Assign the image lists to the list view control.
ListView_SetImageList(hwndLV, himlLarge, LVSIL_NORMAL);
ListView_SetImageList(hwndLV, himlSmall, LVSIL_SMALL);
return TRUE;
}

+$#A Adding Columns to a List View Control
Columns appear only in report view, and they enable multiple pieces of information to be displayed for each list item. You can add columns to a list view control by using the LVM_INSERTCOLUMN_win32_LVM_INSERTCOLUMN message. Each item in a list view control can have, in addition to its item text, any number of strings called subitems. When you add a column to a list view control, you specify which subitem to display in the column.
Unless a list view control has the LVS_NOCOLUMNHEADER window style, each column has a header showing the column name. The user can click the header and can size the columns using the header.
The following example adds several columns to a list view control. The column headings are defined as string resources, which are consecutively numbered starting with IDS_FIRSTCOLUMN (defined in the application's header file). The number of columns is defined in the application's header file as C_COLUMNS.
// InitListViewColumns - adds columns to a list view control.
// Returns TRUE if successful or FALSE otherwise.
// hwndLV - handle of the list view control
BOOL WINAPI InitListViewColumns(HWND hwndLV)
{
extern char g_achTemp[256]; // temporary buffer
LV_COLUMN lvc;
int iCol;

// Initialize the LV_COLUMN structure.
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = 100;
lvc.pszText = g_achTemp;

// Add the columns.
for (iCol = 0; iCol < C_COLUMNS; iCol++) {
lvc.iSubItem = iCol;
LoadString(g_hinst, IDS_FIRSTCOLUMN + iCol,
g_achTemp, sizeof(g_achTemp));
if (ListView_InsertColumn(hwndLV, iCol, &lvc) == -1)
return FALSE;
}
return TRUE;
}

+$#A Adding Items to a List View Control
An application can add items to a list view control by using the LVM_INSERTITEM_win32_LVM_INSERTITEM message. The attributes of a list view item that are specified by an LV_ITEM_win32_LV_ITEM_str structure include a state, a label, an icon, and item data. Associated with each item may be one or more subitems, which are strings that appear to the right of an item in report view.
The example in this section adds a list view item for each line in a text file. Semicolons are assumed to separate the item label and the subitem strings that follow it. The example saves each item's label and subitem strings in a structure, which is defined in the application's header file, as follows.
#define C_COLUMNS 6

typedef struct myitem_tag {
LPSTR aCols[C_COLUMNS];
} MYITEM;

The application fills in an LV_ITEM_win32_LV_ITEM_str structure and adds a list view item by using the LVM_INSERTITEM_win32_LVM_INSERTITEM message. Because the application saves the item label in its own application-defined MYITEM structure, it specifies the LPSTR_TEXTCALLBACK value for the pszText member of the LV_ITEM structure. Specifying this value causes the control to send an LVN_GETDISPINFO_win32_LVN_GETDISPINFO notification message to its owner window whenever it needs to display the item. The address of the application-defined structure is saved as item data.
// InitListViewItems - adds items and subitems to a list view.
// Returns TRUE if successful or FALSE otherwise.
// hwndLV - handle of the list view control
// pfData - text file containing list view items with columns
// separated by semicolons
BOOL WINAPI InitListViewItems(HWND hwndLV, FILE *pfData)
{
extern char g_achTemp[256]; // temporary buffer
PSTR pszStart;
PSTR pszEnd;
int iItem;
int iSubItem;
LV_ITEM lvi;

// Initialize LV_ITEM members that are common to all items.
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvi.state = 0;
lvi.stateMask = 0;
lvi.pszText = LPSTR_TEXTCALLBACK; // app. maintains text
lvi.iImage = 0; // image list index

// Read each line in the specified file.
for (iItem = 0;
fgets(g_achTemp, sizeof(g_achTemp), pfData);
iItem++) {

// Allocate an application-defined structure to store the
// item label and the text of each subitem.
MYITEM *pItem = LocalAlloc(LPTR, sizeof(MYITEM));

// Copy the first string (the label).
pszEnd = strchr(g_achTemp, ';');
*pszEnd = '\0';
pItem->aCols[0] = DupString(g_achTemp);

// Copy subsequent strings (subitems).
for (iSubItem = 1;
iSubItem < C_COLUMNS && pszEnd != NULL;
iSubItem++) {
pszStart = pszEnd + 1;
if ((pszEnd = strchr(pszStart, ';')) != NULL)
*pszEnd = '\0';
pItem->aCols[iSubItem] = DupString(pszStart);
}

// Initialize item-specific LV_ITEM members.
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.lParam = (LPARAM) pItem; // item data

// Add the item.
ListView_InsertItem(hwndLV, &lvi);

// There is no need to set the text of the subitems. They
// default to LPSTR_TEXTCALLBACK.
}
return TRUE;
}

// DupString - allocates a copy of a string.
// lpsz - address of the null-terminated string to copy

LPSTR DupString(LPSTR lpsz)
{
int cb = lstrlen(lpsz) + 1;
LPSTR lpszNew = LocalAlloc(LMEM_FIXED, cb);
if (lpszNew != NULL)
CopyMemory(lpszNew, lpsz, cb);
return lpszNew;
}

+$#A Processing the WM_NOTIFY Message
A list view control notifies its parent window of events by sending a WM_NOTIFY_win32_WM_NOTIFY message. The wParam parameter is the identifier of the list view control, and the lParam parameter is the address of an NMHDR_win32_NMHDR_str structure (or to a larger structure that has an NMHDR structure as its first member). The example in this section processes the LVN_GETDISPINFO_win32_LVN_GETDISPINFO, LVN_ENDLABELEDIT_win32_LVN_ENDLABELEDIT, and LVN_COLUMNCLICK_win32_LVN_COLUMNCLICK notification messages.
A list view control sends the LVN_GETDISPINFO notification message to retrieve information about an item or subitem from the parent window. This notification is sent, for example, when an item with the LPSTR_TEXTCALLBACK value needs to be displayed.
A list view control sends the LVN_ENDLABELEDIT notification message when the user completes or cancels editing of an item's label. This notification is only sent if the list view control has the LVS_EDITLABELS window style. If editing is being canceled, the parent window typically does nothing. If editing is being completed, the parent window should set the item label to the new text, unless the item label is LPSTR_TEXTCALLBACK. In that case, the parent window should simply update the application-defined data it maintains for the list item.
If the user clicks a column header in report view, a list view control sends the LVN_COLUMNCLICK_win32_LVN_COLUMNCLICK notification message. Typically, an application sorts a list view by the specified column when this clicking occurs. To sort, use the LVM_SORTITEMS_win32_LVM_SORTITEMS message, specifying an application-defined comparison function.
The following example shows the portion of the application's window procedure that processes the WM_NOTIFY_win32_WM_NOTIFY message.
case WM_NOTIFY:

// Branch depending on the specific notification message.
switch (((LPNMHDR) lParam)->code) {

// Process LVN_GETDISPINFO to supply information about
// callback items.
case LVN_GETDISPINFO:
Main_OnGetDispInfo((LV_DISPINFO *) lParam);
break;

// Process LVN_ENDLABELEDIT to change item labels after
// in-place editing.
case LVN_ENDLABELEDIT:
return Main_OnEndLabelEdit(
(LV_DISPINFO *) lParam
);

// Process LVN_COLUMNCLICK to sort items by column.
case LVN_COLUMNCLICK:
#define pnm ((NM_LISTVIEW *) lParam)
ListView_SortItems(
pnm->hdr.hwndFrom,
ListViewCompareFunc,
(LPARAM) (pnm->iSubItem)
);
#undef pnm
break;
}
break;

The following example shows the application-defined functions that the window procedure uses to process list view notification messages.
// Main_OnGetDispInfo - processes the LVN_GETDISPINFO
// notification message.
// pnmv - value of lParam (points to an LV_DISPINFO structure)
VOID WINAPI Main_OnGetDispInfo(LV_DISPINFO *pnmv)
{
// Provide the item or subitem's text, if requested.
if (pnmv->item.mask & LVIF_TEXT) {
MYITEM *pItem = (MYITEM *) (pnmv->item.lParam);
lstrcpy(pnmv->item.pszText,
pItem->aCols[pnmv->item.iSubItem]);
}
}

// Main_OnEndLabelEdit - processes the LVN_ENDLABELEDIT
// notification message.
// Returns TRUE if the label is changed or FALSE otherwise.
// pnmv - value of lParam (points to an LV_DISPINFO structure)

BOOL Main_OnEndLabelEdit(LV_DISPINFO *pnmv)
{
MYITEM *pItem;

// The item is -1 if editing is being canceled.
if (pnmv->item.iItem == -1)
return FALSE;

// Copy the new text to the application-defined structure,
// a pointer to which is saved as item data.
pItem = (MYITEM *) (pnmv->item.lParam);
pItem->aCols[0] = (PSTR) LocalReAlloc(
(HLOCAL) (pItem->aCols[0]),
lstrlen(pnmv->item.pszText) + 1,
LMEM_MOVEABLE
);
lstrcpy(pItem->aCols[0], pnmv->item.pszText);

// No need to set the item text, because it is a callback item.
return TRUE;
}

// ListViewCompareFunc - sorts the list view control. It is a
// comparison function.
// Returns a negative value if the first item should precede the
// second item, a positive value if the first item should
// follow the second item, and zero if the items are equivalent.
// lParam1 and lParam2 - item data for the two items (in this
// case, pointers to application-defined MYITEM structures)
// lParamSort - value specified by the LVM_SORTITEMS message
// (in this case, the index of the column to sort)
int CALLBACK ListViewCompareFunc(
LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort)
{
MYITEM *pItem1 = (MYITEM *) lParam1;
MYITEM *pItem2 = (MYITEM *) lParam2;

// Compare the specified column.
int iCmp = lstrcmpi(pItem1->aCols[lParamSort],
pItem2->aCols[lParamSort]);

// Return the result if nonzero, or compare the
// first column otherwise.
return (iCmp != 0) ? iCmp :
lstrcmpi(pItem1->aCols[0], pItem2->aCols[0]);
}

+$#A Changing List View Styles
An application can change the window style of a list view control after it is created by using the GetWindowLong_win32_GetWindowLong and SetWindowLong_win32_SetWindowLong functions. The following example changes the style bits that govern the view mode.
// SetView - sets a list view's window style to change the view.
// hwndLV - handle of the list view control
// dwView - value specifying a view style

VOID WINAPI SetView(HWND hwndLV, DWORD dwView)
{
// Get the current window style.
DWORD dwStyle = GetWindowLong(hwndLV, GWL_STYLE);

// Only set the window style if the view bits have changed.
if ((dwStyle & LVS_TYPEMASK) != dwView)
SetWindowLong(hwndLV, GWL_STYLE,
(dwStyle & ~LVS_TYPEMASK) | dwView);
}

+$#A List View Control Reference
The following messages, notification messages, and structures are associated with list view controls. These elements can be grouped, as follows.
Items and Subitems
LV_ITEM_win32_LV_ITEM_strLVM_DELETEALLITEMS_win32_LVM_DELETEALLITEMSLVM_DELETEITEM_win32_LVM_DELETEITEMLVM_GETITEM_win32_LVM_GETITEMLVM_GETITEMCOUNT_win32_LVM_GETITEMCOUNTLVM_GETITEMSPACING_win32_LVM_GETITEMSPACINGLVM_GETITEMSTATE_win32_LVM_GETITEMSTATELVM_GETITEMTEXT_win32_LVM_GETITEMTEXTLVM_GETSELECTEDCOUNT_win32_LVM_GETSELECTEDCOUNTLVM_INSERTITEM_win32_LVM_INSERTITEMLVM_SETITEM_win32_LVM_SETITEMLVM_SETITEMCOUNT_win32_LVM_SETITEMCOUNTLVM_SETITEMSTATE_win32_LVM_SETITEMSTATELVM_SETITEMTEXT_win32_LVM_SETITEMTEXT
Callback Items
LV_DISPINFO_win32_LV_DISPINFO_strLVM_GETCALLBACKMASK_win32_LVM_GETCALLBACKMASKLVM_REDRAWITEMS_win32_LVM_REDRAWITEMSLVM_SETCALLBACKMASK_win32_LVM_SETCALLBACKMASKLVM_UPDATE_win32_LVM_UPDATELVN_GETDISPINFO_win32_LVN_GETDISPINFOLVN_SETDISPINFO_win32_LVN_SETDISPINFO
Columns
LV_COLUMN_win32_LV_COLUMN_strLVM_DELETECOLUMN_win32_LVM_DELETECOLUMNLVM_GETCOLUMN_win32_LVM_GETCOLUMNLVM_GETCOLUMNWIDTH_win32_LVM_GETCOLUMNWIDTHLVM_GETSTRINGWIDTH_win32_LVM_GETSTRINGWIDTHLVM_INSERTCOLUMN_win32_LVM_INSERTCOLUMNLVM_SETCOLUMN_win32_LVM_SETCOLUMNLVM_SETCOLUMNWIDTH_win32_LVM_SETCOLUMNWIDTHLVN_COLUMNCLICK_win32_LVN_COLUMNCLICK
Arranging, Sorting, and Finding
LV_FINDINFO_win32_LV_FINDINFO_strLVM_ARRANGE_win32_LVM_ARRANGELVM_FINDITEM_win32_LVM_FINDITEMLVM_GETNEXTITEM_win32_LVM_GETNEXTITEMLVM_SORTITEMS_win32_LVM_SORTITEMS
Item Positions and Scrolling
LV_HITTESTINFO_win32_LV_HITTESTINFO_strLVM_ENSUREVISIBLE_win32_LVM_ENSUREVISIBLELVM_GETCOUNTPERPAGE_win32_LVM_GETCOUNTPERPAGELVM_GETITEMPOSITION_win32_LVM_GETITEMPOSITIONLVM_GETITEMRECT_win32_LVM_GETITEMRECTLVM_GETORIGIN_win32_LVM_GETORIGINLVM_GETTOPINDEX_win32_LVM_GETTOPINDEXLVM_GETVIEWRECT_win32_LVM_GETVIEWRECTLVM_HITTEST_win32_LVM_HITTESTLVM_SCROLL_win32_LVM_SCROLLLVM_SETITEMPOSITION_win32_LVM_SETITEMPOSITIONLVM_SETITEMPOSITION32_win32_LVM_SETITEMPOSITION32
Colors
LVM_GETBKCOLOR_win32_LVM_GETBKCOLORLVM_GETTEXTBKCOLOR_win32_LVM_GETTEXTBKCOLORLVM_GETTEXTCOLOR_win32_LVM_GETTEXTCOLORLVM_SETBKCOLOR_win32_LVM_SETBKCOLORLVM_SETTEXTBKCOLOR_win32_LVM_SETTEXTBKCOLORLVM_SETTEXTCOLOR_win32_LVM_SETTEXTCOLOR
Miscellaneous
LV_KEYDOWN_win32_LV_KEYDOWN_strLVM_CREATEDRAGIMAGE_win32_LVM_CREATEDRAGIMAGELVM_EDITLABEL_win32_LVM_EDITLABELLVM_GETEDITCONTROL_win32_LVM_GETEDITCONTROLLVM_GETIMAGELIST_win32_LVM_GETIMAGELISTLVM_SETIMAGELIST_win32_LVM_SETIMAGELISTLVN_BEGINDRAG_win32_LVN_BEGINDRAGLVN_BEGINLABELEDIT_win32_LVN_BEGINLABELEDITLVN_BEGINRDRAG_win32_LVN_BEGINRDRAGLVN_DELETEALLITEMS_win32_LVN_DELETEALLITEMSLVN_DELETEITEM_win32_LVN_DELETEITEMLVN_ENDLABELEDIT_win32_LVN_ENDLABELEDITLVN_INSERTITEM_win32_LVN_INSERTITEMLVN_ITEMCHANGED_win32_LVN_ITEMCHANGEDLVN_ITEMCHANGING_win32_LVN_ITEMCHANGINGLVN_KEYDOWN_win32_LVN_KEYDOWNLVN_PEN_win32_LVN_PENNM_LISTVIEW_win32_NM_LISTVIEW_str
+$#A List View Control Messages
An application sends messages to a list view control to alter its appearance, add or change items and columns, and so on. Each message has a corresponding macro, which you can use instead of sending the message explicitly.
LVM_ARRANGE_win32_LVM_ARRANGELVM_CREATEDRAGIMAGE_win32_LVM_CREATEDRAGIMAGELVM_DELETEALLITEMS_win32_LVM_DELETEALLITEMSLVM_DELETECOLUMN_win32_LVM_DELETECOLUMNLVM_DELETEITEM_win32_LVM_DELETEITEMLVM_EDITLABEL_win32_LVM_EDITLABELLVM_ENSUREVISIBLE_win32_LVM_ENSUREVISIBLELVM_FINDITEM_win32_LVM_FINDITEMLVM_GETBKCOLOR_win32_LVM_GETBKCOLORLVM_GETCALLBACKMASK_win32_LVM_GETCALLBACKMASKLVM_GETCOLUMN_win32_LVM_GETCOLUMNLVM_GETCOLUMNWIDTH_win32_LVM_GETCOLUMNWIDTHLVM_GETCOUNTPERPAGE_win32_LVM_GETCOUNTPERPAGELVM_GETEDITCONTROL_win32_LVM_GETEDITCONTROLLVM_GETIMAGELIST_win32_LVM_GETIMAGELISTLVM_GETISEARCHSTRING_win32_LVM_GETISEARCHSTRINGLVM_GETITEM_win32_LVM_GETITEMLVM_GETITEMCOUNT_win32_LVM_GETITEMCOUNTLVM_GETITEMPOSITION_win32_LVM_GETITEMPOSITIONLVM_GETITEMRECT_win32_LVM_GETITEMRECTLVM_GETITEMSPACING_win32_LVM_GETITEMSPACINGLVM_GETITEMSTATE_win32_LVM_GETITEMSTATELVM_GETITEMTEXT_win32_LVM_GETITEMTEXTLVM_GETNEXTITEM_win32_LVM_GETNEXTITEMLVM_GETORIGIN_win32_LVM_GETORIGINLVM_GETSELECTEDCOUNT_win32_LVM_GETSELECTEDCOUNTLVM_GETSTRINGWIDTH_win32_LVM_GETSTRINGWIDTHLVM_GETTEXTBKCOLOR_win32_LVM_GETTEXTBKCOLORLVM_GETTEXTCOLOR_win32_LVM_GETTEXTCOLORLVM_GETTOPINDEX_win32_LVM_GETTOPINDEXLVM_GETVIEWRECT_win32_LVM_GETVIEWRECTLVM_HITTEST_win32_LVM_HITTESTLVM_INSERTCOLUMN_win32_LVM_INSERTCOLUMNLVM_INSERTITEM_win32_LVM_INSERTITEMLVM_REDRAWITEMS_win32_LVM_REDRAWITEMSLVM_SCROLL_win32_LVM_SCROLLLVM_SETBKCOLOR_win32_LVM_SETBKCOLORLVM_SETCALLBACKMASK_win32_LVM_SETCALLBACKMASKLVM_SETCOLUMN_win32_LVM_SETCOLUMNLVM_SETCOLUMNWIDTH_win32_LVM_SETCOLUMNWIDTHLVM_SETIMAGELIST_win32_LVM_SETIMAGELISTLVM_SETITEM_win32_LVM_SETITEMLVM_SETITEMCOUNT_win32_LVM_SETITEMCOUNTLVM_SETITEMPOSITION_win32_LVM_SETITEMPOSITIONLVM_SETITEMPOSITION32_win32_LVM_SETITEMPOSITION32LVM_SETITEMSTATE_win32_LVM_SETITEMSTATELVM_SETITEMTEXT_win32_LVM_SETITEMTEXTLVM_SETTEXTBKCOLOR_win32_LVM_SETTEXTBKCOLORLVM_SETTEXTCOLOR_win32_LVM_SETTEXTCOLORLVM_SORTITEMS_win32_LVM_SORTITEMSLVM_UPDATE_win32_LVM_UPDATE
+$#A List View Control Notification Messages
A list view control sends notification messages to its owner window when events occur in the control.
LVN_BEGINDRAG_win32_LVN_BEGINDRAGLVN_BEGINLABELEDIT_win32_LVN_BEGINLABELEDITLVN_BEGINRDRAG_win32_LVN_BEGINRDRAGLVN_COLUMNCLICK_win32_LVN_COLUMNCLICKLVN_DELETEALLITEMS_win32_LVN_DELETEALLITEMSLVN_DELETEITEM_win32_LVN_DELETEITEMLVN_ENDLABELEDIT_win32_LVN_ENDLABELEDITLVN_GETDISPINFO_win32_LVN_GETDISPINFOLVN_INSERTITEM_win32_LVN_INSERTITEMLVN_ITEMCHANGED_win32_LVN_ITEMCHANGEDLVN_ITEMCHANGING_win32_LVN_ITEMCHANGINGLVN_KEYDOWN_win32_LVN_KEYDOWNLVN_PEN_win32_LVN_PENLVN_SETDISPINFO_win32_LVN_SETDISPINFO
+$#A List View Control Structures
The following structures are used with list view controls.
LV_COLUMN_win32_LV_COLUMN_strLV_DISPINFO_win32_LV_DISPINFO_strLV_FINDINFO_win32_LV_FINDINFO_strLV_HITTESTINFO_win32_LV_HITTESTINFO_strLV_ITEM_win32_LV_ITEM_strLV_KEYDOWN_win32_LV_KEYDOWN_strNM_LISTVIEW_win32_NM_LISTVIEW_str
+$#A List View Control Constants
The following sections list window style flags and item state flags are used with list view controls.
+$#A List View Window Styles
The following window styles are specific to list view controls.
LVS_ALIGNLEFT Specifies that items are left-aligned in icon and small icon view.
LVS_ALIGNTOP Specifies that items are aligned with the top of the list view control in icon and small icon view.
LVS_AUTOARRANGE Specifies that icons are automatically kept arranged in icon and small icon view.
LVS_BUTTON Specifies that item icons look like buttons in icon view.
LVS_EDITLABELS Allows item text to be edited in place. The parent window must process the LVN_ENDLABELEDIT_win32_LVN_ENDLABELEDIT notification message.
LVS_ICON Specifies icon view.
LVS_LIST Specifies list view.
LVS_NOCOLUMNHEADER Specifies that a column header is not displayed in report view. By default, columns have headers in report view.
LVS_NOLABELWRAP Displays item text on a single line in icon view. By default, item text may wrap in icon view.
LVS_NOSCROLL Disables scrolling. All items must be within the client area.
LVS_NOSORTHEADER Specifies that column headers do not work like buttons. This style is useful if clicking a column header in report view does not carry out an action, such as sorting.
LVS_OWNERDRAWFIXED Enables the owner window to paint items in report view. The list view control sends a WM_DRAWITEM_win32_WM_DRAWITEM message to paint each item; it does not send separate messages for each subitem. The itemData member of the DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure contains the item data for the specified list view item.
LVS_REPORT Specifies report view. When using the LVS_REPORT style with a List View control, the first column is always left-aligned. You can not use LVCFMT_RIGHT to change this alignment.
LVS_SHAREIMAGELISTS Specifies that the control does not take ownership of the image lists assigned to it; that is, it does not destroy the image lists when it is destroyed. This style enables the same image lists to be used with multiple list view controls.
LVS_SHOWSELALWAYS Always show the selection,if any, even if the control does not have the focus.
LVS_SINGLESEL Allows only one item at a time to be selected. By default, multiple items may be selected.
LVS_SMALLICON Specifies small icon view.
LVS_SORTASCENDING Sorts items based on item text in ascending order.
LVS_SORTDESCENDING Sorts items based on item text in descending order.

You can use the LVS_TYPESTYLEMASK value to isolate the window styles that correspond to the current view: LVS_ICON, LVS_SMALLICON, LVS_LIST, and LVS_REPORT.
You can use the LVS_ALIGNMASK value to isolate the window styles that specify the alignment of items: LVS_ALIGNLEFT and LVS_ALIGNTOP.
+$#A List View Item States
An item's state determines its appearance and functionality. The state can be zero, or one or more of the following values.
LVIS_CUT The item is marked for a cut and paste operation.
LVIS_DROPHILITED The item is highlighted as a drag-and-drop target.
LVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle. Although more than one item may be selected, only one item can have the focus.
LVIS_SELECTED The item is selected. The appearance of a selected item depends on whether it has the focus and on the system colors used for selection.

You can use the LVIS_OVERLAYMASK value to isolate the state bits that contain the one-based index of the overlay image. You can use the LVIS_STATEIMAGEMASK value to isolate the state bits that contain the one-based index of the state image.

+$#AK Progress Bars
A progress bar is a window that an application can use to indicate the progress of a lengthy operation. It consists of a rectangle that is gradually filled, from left to right, with the system highlight color as an operation progresses. The following illustration shows a progress bar positioned along the bottom of a window.
{ewc msdncd, EWGraphic, group10601 0 /a "SDK_01.bmp"}
+$#A About Progress Bars
You create a progress bar by using the CreateWindowEx_win32_CreateWindowEx function, specifying the PROGRESS_CLASS window class. This window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls_win32_InitCommonControls function first.
+$#A Range and Current Position
A progress bar has a range and a current position. The range represents the entire duration of the operation, and the current position represents the progress that the application has made toward completing the operation. The window procedure uses the range and the current position to determine the percentage of the progress bar to fill with the highlight color and to determine the text, if any, to display within the progress bar. Because the range and current position values are expressed as unsigned integers, the highest possible range or current position value is 65,535.
The minimum value in the range can be from 0 to 65,535. Likewise, the maximum value can be from 0 to 65,535. If you do not set the range values, the system sets the minimum value to 0 and the maximum value to 100. You can adjust the range to convenient integers by using the PBM_SETRANGE_win32_PBM_SETRANGE message.
A progress bar provides several messages that you can use to set the current position. The PBM_SETPOS_win32_PBM_SETPOS message sets the position to a given value. The PBM_DELTAPOS_win32_PBM_DELTAPOS message advances the position by adding a specified value to the current position.
The PBM_SETSTEP_win32_PBM_SETSTEP message allows you to specify a step increment for a progress bar. Subsequently, whenever you send the PBM_STEPIT_win32_PBM_STEPIT message to the progress bar, the current position advances by the specified increment. By default, the step increment is set to 10.
+$#A Default Progress Bar Message Processing
This section describes the window messages handled by the window procedure for the PROGRESS_CLASS window class.
Message Processing performed
WM_CREATE_win32_WM_CREATE Allocates and initializes an initial structure.
WM_DESTROY_win32_WM_DESTROY Frees all resources associated with the progress bar.
WM_ERASEBKGND_win32_WM_ERASEBKGND Draws the background and borders of the progress bar.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the font that the progress bar is currently using to draw text.
WM_PAINT_win32_WM_PAINT Draws the progress bar.
WM_SETFONT_win32_WM_SETFONT Saves the handle of the new font and returns the handle of the previous font.

+$#A Using Progress Bars
The following example shows how to use a progress bar to indicate the progress of a lengthy file-parsing operation. The example creates a progress bar and positions it along the bottom of the parent window's client area. The height of the progress bar is based on the height of the arrow bitmap used in a scroll bar. The range is based on the size of the file divided by 2048, which is the size of each "chunk" of data read from the file. The example also sets an increment and advances the current position of the progress bar by the increment after parsing each chunk.
// ParseALargeFile - parses a large file and uses a progress bar to
// indicate the progress of the parsing operation.
// Returns TRUE if successful or FALSE otherwise.
// hwndParent - parent window of the progress bar
// lpszFileName - name of the file to parse
//
// Global variable
// g_hinst - instance handle
extern HINSTANCE g_hinst;

BOOL ParseALargeFile(HWND hwndParent, LPSTR lpszFileName)
{
RECT rcClient; // client area of parent window
int cyVScroll; // height of scroll bar arrow
HWND hwndPB; // handle of progress bar
HANDLE hFile; // handle of file
DWORD cb; // size of file and count of bytes read
LPCH pch; // address of data read from file
LPCH pchTmp; // temporary pointer

// Ensure that the common control DLL is loaded and create a
// progress bar along the bottom of the client area of the
// parent window. Base the height of the progress bar on
// the height of a scroll bar arrow.
InitCommonControls();
GetClientRect(hwndParent, &rcClient);
cyVScroll = GetSystemMetrics(SM_CYVSCROLL);
hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPSTR) NULL,
WS_CHILD | WS_VISIBLE, rcClient.left,
rcClient.bottom - cyVScroll,
rcClient.right, cyVScroll,
hwndParent, (HMENU) 0, g_hinst, NULL);

// Open the file for reading, and retrieve the size of the file.
hFile = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

if (hFile == (HANDLE) INVALID_HANDLE_VALUE)
return FALSE;

cb = GetFileSize(hFile, (LPDWORD) NULL);

// Set the range and increment of the progress bar.
SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, cb / 2048));
SendMessage(hwndPB, PBM_SETSTEP, (WPARAM) 1, 0);

// Parse the file.
pch = (LPCH) LocalAlloc(LPTR, sizeof(char) * 2048);
pchTmp = pch;
do {
ReadFile(hFile, pchTmp, sizeof(char) * 2048, &cb,
(LPOVERLAPPED) NULL);
.
. // Include here any code that parses the file.
.

// Advance the current position of the progress bar
// by the increment.
SendMessage(hwndPB, PBM_STEPIT, 0, 0);
} while (cb);
CloseHandle((HANDLE) hFile);

DestroyWindow(hwndPB);
return TRUE;
}

+$#A Progress Bar Messages
The following messages are associated with progress bars.
PBM_DELTAPOS_win32_PBM_DELTAPOSPBM_SETPOS_win32_PBM_SETPOSPBM_SETRANGE_win32_PBM_SETRANGEPBM_SETSTEP_win32_PBM_SETSTEPPBM_STEPIT_win32_PBM_STEPIT

+$#AK Property Sheets
A property sheet is a window that allows the user to view and edit the properties of an item. For example, a spreadsheet application can use a property sheet to allow the user to set the font and border properties of a cell or to view and set the properties of a device, such as a disk drive, printer, or mouse.
This overview assumes that you have a thorough understanding of dialog box templates and dialog box procedures. If you do not, you should read Dialog Boxes_win32_Dialog_Boxes before continuing with this overview.
+$#A About Property Sheets
A property sheet contains one or more overlapping child windows called pages, each containing control windows for setting a group of related properties. For example, a page can contain the controls for setting the font properties of an item, including the type style, point size, color, and so on. Each page has a tab that the user can select to bring the page to the foreground of the property sheet. The following illustration shows a property sheet for viewing and setting the properties of a floppy disk drive.
{ewc msdncd, EWGraphic, group10602 0 /a "SDK_01.bmp"}
+$#A Property Sheet Dialog Boxes
A property sheet and the pages it contains are actually dialog boxes. The property sheet is a system-defined dialog box that manages the pages and provides a common container for them. The property sheet dialog box can be modal or modeless. It includes a frame, a title bar, and four buttons: OK, Cancel, Apply Now, and Help. (The Help button may be hidden as in the preceding illustration.) The dialog box procedures for the pages receive notification messages when the user selects the buttons.
Each page in a property sheet is an application-defined modeless dialog box that manages the control windows used to view and edit the properties of an item. You provide the dialog box template used to create each page as well as the dialog box procedure that manages the controls and sets the properties of the corresponding item.
A property sheet sends notification messages to the dialog box procedure for a page when the page is gaining or losing the activation and when the user chooses the OK, Cancel, Apply Now, or Help button. The notifications are sent in the form of WM_NOTIFY_win32_WM_NOTIFY messages. The lParam parameter points to an NMHDR_win32_NMHDR_str structure, which includes the window handle of the property sheet dialog box.
Some notification messages require a page to return either TRUE or FALSE in response to the WM_NOTIFY message. A page cannot simply return TRUE or FALSE; instead, it must use the SetWindowLong_win32_SetWindowLong function to set the DWL_MSGRESULT value for the page dialog box to either TRUE or FALSE.
+$#A Pages
A property sheet must contain at least one page, but cannot contain more than the value of MAXPROPPAGES as defined in the Win32 header files. Each page has a zero-based index that the property sheet assigns according to the order in which the page is added to the property sheet. The indexes are used in messages that you send to the property sheet.
A property page can contain a nested dialog box. If it does, you must include the WS_EX_CONTROLPARENT style for the top-level dialog box and call the IsDialogMessage_win32_IsDialogMessage function with the handle of the parent dialog box. This ensures that the user can use mnemonics and the dialog box navigation keys to move the focus to controls in the nested dialog box.
Each page has a corresponding icon and label. The property sheet creates a tab for each page and displays the icon and label in the tab. All property sheet pages are expected to use a nonbold font. To ensure that the font is not bold, specify the DS_3DLOOK style in the dialog box template.
The dialog box procedure for a page must not call the EndDialog_win32_EndDialog function. Doing so will destroy the entire property sheet, not just the page.
+$#A Property Sheet Creation
Before creating a property sheet, you must define one or more pages. This involves filling a PROPSHEETPAGE_win32_PROPSHEETPAGE_str structure with information about the page ¾ its icon, label, dialog box template, dialog box procedure, and so on ¾ and then specifying the address of the structure in a call to the CreatePropertySheetPage_win32_CreatePropertySheetPage function. The function returns a handle of the HPROPSHEETPAGE type that uniquely identifies the page.
To create a property sheet, you specify the address of a PROPSHEETHEADER_win32_PROPSHEETHEADER_str structure in a call to the PropertySheet_win32_PropertySheet function. The structure defines the icon and title for the property sheet and also includes a pointer to an array of HPROPSHEETPAGE handles. When PropertySheet creates the property sheet, it includes the pages identified in the array. The order of the array determines the order of the pages in the property sheet.
Another way to create a property sheet is to specify an array of PROPSHEETPAGE_win32_PROPSHEETPAGE_str structures instead of an array of HPROPSHEETPAGE handles. In this case, PropertySheet creates handles for the pages before adding them to the property sheet.
When a page is created, the dialog box procedure for the page receives a WM_INITDIALOG_win32_WM_INITDIALOG message. The message's lParam parameter points to the PROPSHEETPAGE structure used to create the page. The dialog box can save the information in the structure and use it later to modify the page.
PropertySheet_win32_PropertySheet automatically sets the size and initial position of a property sheet. The position is based on the position of the owner window, and the size is based on the largest page specified in the array of pages when the property sheet was created. If you want the pages to match the width of the four buttons at the bottom of the property sheet, set the width of the widest page to 190 dialog units.
+$#A Adding and Removing Pages
After creating a property sheet, an application can add a page by using the PSM_ADDPAGE_win32_PSM_ADDPAGE message. Note that the size of the property sheet cannot change after it has been created, so the new page must be no larger than the largest page currently in the property sheet.
An application removes a page by using the PSM_REMOVEPAGE_win32_PSM_REMOVEPAGE message. When you define a page, you can specify the address of a PropSheetPageProc_win32_PropSheetPageProc callback function that the property sheet calls when it is creating or removing the page. Using PropSheetPageProc gives you an opportunity to perform initialization and cleanup operations for individual pages.
When a property sheet is destroyed, it automatically destroys all of the pages that have been added to it. The pages are destroyed in reverse order from that specified in the array used to create the pages. To destroy a page that was created by the CreatePropertySheetPage_win32_CreatePropertySheetPage function but was not added to the property sheet, use the DestroyPropertySheetPage_win32_DestroyPropertySheetPage function.
+$#A Property Sheet Title and Page Labels
You specify the title of a property sheet in the PROPSHEETHEADER_win32_PROPSHEETHEADER_str structure used to create the property sheet. If the dwFlags member includes the PSH_PROPTITLE value, the property sheet adds the "Properties for" prefix to the specified title string. An application can change the title after a property sheet is created by using the PSM_SETTITLE_win32_PSM_SETTITLE message.
By default, a property sheet uses the name string specified in the dialog box template as the label for a page. You can override the name string by including the PSP_USETITLE value in the dwFlags member of the PROPSHEETPAGE_win32_PROPSHEETPAGE_str structure that defines the page. When PSP_USETITLE is specified, the pszTitle member must contain the address of the label string for the page.
+$#A Page Activation
A property sheet can have only one active page at a time. The page that has the activation is at the foreground of the overlapping stack of pages. The user activates a page by selecting its tab; an application activates a page by using the PSM_SETCURSEL_win32_PSM_SETCURSEL message.
The property sheet sends the PSN_KILLACTIVE_win32_PSN_KILLACTIVE notification message to the page that is about to lose the activation. In response, the page should validate any changes that the user has made to the page. If the page requires additional user input before losing the activation, it should use the SetWindowLong_win32_SetWindowLong function to set the DWL_MSGRESULT value of the page to TRUE. Also, the page should display a message box that describes the problem and provides the recommended action. The page should set DWL_MSGRESULT to FALSE when it is okay to lose the activation.
Before the page that is gaining the activation is visible, the property sheet sends the PSN_SETACTIVE_win32_PSN_SETACTIVE notification message to the page. The page should respond by initializing its control windows.
+$#A Help Button
When a page is activated, the property sheet determines whether to enable or disable the Help button for the page by checking for the PSP_HASHELP style. If the page has this style, it supports the Help button. If the PSP_HASHELP style is not present, the button is disabled.
When the user chooses the Help button, the active page receives the PSN_HELP_win32_PSN_HELP notification message. The page should respond by displaying help information, typically by calling the WinHelp_win32_WinHelp function.
+$#A OK, Cancel, and Apply Now Buttons
The OK and Apply Now buttons are similar; both direct a property sheet's pages to validate and apply the property changes that the user has made. The only difference is that choosing the OK button causes the property sheet to be destroyed after the changes are applied, but choosing the Apply Now button does not.
When the user chooses the OK or Apply Now button, the property sheet sends the PSN_KILLACTIVE_win32_PSN_KILLACTIVE notification message to the active page, giving it an opportunity to validate the user's changes. If the page determines that the changes are valid, it should call the SetWindowLong_win32_SetWindowLong function to set the DWL_MSGRESULT value for the page to FALSE. In this case, the property sheet sends the PSN_APPLY_win32_PSN_APPLY notification message to each page, directing them to apply the new properties to the corresponding item. If the page determines that the user's changes are not valid, it should set DWL_MSGRESULT to TRUE and display a dialog box informing the user of the problem. The page remains active until it sets DWL_MSGRESULT to FALSE in response to a PSN_KILLACTIVE message. An application can use the PSM_APPLY_win32_PSM_APPLY message to simulate the choice of the Apply Now button.
The Apply Now button is initially disabled when a page becomes active, indicating that there are not yet any property changes to apply. When the page receives user input through one of its controls indicating that the user has edited a property, the page should send the PSM_CHANGED_win32_PSM_CHANGED message to the property sheet. The message causes the property sheet to enable the Apply Now button. If the user subsequently chooses the Apply Now or Cancel button, the page should reinitialize its controls and then send the PSM_UNCHANGED_win32_PSM_UNCHANGED message to disable again the Apply Now button.
Sometimes the Apply Now button causes a page to make a change to a property sheet, and the change cannot be undone. When this happens, the page should send the PSM_CANCELTOCLOSE_win32_PSM_CANCELTOCLOSE message to the property sheet. The message causes the property sheet to change the text of the OK button to "Close," indicating that the applied changes cannot be canceled.
Sometimes a page makes a change to the system configuration that requires Windows to be to restarted or the system rebooted before the change can take effect. After making such a change, a page should send either the PSM_RESTARTWINDOWS_win32_PSM_RESTARTWINDOWS or PSM_REBOOTSYSTEM_win32_PSM_REBOOTSYSTEM message to the property sheet. These messages cause the PropertySheet_win32_PropertySheet function to return the ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM value after the property sheet is destroyed.
The property sheet sends the the PSN_RESET_win32_PSN_RESET notification message to all pages when the user chooses the Cancel button, indicating that the property sheet is about to be destroyed. A page should use the notification to perform cleanup operations.
+$#A Wizard Property Sheets
You can create a special type of property sheet called a wizard, which consists of a sequence of dialog boxes that guide the user through the steps of an operation, such as setting up a device or creating a birthday card. In a wizard property sheet, the pages do not have tabs, and only one page is visible at a time. Also, instead of having Ok and Apply Now buttons, a wizard property sheet has a Back button, a Next or Finish button, and a Cancel button. To tell the property sheet which buttons to enable, use the PSM_SETWIZBUTTONS_win32_PSM_SETWIZBUTTONS message with the PSWIZB_BACK, PSWIZB_NEXT, and PSWIZB_FINISH values. You create and initialize a wizard property sheet just as you would a standard property sheet, except that you must include the PSH_WIZARD value in the dwFlags member of the PROPSHEETHEADER_win32_PROPSHEETHEADER_str structure. The system ignores the pszCaption member; instead, it puts the label of the current page in the title bar of the property sheet. When the user switches from one page to the next, the system updates the title using the label of the current page.
You should use the following values to set the sizes of the elements in your wizard property sheet. Using these values ensures that your pages conform to the Window standards.
WIZ_BODYCX Width of the body of a page in a wizard property sheet. The body does not include the bitmap area.
WIZ_BODYX Horizontal coordinate of the upper left corner of the body of a page in a wizard property sheet. Use zero for the vertical coordinate of the body of a page.
WIZ_CXBMP Width of the bitmap area in a page of a wizard property sheet. Use WIZ_CYDLG for the height of the bitmap area.
WIZ_CXDLG Width of a page in a wizard property sheet.
WIZ_CYDLG Height of a page in a wizard property sheet.

The dialog box procedure for a page in a wizard property sheet receives all of the same notification messages as a standard property sheet page. In addition, a wizard property sheet page receives three notification messages that a standard property sheet page does not receive: PSN_WIZBACK_win32_PSN_WIZBACK, PSN_WIZNEXT_win32_PSN_WIZNEXT, and PSN_WIZFINISH_win32_PSN_WIZFINISH. A wizard page receives these notifications when the user chooses the Back, Next, or Finish button.
When the user chooses the Back or Next button, the property sheet automatically advances to the previous or next page. An application can prevent the property sheet from advancing by setting the DWL_MSGRESULT value to -1 in response to the PSN_WIZBACK or PSN_WIZNEXT notification. To jump to a page other than the previous or next one, an application should set DWL_MSGRESULT to the identifier of the dialog box to be displayed.
The system automatically destroys the wizard property sheet when the user clicks the Finish button. An application can prevent the wizard from being destroyed by setting DWL_MSGRESULT to a nonzero value in response to the PSN_WIZFINISH_win32_PSN_WIZFINISH notification message.
+$#A Property Sheet Extensions
A property sheet extension is a dynamic-link library (DLL) that adds one or more pages to a property sheet created by another module. The module that creates the property sheet includes an AddPropSheetPageProc_win32_AddPropSheetPageProc callback function that the extension DLL calls to add a page. The function receives the handle of a page and an application-defined 32-bit value.
The extension DLL also contains a callback function called ExtensionPropSheetPageProc_win32_ExtensionPropSheetPageProc, which receives the address of AddPropSheetPageProc from the module that creates the property sheet. The extension DLL must export ExtensionPropSheetPageProc.
The Windows header files include two prototypes for defining property sheet callback functions. To define AddPropSheetPageProc_win32_AddPropSheetPageProc, use the following prototype.
typedef BOOL (CALLBACK FAR * LPFNADDPROPSHEETPAGE)(HPROPSHEETPAGE, LPARAM);
To define ExtensionPropSheetPageProc_win32_ExtensionPropSheetPageProc, use the following prototype.
typedef BOOL (CALLBACK FAR * LPFNADDPROPSHEETPAGES)(LPVOID, LPFNADDPROPSHEETPAGE, LPARAM);
+$#A Using Property Sheets
· Creating a property sheet_win32_Creating_a_Property_Sheet
· Processing notification messages_win32_Processing_Notification_Messages

+$#A Creating a Property Sheet
The example in this section creates a property sheet that contains two pages ¾ one for setting the font properties of a cell in a spreadsheet and another for setting the border properties of the cell. The example defines the pages by filling a pair of PROPSHEETPAGE_win32_PROPSHEETPAGE_str structures and specifying the address in the PROPSHEETHEADER_win32_PROPSHEETHEADER_str structure that is passed to the PropertySheet_win32_PropertySheet function. The dialog box templates, icons, and labels for the pages are loaded from the resources contained in the application's executable file. The icon for the property sheet is also loaded from the application's resources.
// DoPropertySheet - creates a property sheet that contains two pages.// hwndOwner - handle of the owner window of the property sheet//// Global variables// g_hinst - instance handleextern HINSTANCE g_hinst;VOID DoPropertySheet(HWND hwndOwner){ PROPSHEETPAGE psp[2]; PROPSHEETHEADER psh; psp[0].dwSize = sizeof(PROPSHEETPAGE); psp[0].dwFlags = PSP_USEICONID | PSP_USETITLE; psp[0].hInstance = g_hinst; psp[0].pszTemplate = MAKEINTRESOURCE(DLG_FONT); psp[0].pszIcon = MAKEINTRESOURCE(IDI_FONT); psp[0].pfnDlgProc = FontDialogProc; psp[0].pszTitle = MAKEINTRESOURCE(IDS_FONT) psp[0].lParam = 0; psp[0].pfnCallback = NULL; psp[1].dwSize = sizeof(PROPSHEETPAGE); psp[1].dwFlags = PSP_USEICONID | PSP_USETITLE; psp[1].hInstance = g_hinst; psp[1].pszTemplate = MAKEINTRESOURCE(DLG_BORDER); psp[1].pszIcon = MAKEINTRESOURCE(IDI_BORDER); psp[1].pfnDlgProc = BorderDialogProc; psp[1].pszTitle = MAKEINTRESOURCE(IDS_BORDER); psp[1].lParam = 0; psp[1].pfnCallback = NULL; psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_USEICONID | PSH_PROPSHEETPAGE; psh.hwndParent = hwndOwner; psh.hInstance = g_hinst; psh.pszIcon = MAKEINTRESOURCE(IDI_CELL_PROPERTIES); psh.pszCaption = (LPSTR) "Cell Properties"; psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); psh.nStartPage = 0; psh.ppsp = (LPCPROPSHEETPAGE) &psp; psh.pfnCallback = NULL; PropertySheet(&psh); return;}
+$#A Processing Notification Messages
A property sheet sends WM_NOTIFY_win32_WM_NOTIFY messages to retrieve information from the pages and to notify the pages of user actions. The lParam parameter of the message is the address of an NMHDR_win32_NMHDR_str structure, which contains the handle of the property sheet dialog box, the handle of the page dialog box, and a notification code. The page must respond to some notification messages by setting the DWL_MSGRESULT value of the page to either TRUE or FALSE.
The following example is a code fragment from the dialog box procedure for a page. It shows how to process the PSN_HELP_win32_PSN_HELP notification message.
case WM_NOTIFY: switch (((NMHDR FAR *) lParam)->code) { case PSN_HELP: { char szBuf[FILE_LEN]; // buffer for name of help file // Display help for the font properties page. LoadString(g_hinst, IDS_HELPFILE, &szBuf, FILE_LEN) WinHelp(((NMHDR FAR *) lParam)->hwndFrom, &szBuf, HELP_CONTEXT, IDH_FONT_PROPERTIES); break; } . . // Process other property sheet notifications here. . }
+$#A Property Sheet Reference
The following functions, messages, notification messages, and structures are associated with property sheets. These elements can be grouped as follows.
Property Sheet Creation
CreatePropertySheetPage_win32_CreatePropertySheetPageDestroyPropertySheetPage_win32_DestroyPropertySheetPagePropertySheet_win32_PropertySheetPROPSHEETHEADER_win32_PROPSHEETHEADER_strPROPSHEETPAGE_win32_PROPSHEETPAGE_strPropSheetPageProc_win32_PropSheetPageProcPropSheetProc_win32_PropSheetProcPSM_ADDPAGE_win32_PSM_ADDPAGEPSM_REMOVEPAGE_win32_PSM_REMOVEPAGE
Property Sheet Extensions
AddPropSheetPageProc_win32_AddPropSheetPageProcExtensionPropSheetPageProc_win32_ExtensionPropSheetPageProc
Page Activation
PSM_SETCURSEL_win32_PSM_SETCURSELPSM_SETCURSELID_win32_PSM_SETCURSELIDPSN_KILLACTIVE_win32_PSN_KILLACTIVEPSN_SETACTIVE_win32_PSN_SETACTIVE
Property Sheet Buttons
PSM_APPLY_win32_PSM_APPLYPSM_CANCELTOCLOSE_win32_PSM_CANCELTOCLOSEPSM_CHANGED_win32_PSM_CHANGEDPSM_PRESSBUTTON_win32_PSM_PRESSBUTTONPSM_REBOOTSYSTEM_win32_PSM_REBOOTSYSTEMPSM_RESTARTWINDOWS_win32_PSM_RESTARTWINDOWSPSM_UNCHANGED_win32_PSM_UNCHANGEDPSN_APPLY_win32_PSN_APPLYPSN_HELP_win32_PSN_HELPPSN_QUERYCANCEL_win32_PSN_QUERYCANCELPSN_RESET_win32_PSN_RESET
Wizard Property Sheets
PSM_SETFINISHTEXT_win32_PSM_SETFINISHTEXTPSM_SETWIZBUTTONS_win32_PSM_SETWIZBUTTONSPSN_WIZBACK_win32_PSN_WIZBACKPSN_WIZFINISH_win32_PSN_WIZFINISHPSN_WIZNEXT_win32_PSN_WIZNEXT
Miscellaneous Messages
PSM_GETTABCONTROL_win32_PSM_GETTABCONTROLPSM_SETTITLE_win32_PSM_SETTITLEPSM_QUERYSIBLINGS_win32_PSM_QUERYSIBLINGS

+$#A Property Sheet Functions
The following functions are used with property sheets.
AddPropSheetPageProc_win32_AddPropSheetPageProcCreatePropertySheetPage_win32_CreatePropertySheetPageDestroyPropertySheetPage_win32_DestroyPropertySheetPageExtensionPropSheetPageProc_win32_ExtensionPropSheetPageProcPropertySheet_win32_PropertySheetPropSheetPageProc_win32_PropSheetPageProcPropSheetProc_win32_PropSheetProc
+$#A Property Sheet Messages
An application sends messages to direct a property sheet to add and remove pages, enable and disable buttons, set the return value of the PropertySheet_win32_PropertySheet function, and so on. The following are the property sheet messages.
PSM_ADDPAGE_win32_PSM_ADDPAGEPSM_APPLY_win32_PSM_APPLYPSM_CANCELTOCLOSE_win32_PSM_CANCELTOCLOSEPSM_CHANGED_win32_PSM_CHANGEDPSM_GETCURRENTPAGEHWND_win32_PSM_GETCURRENTPAGEHWNDPSM_GETTABCONTROL_win32_PSM_GETTABCONTROLPSM_ISDIALOGMESSAGE_win32_PSM_ISDIALOGMESSAGEPSM_PRESSBUTTON_win32_PSM_PRESSBUTTONPSM_QUERYSIBLINGS_win32_PSM_QUERYSIBLINGSPSM_REBOOTSYSTEM_win32_PSM_REBOOTSYSTEMPSM_REMOVEPAGE_win32_PSM_REMOVEPAGEPSM_RESTARTWINDOWS_win32_PSM_RESTARTWINDOWSPSM_SETCURSEL_win32_PSM_SETCURSELPSM_SETCURSELID_win32_PSM_SETCURSELIDPSM_SETFINISHTEXT_win32_PSM_SETFINISHTEXTPSM_SETTITLE_win32_PSM_SETTITLEPSM_SETWIZBUTTONS_win32_PSM_SETWIZBUTTONSPSM_UNCHANGED_win32_PSM_UNCHANGED
+$#A Property Sheet Notification Messages
A property sheet sends notification messages to the pages to notify them of events. The following are the property sheet notifications.
PSN_APPLY_win32_PSN_APPLYPSN_HELP_win32_PSN_HELPPSN_KILLACTIVE_win32_PSN_KILLACTIVEPSN_QUERYCANCEL_win32_PSN_QUERYCANCELPSN_RESET_win32_PSN_RESETPSN_SETACTIVE_win32_PSN_SETACTIVEPSN_WIZBACK_win32_PSN_WIZBACKPSN_WIZFINISH_win32_PSN_WIZFINISHPSN_WIZNEXT_win32_PSN_WIZNEXT
+$#A Property Sheet Structures
The following structures are used with property sheets.
PROPSHEETHEADER_win32_PROPSHEETHEADER_strPROPSHEETPAGE_win32_PROPSHEETPAGE_str

+$#AK Rich Edit Controls
A rich edit control is a window in which the user can enter and edit text. The text can be assigned character and paragraph formatting, and can include embedded OLE objects. Rich edit controls provide a programming interface for formatting text. However, an application must implement any user interface components necessary to make formatting operations available to the user.
+$#A About Rich Edit Controls
Rich edit controls support almost all of the messages and notification messages used with multiline edit controls. Thus, applications that already use edit controls can be easily changed to use rich edit controls. Additional messages and notifications enable applications to access the functionality unique to rich edit controls. For information about edit controls, see Edit Controls_win32_Edit_Controls.
An application can send messages to a rich edit control to perform such operations as formatting text, printing, and saving. An application can process notification messages to monitor events in a rich edit control. For example, an application can process notifications to filter keyboard and mouse input, to permit or deny changes to protected text, or to resize the control as needed to fit its content.
You create a rich edit control by using the CreateWindowEx_win32_CreateWindowEx function, specifying the "RichEdit" window class. Because the common control library registers this window class, you must call the LoadLibrary_win32_LoadLibrary function to ensure that the RICHED32.DLL library is loaded before the rich edit control is created.
Rich edit controls support most of the window styles used with edit controls as well as additional styles. You should specify the ES_MULTILINE window style if you want to allow more than one line of text in the control.
+$#A Text Formatting
An application can send messages to a rich edit control to format characters and paragraphs and to retrieve formatting information. Paragraph formatting attributes include alignment, tabs, indents, and numbering. For characters, you can specify typeface, size, color, and effects such as bold, italic, and protected.
You can apply paragraph formatting by using the EM_SETPARAFORMAT_win32_EM_SETPARAFORMAT message. To determine the current paragraph formatting for the selected text, use the EM_GETPARAFORMAT_win32_EM_GETPARAFORMAT message. The PARAFORMAT_win32_PARAFORMAT_str structure is used with both messages to specify paragraph formatting attributes.
You can apply character formatting by using the EM_SETCHARFORMAT_win32_EM_SETCHARFORMAT message. To determine the current character formatting for the selected text, you can use the EM_GETCHARFORMAT_win32_EM_GETCHARFORMAT message. The CHARFORMAT_win32_CHARFORMAT_str structure is used with both messages to specify character attributes.
You can also use EM_SETCHARFORMAT and EM_GETCHARFORMAT messages to set and retrieve the default character formatting, which is the formatting applied to any subsequently inserted characters. For example, if an application sets the default character formatting to bold and the user then types a character, that character is bold.
The default character formatting is applied to newly inserted text only if the current selection is empty. Otherwise, the new text assumes the character formatting of the text it replaces. If the selection changes, the default character formatting changes to match the first character in the new selection.
The protected character effect is unique in that it does not change the appearance of text. If the user attempts to modify protected text, a rich edit control sends its parent window an EN_PROTECTED_win32_EN_PROTECTED notification message, allowing the parent window to allow or prevent the change. To receive this notification message, you must enable it by using the EM_SETEVENTMASK_win32_EM_SETEVENTMASK message.
Foreground color is a character attribute, but background color is a property of the rich edit control. To set the background color, use the EM_SETBKGNDCOLOR_win32_EM_SETBKGNDCOLOR message.
+$#A Current Selection in a Rich Edit Control
The user can select text in a rich edit control by using the mouse or the keyboard. The current selection is the range of selected characters, or the position of the insertion point if no characters are selected. An application can get information about the current selection, set the current selection, determine when the current selection changes, and show or hide the selection highlight.
To determine the current selection in a rich edit control, use the EM_EXGETSEL_win32_EM_EXGETSEL message. To set the current selection, use the EM_EXSETSEL_win32_EM_EXSETSEL message. The CHARRANGE_win32_CHARRANGE_str structure is used with both messages and specifies a range of characters. To retrieve information about the contents of the current selection, you can use the EM_SELECTIONTYPE_win32_EM_SELECTIONTYPE message.
An application can detect when the current selection changes by processing the EN_SELCHANGE_win32_EN_SELCHANGE notification message. The notification message specifies a SELCHANGE_win32_SELCHANGE_str structure containing information about the new selection. A rich edit control sends this notification message only if you enable it by using the EM_SETEVENTMASK_win32_EM_SETEVENTMASK message.
By default, a rich edit control shows and hides the selection highlight when it gains and loses the focus. You can show or hide the selection highlight at any time by using the EM_HIDESELECTION_win32_EM_HIDESELECTION message. For example, an application might provide a Search dialog box to find text in a rich edit control. The application might select matching text without closing the dialog box, in which case it must use the EM_HIDESELECTION message to highlight the selection.
As with edit controls, you can specify the ES_NOHIDESEL window style to prevent a rich edit control from hiding the selection highlight when it loses the focus. You can also use the EM_HIDESELECTION message to change the ES_NOHIDESEL window style after a rich edit control is created.
Instead of using the EM_EXGETSEL_win32_EM_EXGETSEL and EM_EXSETSEL_win32_EM_EXSETSEL messages, you retrieve and set the current selection by using the EM_GETSEL_win32_EM_GETSEL and EM_SETSEL_win32_EM_SETSEL edit control messages. The EM_GETSEL message packs two 16-bit character indexes into its 32-bit return value and, therefore, works only for selections that fall entirely within the first 64K. However, a rich edit control will never contain more than 32K of text, unless you extend this limit by using the EM_EXLIMITTEXT_win32_EM_EXLIMITTEXT message. For selections that extend beyond the first 64K of text, the EM_GETSEL message returns - 1.
+$#A Rich Edit Text Operations
An application can send messages to retrieve or find text in a rich edit control. You can retrieve either the selected text or a specified range of text.
To get the selected text in a rich edit control, use the EM_GETSELTEXT_win32_EM_GETSELTEXT message. The text is copied to the specified character array. You must ensure that the array is large enough to hold the selected text plus a terminating null character.
To retrieve a specified range of text, use the EM_GETTEXTRANGE_win32_EM_GETTEXTRANGE message. The TEXTRANGE_win32_TEXTRANGE_str structure used with this message specifies the text range to retrieve and points to a character array that receives the text. Here again, the application must ensure that the array is large enough for the specified text plus a terminating null character.
You can search for a string in a rich edit control by using the EM_FINDTEXT_win32_EM_FINDTEXT message. The FINDTEXT structure used with this message specifies the text range to search and the string to search for. You can also specify such options as whether the search is case-sensitive.
+$#A Word and Line Breaks
A rich edit control calls a function called a word break procedure to find breaks between words and to determine where it can break lines. The control uses this information when performing word wrap operations and when processing the CTRL+LEFT and CTRL+RIGHT key combinations. An application can send messages to a rich edit control to replace the default word break procedure, to retrieve word break information, and to determine what line a given character falls on.
Word break procedures for rich edit controls are similar to those for edit controls, but they have additional capabilities. Word break procedures for both kinds of controls can determine whether a character is a delimiter and can find the nearest word break before or after the specified position. A delimiter is a character that marks the end of a word, such as a space. In an edit control, word breaks occur only after delimiters.
Word break procedures for rich edit controls also group characters into character classes, each identified by a value in the range 0x00 through 0x0F. Word breaks occur either after delimiters or between characters of different classes. Thus, a word break procedure with different classes for alphanumeric and punctuation characters would find two word breaks in the string "WIN.COM".
A character's class can be combined with zero or more word break flags to form an eight-bit value. When performing word wrap operations, a rich edit control uses word break flags to determine where it can break lines. There are the following word break flags.
WBF_BREAKAFTER Lines may be broken after the character.
WBF_BREAKLINE The character is a delimiter. Delimiters mark the ends of words. Lines may be broken after delimiters.
WBF_ISWHITE The character is a white space character. Trailing white space characters are not included in the length of a line when wrapping.

The WBF_BREAKAFTER value is used to allow wrapping after a character that does not mark the end of a word, such as a hyphen.
You can replace a rich edit control's default word break procedure with your own by using the EM_SETWORDBREAKPROC_win32_EM_SETWORDBREAKPROC message. For more information about word break procedures, see the description of the EditWordBreakProc_win32_EditWordBreakProc function.
Alternately, you can use the EM_SETWORDBREAKPROCEX_win32_EM_SETWORDBREAKPROCEX message to replace the default extended word break procedure with an EditWordBreakProcEx_win32_EditWordBreakProcEx function. This function provides additional information about the text, such as the character set. You can use the EM_GETWORDBREAKPROCEX_win32_EM_GETWORDBREAKPROCEX message to retrieve the address of the current extended word break procedure.
You can use the EM_FINDWORDBREAK_win32_EM_FINDWORDBREAK message to find word breaks or to determine a character's class and word break flags. In turn, the control calls its word break procedure to get the requested information.
To determine which line a given character falls on, you can use the EM_EXLINEFROMCHAR_win32_EM_EXLINEFROMCHAR message.
+$#A Rich Edit Clipboard Operations
An application can paste the contents of the clipboard into a rich edit control using either the best available clipboard format or a specific clipboard format. You can also determine whether a rich edit control is capable of pasting a clipboard format.
As with an edit control, you can copy or cut the contents of the current selection by using the WM_COPY_win32_WM_COPY or WM_CUT_win32_WM_CUT message. Similarly, you can paste the contents of the clipboard into a rich edit control by using the WM_PASTE_win32_WM_PASTE message. The control pastes the first available format that it recognizes, which presumably is the most descriptive format.
To paste a specific clipboard format, you can use the EM_PASTESPECIAL_win32_EM_PASTESPECIAL message. This message is useful for applications with a Paste Special command that enables the user to select the clipboard format. You can use the EM_CANPASTE_win32_EM_CANPASTE message to determine whether a given format is recognized by the control.
You can also use the EM_CANPASTE message to determine whether any available clipboard format is recognized by a rich edit control. This message is useful when processing the WM_INITMENUPOPUP_win32_WM_INITMENUPOPUP message. An application might enable or gray its Paste command depending on whether the control can paste any available format.
Rich edit controls register two clipboard formats: rich-text format and a format called RichEdit Text and Objects. An application can register these formats by using the RegisterClipboardFormat_win32_RegisterClipboardFormat function, specifying the CF_RTF and CF_RETEXTOBJ values.
+$#A Streams
You can use streams to transfer data into or out of a rich edit control. A stream is defined by an EDITSTREAM_win32_EDITSTREAM_str structure, which specifies a buffer and an application-defined callback function.
To read data into a rich edit control (that is, stream the data in), use the EM_STREAMIN_win32_EM_STREAMIN message. The control repeatedly calls the application's callback function, which transfers a portion of the data into the buffer each time.
To save the contents of a rich edit control (that is, stream the data out), you can use the EM_STREAMOUT_win32_EM_STREAMOUT message. The control repeatedly writes to the buffer and then calls the application's callback function. For each call, the callback function saves the contents of the buffer.
+$#A Printing
You can send messages to a rich edit control to render its output for a specified device, such as a printer. You can also specify the output device for which a rich edit control formats its text.
To format part of a rich edit control's contents for a specific device, you can use the EM_FORMATRANGE_win32_EM_FORMATRANGE message. The FORMATRANGE_win32_FORMATRANGE_str structure used with this message specifies the range of text to format as well as the device context (DC) for the target device.
After formatting text for an output device, you can send the output to the device by using the EM_DISPLAYBAND_win32_EM_DISPLAYBAND message. By repeatedly using the EM_FORMATRANGE and EM_DISPLAYBAND messages, an application that prints the contents of a rich edit control can implement banding. (Banding is division of output into smaller parts for pringing purposes).
You can use the EM_SETTARGETDEVICE_win32_EM_SETTARGETDEVICE message to specify the target device for which a rich edit control formats its text. This message is useful for WYSIWYG (what you see is what you get) formatting, in which an application positions text using the default printer's font metrics instead of the screen's.
+$#A Bottomless Rich Edit Controls
An application can resize a rich edit control as needed so that it is always the same size as its contents. A rich edit control supports this so-called "bottomless" functionality by sending its parent window an EN_REQUESTRESIZE_win32_EN_REQUESTRESIZE notification message whenever the size of its contents changes.
When processing the EN_REQUESTRESIZE notification message, an application should resize the control to the dimensions in the specified REQRESIZE_win32_REQRESIZE_str structure. An application might also move any information near the control to accommodate the control's change in height. To resize the control, you can use the SetWindowPos_win32_SetWindowPos function.
You can force a bottomless rich edit control to send an EN_REQUESTRESIZE_win32_EN_REQUESTRESIZE notification message by using the EM_REQUESTRESIZE_win32_EM_REQUESTRESIZE message. This message can be useful when processing the WM_SIZE_win32_WM_SIZE message.
To receive EN_REQUESTRESIZE notification messages, you must enable the notification by using the EM_SETEVENTMASK_win32_EM_SETEVENTMASK message.
+$#A Miscellaneous Notification Messages
A rich edit control's parent window can process notification messages to monitor events affecting the control. Rich edit controls support all of the notification messages used with edit controls as well as several additional ones. You can determine which notification messages a rich edit control sends its parent window by setting its event mask.
To set the event mask for a rich edit control, use the EM_SETEVENTMASK_win32_EM_SETEVENTMASK message. You can retrieve the current event mask for a rich edit control by using the EM_GETEVENTMASK_win32_EM_GETEVENTMASK message.
A rich edit control's parent window can filter all keyboard and mouse input to the control by processing the EN_MSGFILTER_win32_EN_MSGFILTER notification message. The parent window can prevent the keyboard or mouse message from being processed or can change the message by modifying the specified MSGFILTER_win32_MSGFILTER_str structure.
An application can process the EN_PROTECTED_win32_EN_PROTECTED notification message to detect when the users attempts to modify protected text. To mark a range of text as protected, you can set the protected character effect. For more information, see Text Formatting_win32_Text_Formatting.
You can enable the user to drop files in a rich edit control by processing the EN_DROPFILES_win32_EN_DROPFILES notification message. The specified ENDROPFILES_win32_ENDROPFILES_str structure contains information about the files being dropped.
+$#A Unsupported Edit Control Functionality
Rich edit controls support most but not all of the functionality of multiline edit controls. This section lists the edit control messages and window styles that are not supported by rich edit controls.
The following messages are processed by edit controls but not by rich edit controls.
Unsupported message Comments
EM_FMTLINES_win32_EM_FMTLINES Not supported.
EM_GETHANDLE_win32_EM_GETHANDLE Rich edit controls do not store text as a simple array of characters.
EM_GETMARGINS_win32_EM_GETMARGINS Not supported.
EM_GETPASSWORDCHAR_win32_EM_GETPASSWORDCHAR The ES_PASSWORD style is not supported.
EM_SETHANDLE_win32_EM_SETHANDLE Rich edit controls do not store text as a simple array of characters.
EM_SETMARGINS_win32_EM_SETMARGINS Not supported.
EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR The ES_PASSWORD style is not supported.
EM_SETRECTNP_win32_EM_SETRECTNP Not supported.
EM_SETTABSTOPS_win32_EM_SETTABSTOPS The EM_SETPARAFORMAT_win32_EM_SETPARAFORMAT message is used instead.
WM_CTLCOLOR The EM_SETBKGNDCOLOR_win32_EM_SETBKGNDCOLOR message is used instead.
WM_GETFONT_win32_WM_GETFONT The EM_GETCHARFORMAT_win32_EM_GETCHARFORMAT message is used instead.

The following window styles are used with multiline edit controls but not with rich edit controls:
ES_LOWERCASE ES_PASSWORD
ES_OEMCONVERT ES_UPPERCASE

+$#AK Rich Edit Control Reference
The messages, notification messages, and structures associated with rich edit controls are grouped as follows. Some of these application programming interface (API) elements are also used with edit controls.
Formatting
CHARFORMAT_win32_CHARFORMAT_strEM_GETCHARFORMAT_win32_EM_GETCHARFORMATEM_GETPARAFORMAT_win32_EM_GETPARAFORMATEM_GETRECT_win32_EM_GETRECTEM_SETBKGNDCOLOR_win32_EM_SETBKGNDCOLOREM_SETCHARFORMAT_win32_EM_SETCHARFORMATEM_SETEVENTMASK_win32_EM_SETEVENTMASKEM_SETPARAFORMAT_win32_EM_SETPARAFORMATEM_SETRECT_win32_EM_SETRECTPARAFORMAT_win32_PARAFORMAT_str
Selection and Hit Testing
CHARRANGE_win32_CHARRANGE_strEM_CHARFROMPOS_win32_EM_CHARFROMPOSEM_EXGETSEL_win32_EM_EXGETSELEM_EXSETSEL_win32_EM_EXSETSELEM_GETFIRSTVISIBLELINE_win32_EM_GETFIRSTVISIBLELINEEM_GETSEL_win32_EM_GETSELEM_HIDESELECTION_win32_EM_HIDESELECTIONEM_POSFROMCHAR_win32_EM_POSFROMCHAREM_SELECTIONTYPE_win32_EM_SELECTIONTYPEEM_SETSEL_win32_EM_SETSELEN_SELCHANGE_win32_EN_SELCHANGESELCHANGE_win32_SELCHANGE_str
Text Operations
EM_EXLIMITTEXT_win32_EM_EXLIMITTEXTEM_FINDTEXT_win32_EM_FINDTEXTEM_GETLIMITTEXT_win32_EM_GETLIMITTEXTEM_GETSELTEXT_win32_EM_GETSELTEXTEM_GETTEXTRANGE_win32_EM_GETTEXTRANGEEM_REPLACESEL_win32_EM_REPLACESELEM_SETLIMITTEXT_win32_EM_SETLIMITTEXTFINDTEXT_win32_FINDTEXT_strTEXTRANGE_win32_TEXTRANGE_str
Word and Line Breaks
EM_EXLINEFROMCHAR_win32_EM_EXLINEFROMCHAREM_FINDWORDBREAK_win32_EM_FINDWORDBREAKEM_GETWORDBREAKPROC_win32_EM_GETWORDBREAKPROCEM_SETWORDBREAKPROC_win32_EM_SETWORDBREAKPROC EM_GETWORDBREAKPROCEX_win32_EM_GETWORDBREAKPROCEXEM_SETWORDBREAKPROCEX_win32_EM_SETWORDBREAKPROCEXEditWordBreakProcEx_win32_EditWordBreakProcEx
Lines and Scrolling
EM_GETLINE_win32_EM_GETLINEEM_GETLINECOUNT_win32_EM_GETLINECOUNTEM_GETTHUMB_win32_EM_GETTHUMBEM_LINEFROMCHAR_win32_EM_LINEFROMCHAREM_LINEINDEX_win32_EM_LINEINDEXEM_LINELENGTH_win32_EM_LINELENGTHEM_LINESCROLL_win32_EM_LINESCROLLEM_SCROLL_win32_EM_SCROLLEM_SCROLLCARET_win32_EM_SCROLLCARET
Editing Operations
EM_CANPASTE_win32_EM_CANPASTEEM_CANUNDO_win32_EM_CANUNDOEM_EMPTYUNDOBUFFER_win32_EM_EMPTYUNDOBUFFEREM_PASTESPECIAL_win32_EM_PASTESPECIALEM_UNDO_win32_EM_UNDO
Streams
EDITSTREAM_win32_EDITSTREAM_strEM_STREAMIN_win32_EM_STREAMINEM_STREAMOUT_win32_EM_STREAMOUT
Printing
EM_DISPLAYBAND_win32_EM_DISPLAYBANDEM_FORMATRANGE_win32_EM_FORMATRANGEEM_SETTARGETDEVICE_win32_EM_SETTARGETDEVICEFORMATRANGE_win32_FORMATRANGE_str
Bottomless Rich Edit Controls
EM_REQUESTRESIZE_win32_EM_REQUESTRESIZEEN_REQUESTRESIZE_win32_EN_REQUESTRESIZEREQRESIZE_win32_REQRESIZE_str
OLE Interfaces
IRichEditOle_win32_IRichEditOleIRichEditOleCallback_win32_IRichEditOleCallback
Miscellaneous
EM_GETEVENTMASK_win32_EM_GETEVENTMASKEM_GETIMECOLOR_win32_EM_GETIMECOLOREM_GETIMEOPTIONS_win32_EM_GETIMEOPTIONSEM_GETMODIFY_win32_EM_GETMODIFYEM_GETOPTIONS_win32_EM_GETOPTIONSEM_GETPUNCTUATION_win32_EM_GETPUNCTUATIONEM_GETWORDWRAPMODE_win32_EM_GETWORDWRAPMODEEM_SETEVENTMASK_win32_EM_SETEVENTMASKEM_SETIMECOLOR_win32_EM_SETIMECOLOREM_SETIMEOPTIONS_win32_EM_SETIMEOPTIONSEM_SETEVENTMASK_win32_EM_SETEVENTMASKEM_SETMODIFY_win32_EM_SETMODIFYEM_SETREADONLY_win32_EM_SETREADONLYEM_SETOPTIONS_win32_EM_SETOPTIONSEM_SETPUNCTUATION_win32_EM_SETPUNCTUATIONEM_SETWORDWRAPMODE_win32_EM_SETWORDWRAPMODEEN_CHANGE_win32_EN_CHANGEEN_CORRECTTEXT_win32_EN_CORRECTTEXTEN_DROPFILES_win32_EN_DROPFILESEN_ERRSPACE_win32_EN_ERRSPACEEN_HSCROLL_win32_EN_HSCROLLEN_IMECHANGE_win32_EN_IMECHANGEEN_KILLFOCUS_win32_EN_KILLFOCUSEN_MAXTEXT_win32_EN_MAXTEXTEN_MSGFILTER_win32_EN_MSGFILTEREN_OLEOPFAILED_win32_EN_OLEOPFAILEDEN_PROTECTED_win32_EN_PROTECTEDEN_SAVECLIPBOARD_win32_EN_SAVECLIPBOARDEN_SETFOCUS_win32_EN_SETFOCUSEN_STOPNOUNDO_win32_EN_STOPNOUNDOEN_UPDATE_win32_EN_UPDATEEN_VSCROLL_win32_EN_VSCROLLIMC_GETCOMPOSITIONCOLOR_win32_IMC_GETCOMPOSITIONCOLORIMC_SETCOMPOSITIONCOLOR_win32_IMC_SETCOMPOSITIONCOLORWM_CONTEXTMENU_win32_WM_CONTEXTMENUWM_CTLCOLOREDIT_win32_WM_CTLCOLOREDITCOMPCOLOR_win32_COMPCOLOR_strENCORRECTTEXT_win32_ENCORRECTTEXT_strENDROPFILES_win32_ENDROPFILES_strENOLEOPFAILED_win32_ENOLEOPFAILED_strENPROTECTED_win32_ENPROTECTED_strENSAVECLIPBOARD_win32_ENSAVECLIPBOARD_strPUNCTUATION_win32_PUNCTUATION_strMSGFILTER_win32_MSGFILTER_str
+$#AK Rich Edit Callback Functions
The following callback function is unique to rich edit controls:
EditWordBreakProcEx_win32_EditWordBreakProcEx
+$#A Rich Edit Messages
The following messages are unique to rich edit controls.
EM_CANPASTE_win32_EM_CANPASTEEM_DISPLAYBAND_win32_EM_DISPLAYBANDEM_EXGETSEL_win32_EM_EXGETSELEM_EXLIMITTEXT_win32_EM_EXLIMITTEXTEM_EXLINEFROMCHAR_win32_EM_EXLINEFROMCHAREM_EXSETSEL_win32_EM_EXSETSELEM_FINDTEXT_win32_EM_FINDTEXTEM_FINDTEXTEX_win32_EM_FINDTEXTEXEM_FINDWORDBREAK_win32_EM_FINDWORDBREAKEM_FORMATRANGE_win32_EM_FORMATRANGEEM_GETCHARFORMAT_win32_EM_GETCHARFORMATEM_GETEVENTMASK_win32_EM_GETEVENTMASKEM_GETIMECOLOR_win32_EM_GETIMECOLOREM_GETIMEOPTIONS_win32_EM_GETIMEOPTIONSEM_GETOLEINTERFACE_win32_EM_GETOLEINTERFACEEM_GETOPTIONS_win32_EM_GETOPTIONSEM_GETPARAFORMAT_win32_EM_GETPARAFORMATEM_GETPUNCTUATION_win32_EM_GETPUNCTUATIONEM_GETSELTEXT_win32_EM_GETSELTEXTEM_GETTEXTRANGE_win32_EM_GETTEXTRANGEEM_GETWORDBREAKPROCEX_win32_EM_GETWORDBREAKPROCEXEM_GETWORDWRAPMODE_win32_EM_GETWORDWRAPMODEEM_HIDESELECTION_win32_EM_HIDESELECTIONEM_PASTESPECIAL_win32_EM_PASTESPECIALEM_REQUESTRESIZE_win32_EM_REQUESTRESIZEEM_SELECTIONTYPE_win32_EM_SELECTIONTYPEEM_SETBKGNDCOLOR_win32_EM_SETBKGNDCOLOREM_SETCHARFORMAT_win32_EM_SETCHARFORMATEM_SETEVENTMASK_win32_EM_SETEVENTMASKEM_SETIMECOLOR_win32_EM_SETIMECOLOREM_SETIMEOPTIONS_win32_EM_SETIMEOPTIONSEM_SETOLEINTERFACE_win32_EM_SETOLEINTERFACEEM_SETOPTIONS_win32_EM_SETOPTIONSEM_SETPARAFORMAT_win32_EM_SETPARAFORMATEM_SETPUNCTUATION_win32_EM_SETPUNCTUATIONEM_SETTARGETDEVICE_win32_EM_SETTARGETDEVICEEM_SETWORDBREAKPROCEX_win32_EM_SETWORDBREAKPROCEXEM_SETWORDWRAPMODE_win32_EM_SETWORDWRAPMODEEM_STREAMIN_win32_EM_STREAMINEM_STREAMOUT_win32_EM_STREAMOUTIMC_GETCOMPOSITIONCOLOR_win32_IMC_GETCOMPOSITIONCOLORIMC_SETCOMPOSITIONCOLOR_win32_IMC_SETCOMPOSITIONCOLORWM_CONTEXTMENU_win32_WM_CONTEXTMENU
+$#A Rich Edit Notification Messages
Rich edit controls support most of the notification messages used with edit controls, plus the following notification messages. A rich edit control sends the following notification messages only if they have been enabled by using the EM_SETEVENTMASK_win32_EM_SETEVENTMASK message.
EN_CORRECTTEXT_win32_EN_CORRECTTEXTEN_DROPFILES_win32_EN_DROPFILESEN_IMECHANGE_win32_EN_IMECHANGEEN_MSGFILTER_win32_EN_MSGFILTEREN_OLEOPFAILED_win32_EN_OLEOPFAILEDEN_PROTECTED_win32_EN_PROTECTEDEN_REQUESTRESIZE_win32_EN_REQUESTRESIZEEN_SAVECLIPBOARD_win32_EN_SAVECLIPBOARDEN_SELCHANGE_win32_EN_SELCHANGEEN_STOPNOUNDO_win32_EN_STOPNOUNDO
+$#A Rich Edit Structures
The following structures are used with rich edit controls.
CHARFORMAT_win32_CHARFORMAT_strCHARRANGE_win32_CHARRANGE_strCOMPCOLOR_win32_COMPCOLOR_strEDITSTREAM_win32_EDITSTREAM_strENDROPFILES_win32_ENDROPFILES_strENOLEOPFAILED_win32_ENOLEOPFAILED_strENPROTECTED_win32_ENPROTECTED_strENSAVECLIPBOARD_win32_ENSAVECLIPBOARD_strFINDTEXT_win32_FINDTEXT_strFINDTEXTEX_win32_FINDTEXTEX_strFORMATRANGE_win32_FORMATRANGE_strMSGFILTER_win32_MSGFILTER_strPARAFORMAT_win32_PARAFORMAT_strPUNCTUATION_win32_PUNCTUATION_strREOBJECT_win32_REOBJECT_strREPASTESPECIAL_win32_REPASTESPECIAL_strREQRESIZE_win32_REQRESIZE_strSELCHANGE_win32_SELCHANGE_strTEXTRANGE_win32_TEXTRANGE_str
+$#A Rich Edit OLE Interfaces
The rich edit control supports the client functionality defined by OLE. The control provides full support for most OLE client features. It does not support linking to its contents. The client is responsible for dialogs and error messages, managing storage for OLE objects, and document and application window level in-place activation support. The client can use the EM_GETOLEINTERFACE message to obtain an IRichEditOle interface from the rich edit control which allows it to manage objects. The client uses the EM_SETOLEINTERFACE message to register an IRichEditOleCallback interface that the control uses to obtain the required interfaces and storage.
The following OLE interfaces are used with rich text edit controls:
IRichEditOle_win32_IRichEditOleIRichEditOleCallback_win32_IRichEditOleCallback

+$#A Rich Edit Constants
The following sections describe constants used with rich edit controls.
+$#A Rich Edit Control Styles
The following window styles are unique to rich edit controls:
Style Description
ES_DISABLENOSCROLL Disables scrollbars instead of hiding them when they are not needed.
ES_EX_NOCALLOLEINIT Prevents the control from calling the OleInitialize!AL(`_ole_OleInitialize',3,`No_SDK') function when created. Useful only in dialog templates because CreateWindowEx_win32_CreateWindowEx does not accept this style.
ES_NOIME Disables the input method editor (IME) operation. Available for Asian-languages only.
ES_SAVESEL Preserves the selection when the control loses the focus. By default, the entire contents of the control are selected when it regains the focus.
ES_SELFIME Directs the rich edit control to allow the application to handle all IME operations. Available for Asian-languages only.
ES_SUNKEN Displays the control with a sunken border style so that the rich edit control appears recessed into its parent window. Applications developed for Windows 95 should use WS_EX_CLIENTEDGE instead of ES_SUNKEN.
ES_VERTICAL Draws text and objects in a vertical direction. Available for Asian-languages only.

Rich edit controls also support the following edit control styles. To allow more than one line of text, you must specify the ES_MULTILINE style.
ES_AUTOHSCROLL ES_NOHIDESEL
ES_AUTOVSCROLL ES_READONLY
ES_CENTER ES_RIGHT
ES_LEFT ES_WANTRETURN
ES_MULTILINE

Rich edit controls do not support the following edit control styles:
ES_LOWERCASE ES_PASSWORD
ES_OEMCONVERT ES_UPPERCASE

+$#A Rich Edit Control Event Mask Flags
The event mask specifies which notification messages a rich edit control sends its parent window. The event mask can be zero or more of these values:
Value Meaning
ENM_CHANGE Sends EN_CHANGE_win32_EN_CHANGE notifications.
ENM_CORRECTTEXT Sends EN_CORRECTTEXT_win32_EN_CORRECTTEXT notifications.
ENM_DROPFILES Sends EN_DROPFILES_win32_EN_DROPFILES notifications.
ENM_KEYEVENTS Sends EN_MSGFILTER_win32_EN_MSGFILTER notifications for keyboard events.
ENM_MOUSEEVENTS Sends EN_MSGFILTER_win32_EN_MSGFILTER notifications for mouse events.
ENM_PROTECTED Sends EN_PROTECTED_win32_EN_PROTECTED notifications.
ENM_RESIZEREQUEST Sends EN_REQUESTRESIZE_win32_EN_REQUESTRESIZE notifications.
ENM_SCROLL Sends EN_HSCROLL_win32_EN_HSCROLL notifications.
ENM_SELCHANGE Sends EN_SELCHANGE_win32_EN_SELCHANGE notifications.
ENM_UPDATE Sends EN_UPDATE_win32_EN_UPDATE notifications.

The default event mask is ENM_NONE in which case no notification messages are sent to the parent window. You can retrieve and set the event mask for a rich edit control by using the ENM_GETEVENTMASK and ENM_SETEVENTMASK messages.

+$#AK Status Windows
A status window is a horizontal window at the bottom of a parent window in which an application can display various kinds of status information. The status window can be divided into parts to display more than one type of information. The following illustration shows the status bar in the Microsoft® Windows® Paint application.
{ewc msdncd, EWGraphic, group10604 0 /a "SDK_01.bmp"}
+$#A About Status Windows
You can create a status window by using the CreateStatusWindow_win32_CreateStatusWindow function or by using the CreateWindowEx_win32_CreateWindowEx function and specifying the STATUSCLASSNAME window class. To ensure that the common control dynamic-link library (DLL) is loaded, use the InitCommonControls_win32_InitCommonControls function first. After you create the status window, you can divide it into parts, set the text for each part, and control the appearance of the window by using status window messages.
+$#A Types and Styles
The default position of a status window is along the bottom of the parent window, but you can specify the CCS_TOP style to have it appear at the top of the parent window's client area.
You can specify the SBARS_SIZEGRIP style to include a sizing grip at the right end of the status window. A sizing grip is similar to a sizing border; it is a rectangular area that the user can click and drag to resize the parent window.

Note You can combine the CCS_TOP and SBARS_SIZEGRIP styles. Doing so, however, is not recommended, because the resulting sizing grip is not functional even though the system draws it in the status window.

+$#A Size and Height
The window procedure for the status window automatically sets the initial size and position of the window, ignoring the values specified in the CreateWindowEx_win32_CreateWindowEx function. The width is the same as that of the parent window's client area. The height is based on the metrics of the font that is currently selected into the status window's device context and on the width of the window's borders.
The window procedure automatically adjusts the size of the status window whenever it receives a WM_SIZE_win32_WM_SIZE message. Typically, when the size of the parent window changes, the parent sends a WM_SIZE message to the status window.
An application can set the minimum height of a status window's drawing area by sending the window an SB_SETMINHEIGHT_win32_SB_SETMINHEIGHT message, specifying the minimum height, in pixels. The drawing area does not include the window's borders. A minimum height is useful for drawing in an owner-drawn status window. For more information about owner-drawn status windows, see Owner-Drawn Status Windows_win32_Owner.2d.Drawn_Status_Windows.
You retrieve the widths of the borders of a status window by sending the window an SB_GETBORDERS_win32_SB_GETBORDERS message. The message includes the pointer to a three element array that receives the widths.
+$#A Multiple-Part Status Windows
A status window can have many different parts, each displaying a different line of text. You divide a status window into parts by sending the window an SB_SETPARTS_win32_SB_SETPARTS message, specifying the number of parts to create and the pointer to an integer array. The array contains one element for each part, and each element specifies the client coordinate of the right edge of a part.
A status window can have a maximum of 255 parts, although applications typically use far fewer than that. You retrieve a count of the parts in a status window, as well as the coordinate of the right edge of each part, by sending the window an SB_GETPARTS_win32_SB_GETPARTS message.
+$#A Status Window Text Operations
You set the text of any part of a status window by sending the SB_SETTEXT_win32_SB_SETTEXT message, specifying the zero-based index of a part, a pointer to the string to draw in the part, and the technique for drawing the string. The drawing technique determines whether the text has a border and, if it does, the style of the border. It also determines whether the parent window is responsible for drawing the text. For more information, see the following topic.
By default, text is left-aligned within the specified part of a status window. You can embed tab characters (\ t) in the text to center or right-align it. Text to the right of a single tab character is centered, and text to the right of a second tab character is right-aligned.
To retrieve text from a status window, use the SB_GETTEXTLENGTH_win32_SB_GETTEXTLENGTH and SB_GETTEXT_win32_SB_GETTEXT messages.
If your application uses a status window that has only one part, you can use the WM_SETTEXT_win32_WM_SETTEXT, WM_GETTEXT_win32_WM_GETTEXT, and WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH messages to perform text operations. These messages deal only with the part that has an index of zero, allowing you to treat the status window much like a static text control.
To display a line of status without creating a status window, use the DrawStatusText_win32_DrawStatusText function. The function uses the same techniques to draw the status as the window procedure for the status window, but it does not automatically set the size and position of the status information. When calling the function, you must specify the size and position of the status information, as well as the device context of the window in which to draw it.
+$#A Owner-Drawn Status Windows
You can define individual parts of a status window to be owner-drawn parts. Using this technique gives you more control than you would otherwise have over the appearance of the window part. For example, you can display a bitmap rather than text in a part or draw text using a different font.
To define a window part as owner-drawn, send the SB_SETTEXT_win32_SB_SETTEXT message to the status window, specifying the part and the SBT_OWNERDRAW drawing technique. When SBT_OWNERDRAW is specified, the lParam parameter is a 32-bit application-defined value that the application can use when drawing the part. For example, you can specify a font handle, a bitmap handle, a pointer to a string, and so on.
When a status window needs to draw an owner-drawn part, it sends the WM_DRAWITEM_win32_WM_DRAWITEM message to the parent window. The wParam parameter of the message is the child window identifier of the status window, and the lParam parameter is a pointer to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure. The parent window uses the information in the structure to draw the part. For an owner-drawn part of a status window, DRAWITEMSTRUCT contains the following information.
Member Description
CtlType Undefined; do not use.
CtlID Child-window identifier of the status window.
itemID Zero-based index of the part to be drawn.
itemAction Undefined; do not use.
itemState Undefined; do not use.
hwndItem Handle to the status window.
hDC Handle to the device context of the status window.
rcItem Coordinates of the window part to be drawn. The coordinates are relative to the upper left corner of the status window.

itemData Application-defined 32-bit value specified in the lParam parameter of the SB_SETTEXT_win32_SB_SETTEXT message.

+$#A Simple Mode Status Windows
You put a status window into "simple mode" by sending it an SB_SIMPLE_win32_SB_SIMPLE message. A simple mode status window displays only one part. When the text of the window is set, the window is invalidated, but it is not redrawn until the next WM_PAINT_win32_WM_PAINT message. Waiting for the message reduces screen flicker by minimizing the number of times the window is redrawn. A simple mode status window is useful for displaying help text for menu items while the user is scrolling through a menu.
The string that a status window displays while in simple mode is maintained separately from the strings that it displays while not in simple mode. This means that you can put the window in simple mode, set its text, and switch back to nonsimple mode without the text being used in either mode.
When setting the text of a simple mode status window, you can specify any drawing technique except SBT_OWNERDRAW. A simple mode status window does not support owner drawing.
+$#A Default Status Window Message Processing
This section describes the window messages handled by the window procedure for the predefined STATUSCLASSNAME window class.
Message Default processing
WM_CREATE_win32_WM_CREATE Initializes the status window.
WM_DESTROY_win32_WM_DESTROY Frees resources allocated for the status window.
WM_GETFONT_win32_WM_GETFONT Returns the handle to the current font with which the status window draws its text.
WM_GETTEXT_win32_WM_GETTEXT Copies the text from the first part of a status window to a buffer. It returns a 32-bit value that specifies the length, in characters, of the text and the technique used to draw the text.
WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH Returns a 32-bit value that specifies the length, in characters, of the text in the first part of a status window and the technique used to draw the text.
WM_NCHITTEST_win32_WM_NCHITTEST Returns the HTBOTTOMRIGHT value if the mouse cursor is in the sizing grip, causing the system to display the sizing cursor. If the mouse cursor is not in the sizing grip, the status window passes this message to the DefWindowProc_win32_DefWindowProc function.
WM_PAINT_win32_WM_PAINT Paints the invalid region of the status window.
WM_SETFONT_win32_WM_SETFONT Selects the font handle into the device context for the status window.
WM_SETTEXT_win32_WM_SETTEXT Copies the specified text into the first part of a status window, using the default drawing operation (specified as zero). It returns TRUE if successful or FALSE otherwise.
WM_SIZE_win32_WM_SIZE Resizes the status window based on the current width of the parent window's client area and the height of the current font of the status window.

+$#A Using Status Windows
The following example creates a status window that has a sizing grip and divides the window into four equal parts based on the width of the parent window's client area.
// DoCreateStatusWindow - creates a status window and divides it into
// the specified number of parts.
// Returns the handle to the status window.
// hwndParent - parent window for the status window
// nStatusID - child window identifier
// hinst - handle to the application instance
// nParts - number of parts into which to divide the status window
HWND DoCreateStatusWindow(HWND hwndParent, int nStatusID,
HINSTANCE hinst, int nParts)
{
HWND hwndStatus;
RECT rcClient;
HLOCAL hloc;
LPINT lpParts;
int i, nWidth;

// Ensure that the common control DLL is loaded.
InitCommonControls();

// Create the status window.
hwndStatus = CreateWindowEx(
0, // no extended styles
STATUSCLASSNAME, // name of status window class
(LPCTSTR) NULL, // no text when first created
SBARS_SIZEGRIP | // includes a sizing grip
WS_CHILD, // creates a child window
0, 0, 0, 0, // ignores size and position
hwndParent, // handle to parent window
(HMENU) nStatusID, // child window identifier
hinst, // handle to application instance
NULL); // no window creation data

// Get the coordinates of the parent window's client area.
GetClientRect(hwndParent, &rcClient);

// Allocate an array for holding the right edge coordinates.
hloc = LocalAlloc(LHND, sizeof(int) * nParts);
lpParts = LocalLock(hloc);

// Calculate the right edge coordinate for each part, and
// copy the coordinates to the array.
nWidth = rcClient.right / nParts;
for (i = 0; i < nParts; i++) {
lpParts[i] = nWidth;
nWidth += nWidth;
}

// Tell the status window to create the window parts.
SendMessage(hwndStatus, SB_SETPARTS, (WPARAM) nParts,
(LPARAM) lpParts);

// Free the array, and return.
LocalUnlock(hloc);
LocalFree(hloc);
return hwndStatus;
}

+$#A Status Window Reference
The following functions and messages are associated with status windows. These elements can be grouped as follows.
Creation
CreateStatusWindow_win32_CreateStatusWindow
Size and Height
SB_GETRECT_win32_SB_GETRECTSB_SETMINHEIGHT_win32_SB_SETMINHEIGHTSB_GETBORDERS_win32_SB_GETBORDERS
Text Operations
DrawStatusText_win32_DrawStatusTextMenuHelp_win32_MenuHelpSB_GETTEXT_win32_SB_GETTEXTSB_GETTEXTLENGTH_win32_SB_GETTEXTLENGTHSB_SETTEXT_win32_SB_SETTEXT
Multiple Part Status Windows
SB_GETPARTS_win32_SB_GETPARTSSB_SETPARTS_win32_SB_SETPARTS
Simple Mode Status Windows
SB_SIMPLE_win32_SB_SIMPLE

+$#A Status Window Functions
The following functions are used with status windows.
CreateStatusWindow_win32_CreateStatusWindowDrawStatusText_win32_DrawStatusTextMenuHelp_win32_MenuHelp
+$#A Status Window Messages
The following messages are used with status windows.
SB_GETBORDERS_win32_SB_GETBORDERSSB_GETPARTS_win32_SB_GETPARTSSB_GETRECT_win32_SB_GETRECTSB_GETTEXT_win32_SB_GETTEXTSB_GETTEXTLENGTH_win32_SB_GETTEXTLENGTHSB_SETMINHEIGHT_win32_SB_SETMINHEIGHTSB_SETPARTS_win32_SB_SETPARTSSB_SETTEXT_win32_SB_SETTEXTSB_SIMPLE_win32_SB_SIMPLE

+$#AK Tab Controls
A tab control is analogous to the dividers in a notebook or the labels in a file cabinet. By using a tab control, an application can define multiple pages for the same area of a window or dialog box. Each page consists of a set of information or a group of controls that the application displays when the user selects the corresponding tab. A special type of tab control displays tabs that look like buttons. Clicking a button should immediately perform a command instead of displaying a page.
+$#A About Tab Controls
You can create a tab control by calling the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_TABCONTROL window class. This window class is registered when the common controls dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls_win32_InitCommonControls function.
You send messages to a tab control to add tabs and otherwise affect the control's appearance and behavior. Each message has a corresponding macro, which you can use instead of sending the message explicitly. You cannot, however, disable an individual tab in a tab control. However, you can disable a tab control in a property sheet by disabling the corresponding page.
+$#A About Tab Control Styles
You can apply specific characteristics to tab controls by specifying tab control styles (window styles specific to tab controls). For example, you can specify the alignment and general appearance of the tabs in a tab control.
You can cause the tabs to look like buttons by specifying the TCS_BUTTONS style. Tabs in this type of tab control should serve the same function as button controls; that is, clicking a tab should carry out a command instead of displaying a page. Because the display area in a button tab control is typically not used, no border is drawn around it.
You can cause a tab to receive the input focus when clicked by specifying the TCS_FOCUSONBUTTONDOWN style. This style is typically used only with the TCS_BUTTONS style. You can specify that a tab never receives the input focus by using the TCS_FOCUSNEVER style.
By default, a tab control displays only one row of tabs. If not all tabs can be shown at once, the tab control displays an up-down control so that the user can scroll additional tabs into view. For more information about up-down controls, see Up-Down Controls_win32_Up_Down_Controls. You can cause a tab control to display multiple rows of tabs, if necessary, by specifying the TCS_MULTILINE style. In this way, all tabs can be displayed at once. The tabs are left-aligned within each row unless you specify the TCS_RIGHTJUSTIFY style. In this case, the width of each tab is increased so that each row of tabs fills the entire width of the tab control.
A tab control automatically sizes each tab to fit its icon, if any, and its label. To give all tabs the same width, you can specify the TCS_FIXEDWIDTH style. The control sizes all the tabs to fit the widest label, or you can assign a specific width and height by using the TCM_SETITEMSIZE_win32_TCM_SETITEMSIZE message. Within each tab, the control centers the icon and label with the icon to the left of the label. You can force the icon to the left, leaving the label centered, by specifying the TCS_FORCEICONLEFT style. You can left-align both the icon and label by using the TCS_FORCELABELLEFT style. You cannot use the TCS_FIXEDWIDTH style with the TCS_RIGHTJUSTIFY style.
You can specify that the parent window draws the tabs in the control by using the TCS_OWNERDRAWFIXED style. For more information about owner-drawn tabs, see Owner-Drawn Tabs_win32_Owner_Drawn_Tabs.
You can specify that a tab control create a tool tip control by using the TCS_TOOLTIPS style. For more information about using tool tip controls with a tab control, see Tooltip Controls_win32_Tooltip_Controls_local.
+$#A Tabs and Tab Attributes
Each tab in a tab control consists of an icon, a label, and application-defined data. This information is specified by a TC_ITEM_win32_TC_ITEM_str structure. You can add tabs to a tab control, get the number of tabs, retrieve and set the contents of a tab, and delete tabs. Tabs are identified by their zero-based index.
To add tabs to a tab control, use the TCM_INSERTITEM_win32_TCM_INSERTITEM message, specifying the position of the item and the address of a TC_ITEM structure. You can retrieve and set the contents of an existing tab by using the TCM_GETITEM_win32_TCM_GETITEM and TCM_SETITEM_win32_TCM_SETITEM messages. For each tab, you can specify an icon, a label, or both. You can also specify application-defined data to associate with the tab.
You can retrieve the current number of tabs by using the TCM_GETITEMCOUNT_win32_TCM_GETITEMCOUNT message, delete a tab by using the TCM_DELETEITEM_win32_TCM_DELETEITEM message, and delete all tabs in a tab control by using the TCM_DELETEALLITEMS_win32_TCM_DELETEALLITEMS message.
You can associate application-defined data with each tab. For example, you might save information about each page with its corresponding tab. By default, a tab control allocates four extra bytes per tab for application-defined data. You can change the number of extra bytes per tab by using the TCM_SETITEMEXTRA_win32_TCM_SETITEMEXTRA message. You can only use this message when the tab control is empty.
The application-defined data is specified by the lParam member of the TC_ITEM_win32_TC_ITEM_str structure. If you use more than four bytes of application-defined data, you need to define your own structure and use it instead of TC_ITEM. You can retrieve and set application-defined data the same way you retrieve and set other information about a tab ¾ by using the TCM_GETITEM_win32_TCM_GETITEM and TCM_SETITEM_win32_TCM_SETITEM messages.
The first member of your structure must be a TC_ITEMHEADER_win32_TC_ITEMHEADER_str structure, and the remaining members must specify application-defined data. TC_ITEMHEADER is identical to TC_ITEM, except it does not have the lParam member. The difference between the size of your structure and the size of TC_ITEMHEADER should equal the number of extra bytes per tab.
+$#A Display Area
The display area of a tab control is the area in which an application displays the current page. Typically, an application creates a child window or dialog box, setting the window size and position to fit the display area. Given the window rectangle for a tab control, you can calculate the bounding rectangle of the display area by using the TCM_ADJUSTRECT_win32_TCM_ADJUSTRECT message.
Sometimes the display area must be a particular size ¾ for example, the size of a modeless child dialog box. Given the bounding rectangle for the display area, you can use TCM_ADJUSTRECT to calculate the corresponding window rectangle for the tab control.
+$#A Tab Selection
When the user selects a tab, a tab control sends its parent window notification messages in the form of WM_NOTIFY_win32_WM_NOTIFY messages. The TCN_SELCHANGING_win32_TCN_SELCHANGING notification message is sent before the selection changes, and the TCN_SELCHANGE_win32_TCN_SELCHANGE notification message is sent after the selection changes.
You can process TCN_SELCHANGING to save the state of the outgoing page. You can return TRUE to prevent the selection from changing. For example, you might not want to switch away from a child dialog box in which a control has an invalid setting.
You must process TCN_SELCHANGE_win32_TCN_SELCHANGE to display the incoming page in the display area. This might simply entail changing the information displayed in a child window. More often, each page consists of a child window or dialog box. In this case, an application might process this notification by destroying or hiding the outgoing child window or dialog box and by creating or showing the incoming child window or dialog box.
You can retrieve and set the current selection by using the TCM_GETCURSEL_win32_TCM_GETCURSEL and TCM_SETCURSEL_win32_TCM_SETCURSEL messages.
+$#A Tab Control Image Lists
Each tab can have an icon associated with it, which is specified by an index into the image list for the tab control. When a tab control is created, it has no image list associated with it. An application can create an image list by using the ImageList_Create_win32_ImageList_Create function and then assign it to a tab control by using the TCM_SETIMAGELIST_win32_TCM_SETIMAGELIST message.
You can add images to a tab control's image list just as you would to any other image list. However, an application should remove images by using the TCM_REMOVEIMAGE_win32_TCM_REMOVEIMAGE message instead of the ImageList_Remove_win32_ImageList_Remove function. This message ensures that each tab remains associated with the same image it had been.
Destroying a tab control does not destroy an image list that is associated with it. You must destroy the image list separately. This is useful if you want to assign the same image list to multiple tab controls.
To retrieve the handle of the image list currently associated with a tab control, you can use the TCM_GETIMAGELIST_win32_TCM_GETIMAGELIST message.
+$#A Tab Size and Position
Each tab in a tab control has a size and position. You can set the size of tabs, retrieve the bounding rectangle of a tab, or determine which tab is at a specified position.
For fixed-width and owner-drawn tab controls, you can set the exact width and height of tabs by using the TCM_SETITEMSIZE_win32_TCM_SETITEMSIZE message. In other tab controls, each tab's size is calculated based on the icon and label for the tab. The tab control includes space for a border and an additional margin. You can set the thickness of the margin by using the TCM_SETPADDING_win32_TCM_SETPADDING message.
You can determine the current bounding rectangle for a tab by using the TCM_GETITEMRECT_win32_TCM_GETITEMRECT message. You can determine which tab, if any, is at a specified location by using the TCM_HITTEST_win32_TCM_HITTEST message.
In a tab control with the TCS_MULTILINE style, you can determine the current number of rows of tabs by using the TCM_GETROWCOUNT_win32_TCM_GETROWCOUNT message.
+$#A Owner-Drawn Tabs
If a tab control has the TCS_OWNERDRAWFIXED style, the parent window must paint tabs by processing the WM_DRAWITEM_win32_WM_DRAWITEM message. The tab control sends this message whenever a tab needs to be painted. The lParam parameter specifies the address of a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure, which contains the index of the tab, its bounding rectangle, and the device context (DC) in which to draw.
By default, the itemData member of DRAWITEMSTRUCT contains the value of the lParam member of the TC_ITEM_win32_TC_ITEM_str structure. However, if you change the amount of application-defined data per tab, itemData contains a pointer to the data instead. You can change the amount of application-defined data per tab by using the TCM_SETITEMEXTRA_win32_TCM_SETITEMEXTRA message.
To specify the size of items in a tab control, the parent window must process the WM_MEASUREITEM_win32_WM_MEASUREITEM message. Because all tabs in an owner-drawn tab control are the same size, this message is sent only once. There is no tab control style for owner-drawn tabs of varying size. You can also set the width and height of tabs by using the TCM_SETITEMSIZE_win32_TCM_SETITEMSIZE message.
+$#A Tooltip Controls
You can use a tooltip control to provide a brief description of each tab in a tab control. A tab control that has the TCS_TOOLTIPS style creates a tooltip control when it is created, and when the tab control is destroyed, it destroys the tooltip control. You can also create a tooltip control and assign it to a tab control.
If you use a tooltip control with a tab control, the parent window must process the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message to provide a description of each tab on request.
To use the same tooltip control with more than one tab control, create the tooltip control yourself and assign it to the tab control by using the TCM_SETTOOLTIPS_win32_TCM_SETTOOLTIPS message. You can get the handle of a tab control's current tooltip control by using the TCM_GETTOOLTIPS_win32_TCM_GETTOOLTIPS message. If you use this method, you should not use the TCS_TOOLTIPS style. For more information about tooltip controls, see Tooltip Controls_win32_Tooltip_Controls.
+$#A Default Tab Control Message Processing
This section describes the message processing performed by a tab control. Messages specific to tab controls are discussed elsewhere and are, therefore, not included here.
Message Processing performed
WM_CAPTURECHANGED_win32_WM_CAPTURECHANGED Does nothing if the tab control released the mouse capture itself. If another window captured the mouse and a button is held down, the command releases the button.
WM_CREATE_win32_WM_CREATE Allocates and initializes an internal data structure. The control creates a tool tip control if the TCS_TOOLTIPS style is specified.
WM_DESTROY_win32_WM_DESTROY Frees resources allocated during WM_CREATE_win32_WM_CREATE processing.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the font used for labels.
WM_KEYDOWN_win32_WM_KEYDOWN Processes DIRECTION keys and changes the selection, if appropriate.
WM_KILLFOCUS_win32_WM_KILLFOCUS Invalidates the tab that has the focus, so it will be repainted to reflect an unfocused state.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Forwards the message to the tool tip control, if any, and changes the selection if the user is clicking a tab. If the user is clicking a button, the control redraws the button to give a sunken appearance and captures the mouse. If the user is clicking either a tab or button and the TCS_FOCUSONBUTTONDOWN style is specified, the control sets the focus to itself.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse if a button was pressed. If the cursor is over the button and is being held down, the control changes the selection accordingly and redraws the button.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Forwards the message to the tool tip control, if any. If the TCS_BUTTONS style is specified and the mouse button is being held down after clicking, the control may also redraw the affected button to give it a raised or sunken appearance.
WM_NOTIFY_win32_WM_NOTIFY Forwards notification messages sent by the tool tip control.
WM_PAINT_win32_WM_PAINT Draws a border around the display area (unless the TCS_BUTTONS style is specified) and paints any tabs that intersect the invalid rectangle. For each tab, it draws the body of each tab (or sends a WM_DRAWITEM_win32_WM_DRAWITEM message to the parent window) and then draws a border around the tab.
WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN Sends an NM_RCLICK_win32_NM_RCLICK notification message to the parent window.
WM_SETFOCUS_win32_WM_SETFOCUS Invalidates the tab that has the focus so that it will be repainted to reflect a focused state.
WM_SETFONT_win32_WM_SETFONT Sets the font used for labels.
WM_SETREDRAW_win32_WM_SETREDRAW Sets the state of an internal flag that determines whether the control is repainted when items are inserted and deleted, when the font is changed, and so on.
WM_SIZE_win32_WM_SIZE Recalculates the positions of tabs and may invalidate part of the tab control to force repainting of some or all tabs.

+$#A Using Tab Controls
This section provides two examples that use tab controls. The first example uses a tab control to switch between multiple pages of text in an application's main window. The second example uses a tab control to switch between multiple pages of controls in a dialog box.
· Creating a tab control_win32_Creating_a_Tab_Control
· Creating a tabbed dialog box_win32_Creating_a_Tabbed_Dialog_Box

+$#A Creating a Tab Control
The example in this section creates a tab control and displays it in the client area of the application's main window. The application displays a third window (a static control) in the display area of the tab control. The parent window positions and sizes the tab control and static control when it processes the WM_SIZE_win32_WM_SIZE message.
There are seven tabs, one for each day of the week. When the user selects a tab, the application displays the name of the corresponding day in the static control. The following global variables are used in this example.
// Global variables

HINSTANCE g_hinst; // handle of application instance
char g_achTemp[256]; // temporary buffer for strings
HWND g_hwndMain; // main application window
HWND g_hwndTab; // tab control
HWND g_hwndDisplay; // handle of static control in
// tab control's display area

The following function creates the tab control and adds a tab for each day of the week. The names of the days are defined as string resources, consecutively numbered starting with IDS_FIRSTDAY (defined in the application's header file). Both the parent window and the tab control must have the WS_CLIPSIBLINGS window style. The application's initialization function calls this function after creating the main window.
// DoCreateTabControl - creates a tab control, sized to fit the
// specified parent window's client area, and adds some tabs.
// Returns the handle of the tab control.
// hwndParent - parent window (the application's main window)

HWND WINAPI DoCreateTabControl(HWND hwndParent)
{
RECT rcClient;
HWND hwndTab;
TC_ITEM tie;
int i;

// Get the dimensions of the parent window's client area, and
// create a tab control child window of that size.
GetClientRect(hwndParent, &rcClient);
InitCommonControls();
hwndTab = CreateWindow(
WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hwndParent, NULL, g_hinst, NULL
);
if (hwndTab == NULL)
return NULL;

// Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = g_achTemp;

for (i = 0; i < 7; i++) {
LoadString(g_hinst, IDS_FIRSTDAY + i,
g_achTemp, sizeof(g_achTemp));
if (TabCtrl_InsertItem(hwndTab, i, &tie) == -1) {
DestroyWindow(hwndTab);
return NULL;
}
}
return hwndTab;
}

The following function creates the static control that occupies the tab control's display area. The application's initialization function calls this function after creating the main window and the tab control.
// DoCreateDisplayWindow - creates a child window (a static
// control) to occupy the tab control's display area.
// Returns the handle of the static control.
// hwndParent - parent window (the application's main window)

HWND WINAPI DoCreateDisplayWindow(HWND hwndParent)
{
HWND hwndStatic = CreateWindow("STATIC", "",
WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
hwndParent, NULL, g_hinst, NULL);

return hwndStatic;
}

Following are the relevant portions of the application's window procedure. The application processes the WM_SIZE_win32_WM_SIZE message to position and size the tab control and the static control. To determine the appropriate position and size for the static control, this example sends the tab control a TCM_ADJUSTRECT_win32_TCM_ADJUSTRECT message (by using the TabCtrl_AdjustRect macro).
When a tab is selected, the tab control sends a WM_NOTIFY_win32_WM_NOTIFY message, specifying the TCN_SELCHANGE_win32_TCN_SELCHANGE notification message. The application processes this notification message by setting the text of the static control.
// MainWindowProc - processes the message for the main window class.
// The return value depends on the message.
// hwnd - handle of the window
// uMsg - identifier for the message
// wParam - message-specific parameter
// lParam - message-specific parameter

LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg) {
case WM_SIZE: {
HDWP hdwp;
RECT rc;

// Calculate the display rectangle, assuming the
// tab control is the size of the client area.
SetRect(&rc, 0, 0,
LOWORD(lParam), HIWORD(lParam));
TabCtrl_AdjustRect(g_hwndTab, FALSE, &rc);

// Size the tab control to fit the client area.
hdwp = BeginDeferWindowPos(2);
DeferWindowPos(hdwp, g_hwndTab, NULL, 0, 0,
LOWORD(lParam), HIWORD(lParam),
SWP_NOMOVE | SWP_NOZORDER
);

// Position and size the static control to fit the
// tab control's display area, and make sure the
// static control is in front of the tab control.
DeferWindowPos(hdwp,
g_hwndDisplay, HWND_TOP, rc.left, rc.top,
rc.right - rc.left, rc.bottom - rc.top, 0
);
EndDeferWindowPos(hdwp);
}
break;

case WM_NOTIFY:
switch (HIWORD(wParam)) {
case 0:
.
. // menu command processing
.

case TCN_SELCHANGE: {
int iPage = TabCtrl_GetCurSel(g_hwndTab);
LoadString(g_hinst, IDS_FIRSTDAY + iPage,
g_achTemp, sizeof(g_achTemp));
SendMessage(g_hwndDisplay, WM_SETTEXT, 0
(LPARAM) g_achTemp);
}
break;
}
break;

.
. // additional message processing
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

+$#A Creating a Tabbed Dialog Box
The example in this section creates a dialog box that uses tabs to provide multiple pages of controls. The main dialog box is a modal dialog box. Each page of controls is defined by a dialog box template that specifies the WS_CHILD style. When a tab is selected, the example creates a modeless dialog box for the incoming page and destroys the dialog box for the outgoing page.

Note In many cases, you can implement multiple-page dialog boxes more easily by using property sheets. For more information about property sheets, see Property Sheets_win32_Property_Sheets.

The template for the main dialog box simply defines two button controls. When processing the WM_INITDIALOG_win32_WM_INITDIALOG message, the dialog box procedure creates a tab control and loads the dialog template resources for each of the child dialog boxes.
The information is saved in an application-defined structure called DLGHDR. A pointer to this structure is associated with the dialog box window by using the SetWindowLong_win32_SetWindowLong function. The structure is defined in the application's header file, as follows.
#define C_PAGES 3

typedef struct tag_dlghdr {
HWND hwndTab; // tab control
HWND hwndDisplay; // current child dialog box
RECT rcDisplay; // display rectangle for the tab control
DLGTEMPLATE *apRes[C_PAGES];
} DLGHDR;

The following function processes the WM_INITDIALOG_win32_WM_INITDIALOG message for the main dialog box. The function allocates the DLGHDR structure, loads the dialog template resources for the child dialog boxes, and creates the tab control.
The size of each child dialog box is specified by the DLGTEMPLATE_win32_DLGTEMPLATE_str structure. The function examines the size of each dialog box and uses the macro for the TCM_ADJUSTRECT_win32_TCM_ADJUSTRECT message to calculate an appropriate size for the tab control. Then it sizes the dialog box and positions the two buttons accordingly. This example sends TCM_ADJUSTRECT by using the TabCtrl_AdjustRect macro.
VOID WINAPI OnTabbedDialogInit(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));
DWORD dwDlgBase = GetDialogBaseUnits();
int cxMargin = LOWORD(dwDlgBase) / 4;
int cyMargin = HIWORD(dwDlgBase) / 8;
TC_ITEM tie;
RECT rcTab;
HWND hwndButton;
RECT rcButton;
int i;

// Save a pointer to the DLGHDR structure.
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr);

// Create the tab control.
pHdr->hwndTab = CreateWindow(
WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 100, 100,
hwndDlg, NULL, g_hinst, NULL
);

// Add a tab for each of the three child dialog boxes.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = "First";
TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);
tie.pszText = "Second";
TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);
tie.pszText = "Third";
TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);

// Lock the resources for the three child dialog boxes.
pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(DLG_FIRST));
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(DLG_SECOND));
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(DLG_THIRD));

// Determine the bounding rectangle for all child dialog boxes.
SetRectEmpty(&rcTab);
for (i = 0; i < C_PAGES; i++) {
if (pHdr->apRes[i]->cx > rcTab.right)
rcTab.right = pHdr->apRes[i]->cx;
if (pHdr->apRes[i]->cy > rcTab.bottom)
rcTab.bottom = pHdr->apRes[i]->cy;
}
rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;
rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;

// Calculate how large to make the tab control, so
// the display area can accomodate all the child dialog boxes.
TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
OffsetRect(&rcTab, cxMargin - rcTab.left,
cyMargin - rcTab.top);

// Calculate the display rectangle.
CopyRect(&pHdr->rcDisplay, &rcTab);
TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);

// Set the size and position of the tab control, buttons,
// and dialog box.
SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top,
rcTab.right - rcTab.left, rcTab.bottom - rcTab.top,
SWP_NOZORDER);

// Move the first button below the tab control.
hwndButton = GetDlgItem(hwndDlg, BTN_CLOSE);
SetWindowPos(hwndButton, NULL,
rcTab.left, rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);

// Determine the size of the button.
GetWindowRect(hwndButton, &rcButton);
rcButton.right -= rcButton.left;
rcButton.bottom -= rcButton.top;

// Move the second button to the right of the first.
hwndButton = GetDlgItem(hwndDlg, BTN_TEST);
SetWindowPos(hwndButton, NULL,
rcTab.left + rcButton.right + cxMargin,
rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);

// Size the dialog box.
SetWindowPos(hwndDlg, NULL, 0, 0,
rcTab.right + cyMargin +
2 * GetSystemMetrics(SM_CXDLGFRAME),
rcTab.bottom + rcButton.bottom + 2 * cyMargin +
2 * GetSystemMetrics(SM_CYDLGFRAME) +
GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);

// Simulate selection of the first item.
OnSelChanged(hwndDlg);
}

// DoLockDlgRes - loads and locks a dialog template resource.
// Returns a pointer to the locked resource.
// lpszResName - name of the resource

DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName)
{
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
HGLOBAL hglb = LoadResource(g_hinst, hrsrc);
return (DLGTEMPLATE *) LockResource(hglb);
}

The following function processes the TCN_SELCHANGE_win32_TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect_win32_CreateDialogIndirect function to create a modeless dialog box for the incoming page.
// OnSelChanged - processes the TCN_SELCHANGE notification.
// hwndDlg - handle of the parent dialog box

VOID WINAPI OnSelChanged(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndDlg, GWL_USERDATA);
int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);

// Destroy the current child dialog box, if any.
if (pHdr->hwndDisplay != NULL)
DestroyWindow(pHdr->hwndDisplay);

// Create the new child dialog box.
pHdr->hwndDisplay = CreateDialogIndirect(g_hinst,
pHdr->apRes[iSel], hwndDlg, ChildDialogProc);
}

The following function processes the WM_INITDIALOG_win32_WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect_win32_CreateDialogIndirect function. This function uses the SetWindowPos_win32_SetWindowPos function to position the child dialog within the tab control's display area.
// OnChildDialogInit - Positions the child dialog box to fall
// within the display area of the tab control.

VOID WINAPI OnChildDialogInit(HWND hwndDlg)
{
HWND hwndParent = GetParent(hwndDlg);
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndParent, GWL_USERDATA);
SetWindowPos(hwndDlg, HWND_TOP,
pHdr->rcDisplay.left, pHdr->rcDisplay.top,
0, 0, SWP_NOSIZE);
}

+$#A Tab Control Reference
The messages, notification messages, and structures are associated with tab controls. These elements can be grouped as follows.
Tabs and Tab Attributes
TC_ITEM_win32_TC_ITEM_strTC_ITEMHEADER_win32_TC_ITEMHEADER_strTCM_DELETEALLITEMS_win32_TCM_DELETEALLITEMSTCM_DELETEITEM_win32_TCM_DELETEITEMTCM_GETITEM_win32_TCM_GETITEMTCM_INSERTITEM_win32_TCM_INSERTITEMTCM_SETITEM_win32_TCM_SETITEMTCM_SETITEMEXTRA_win32_TCM_SETITEMEXTRA
Tab Selection
TCM_GETCURFOCUS_win32_TCM_GETCURFOCUSTCM_GETCURSEL_win32_TCM_GETCURSELTCM_SETCURSEL_win32_TCM_SETCURSELTCN_SELCHANGE_win32_TCN_SELCHANGETCN_SELCHANGING_win32_TCN_SELCHANGING
Tab Size and Position
TC_HITTESTINFO_win32_TC_HITTESTINFO_strTCM_ADJUSTRECT_win32_TCM_ADJUSTRECTTCM_GETITEMRECT_win32_TCM_GETITEMRECTTCM_GETROWCOUNT_win32_TCM_GETROWCOUNTTCM_HITTEST_win32_TCM_HITTESTTCM_SETITEMSIZE_win32_TCM_SETITEMSIZETCM_SETPADDING_win32_TCM_SETPADDING
Miscellaneous
TC_KEYDOWN_win32_TC_KEYDOWN_strTCM_GETIMAGELIST_win32_TCM_GETIMAGELISTTCM_GETITEMCOUNT_win32_TCM_GETITEMCOUNTTCM_GETTOOLTIPS_win32_TCM_GETTOOLTIPSTCM_REMOVEIMAGE_win32_TCM_REMOVEIMAGETCM_SETIMAGELIST_win32_TCM_SETIMAGELISTTCM_SETTOOLTIPS_win32_TCM_SETTOOLTIPSTCN_KEYDOWN_win32_TCN_KEYDOWN
+$#A Tab Control Messages
An application sends messages to add tabs and otherwise control the appearance and behavior of a tab control. Each message has a corresponding macro, which you can use instead of sending the message explicitly.
TCM_ADJUSTRECT_win32_TCM_ADJUSTRECTTCM_DELETEALLITEMS_win32_TCM_DELETEALLITEMSTCM_DELETEITEM_win32_TCM_DELETEITEMTCM_GETCURFOCUS_win32_TCM_GETCURFOCUSTCM_GETCURSEL_win32_TCM_GETCURSELTCM_GETIMAGELIST_win32_TCM_GETIMAGELISTTCM_GETITEM_win32_TCM_GETITEMTCM_GETITEMCOUNT_win32_TCM_GETITEMCOUNTTCM_GETITEMRECT_win32_TCM_GETITEMRECTTCM_GETROWCOUNT_win32_TCM_GETROWCOUNTTCM_GETTOOLTIPS_win32_TCM_GETTOOLTIPSTCM_HITTEST_win32_TCM_HITTESTTCM_INSERTITEM_win32_TCM_INSERTITEMTCM_REMOVEIMAGE_win32_TCM_REMOVEIMAGETCM_SETCURSEL_win32_TCM_SETCURSELTCM_SETIMAGELIST_win32_TCM_SETIMAGELISTTCM_SETITEM_win32_TCM_SETITEMTCM_SETITEMEXTRA_win32_TCM_SETITEMEXTRATCM_SETITEMSIZE_win32_TCM_SETITEMSIZETCM_SETPADDING_win32_TCM_SETPADDINGTCM_SETTOOLTIPS_win32_TCM_SETTOOLTIPS
+$#A Tab Control Notification Messages
The following notification messages are used with tab controls.
TCN_KEYDOWN_win32_TCN_KEYDOWNTCN_SELCHANGE_win32_TCN_SELCHANGETCN_SELCHANGING_win32_TCN_SELCHANGING
+$#A Tab Control Structures
The following structures are used with tab controls.
TC_HITTESTINFO_win32_TC_HITTESTINFO_strTC_ITEM_win32_TC_ITEM_strTC_ITEMHEADER_win32_TC_ITEMHEADER_strTC_KEYDOWN_win32_TC_KEYDOWN_str
+$#A Tab Control Styles
The following window styles are specific to tab controls.
TCS_BUTTONS Specifies that tabs appear as buttons and no border is drawn around the display area.
TCS_FIXEDWIDTH Specifies that all tabs are the same width. This style cannot be combined with the TCS_RIGHTJUSTIFY style.
TCS_FOCUSNEVER Specifies that the tab control never receives the input focus.
TCS_FOCUSONBUTTONDOWN Specifies that tabs receive the input focus when clicked.
TCS_FORCEICONLEFT Aligns icons with the left edge of each fixed-width tab. This style can only be used with the TCS_FIXEDWIDTH style.
TCS_FORCELABELLEFT Aligns labels with the left edge of each fixed-width tab; that is, it displays the label immediately to the right of the icon instead of centering it. This style can only be used with the TCS_FIXEDWIDTH style, and it implies the TCS_FORCEICONLEFT style.
TCS_MULTILINE Displays multiple rows of tabs, if necessary, so all tabs are visible at once.
TCS_OWNERDRAWFIXED Specifies that the parent window is responsible for drawing tabs.
TCS_RAGGEDRIGHT Does not stretch each row of tabs to fill the entire width of the control. This style is the default.
TCS_RIGHTJUSTIFY Increases the width of each tab, if necessary, so that each row of tabs fills the entire width of the tab control. This window style is ignored unless the TCS_MULTILINE style is also specified.
TCS_SINGLELINE Displays only one row of tabs. The user can scroll to see more tabs, if necessary. This style is the default.
TCS_TABS Specifies that tabs appear as tabs and that a border is drawn around the display area. This style is the default.
TCS_TOOLTIPS Specifies that the tab control has a tooltip control associated with it. For more information about tooltip controls, see Tooltip Controls_win32_Tooltip_Controls.

+$#AK Toolbars
A toolbar is a control window that contains one or more buttons. Each button sends a command message to the parent window when the user chooses it.
+$#A About Toolbars
Typically, the buttons in a toolbar correspond to items in the application's menu, providing an additional and more direct way for the user to access an application's commands. The following illustration shows a window that has a toolbar positioned below the menu bar.
{ewc msdncd, EWGraphic, group10606 0 /a "SDK_01.bmp"}
A toolbar has built-in customization features, including a system-defined customization dialog box, that allow the user to insert, delete, or rearrange toolbar buttons. An application determines whether the customization features are available to the user and controls the extent to which the user may customize the toolbar.
+$#A Toolbar Creation
You can use the CreateToolbarEx_win32_CreateToolbarEx function to create a toolbar and add an initial set of buttons to it. You can also use the CreateWindowEx_win32_CreateWindowEx function, specifying the TOOLBARCLASSNAME window class, but this second method creates a toolbar that initially contains no buttons. You add buttons to the toolbar by using the TB_ADDBUTTONS_win32_TB_ADDBUTTONS or TB_INSERTBUTTON_win32_TB_INSERTBUTTON message.
The TOOLBARCLASSNAME window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls_win32_InitCommonControls function first.
A toolbar must be created as a child window with the WS_CHILD style. If you use CreateWindowEx_win32_CreateWindowEx to create a toolbar, you must specify the WS_CHILD window style. CreateToolbarEx_win32_CreateToolbarEx includes the WS_CHILD style by default. You must specify the initial parent window when creating the toolbar, but you can change the parent window after creation by using the TB_SETPARENT_win32_TB_SETPARENT message.
+$#A Toolbar Size and Position
The window procedure for a toolbar automatically sets the size and position of the toolbar window. The height is based on the height of the buttons in the toolbar. The width is the same as the width of the parent window's client area. The CCS_TOP and CCS_BOTTOM common control styles determine whether the toolbar is positioned along the top or bottom of the client area. By default, a toolbar has the CCS_TOP style.
The toolbar window procedure automatically adjusts the size of the toolbar whenever it receives a WM_SIZE_win32_WM_SIZE or TB_AUTOSIZE_win32_TB_AUTOSIZE message. An application should send either of these messages whenever the size of the parent window changes or after sending a message that requires the size of the toolbar to be adjusted ¾ for example, the TB_SETBUTTONSIZE_win32_TB_SETBUTTONSIZE message.
+$#A Tooltip Style
There is only one class-specific style associated with toolbars: TBSTYLE_TOOLTIPS. When you specify this style, the toolbar creates and manages a tooltip control. A tooltip control is a small pop-up window that contains a line of text describing a toolbar button. The tooltip control is hidden and appears only when the user puts the cursor on a toolbar button, leaving it there for approximately one second. The tooltip control is displayed near the cursor.
When the toolbar receives a WM_MOUSEMOVE_win32_WM_MOUSEMOVE message, it sends a notification message to the tooltip control. The tooltip control sets a timer, and after approximately one second, checks if the cursor is still at the same location and is on a toolbar button. If it is, the tooltip control sends the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message to the parent window to retrieve the descriptive text for the button. Then the tooltip control creates a pop-up window and displays the text in the window. The tooltip control destroys the pop-up window when the user clicks a mouse button or moves the cursor out of the toolbar.
An application that needs to send messages directly to the tooltip control can retrieve the handle of the tooltip by using the TB_GETTOOLTIPS_win32_TB_GETTOOLTIPS message. An application can replace the tooltip control of a toolbar by using the TB_SETTOOLTIPS_win32_TB_SETTOOLTIPS message. For more information about tooltip controls, see Tooltip Controls_win32_Tooltip_Controls.
+$#A Toolbar Bitmaps
Each button in a toolbar can include a bitmapped image. A toolbar stores the information that it needs to draw the images in an internal list. When you call the CreateToolbarEx_win32_CreateToolbarEx function, you specify a monochrome or color bitmap that contains the initial images, and the toolbar adds the information to the internal list of images. You can add additional images later by using the TB_ADDBITMAP_win32_TB_ADDBITMAP message.
Each image has a zero-based index. The first image added to the internal list has an index of zero, the second image has an index of one, and so on. TB_ADDBITMAP adds images to the end of the list and returns the index of the first new image that it added. You use an image's index to associate the image with a button.
Windows assumes that all of a toolbar's bitmapped images are the same size. You specify the size when you create the toolbar by using CreateToolbarEx_win32_CreateToolbarEx. If you use the CreateWindowEx_win32_CreateWindowEx function to create a toolbar, the size of the images is set to the default dimensions of 16 by 15 pixels. You can use the TB_SETBITMAPSIZE_win32_TB_SETBITMAPSIZE message to change the dimensions of the bitmapped images, but you must do so before adding any images to the internal list of images.
+$#A Toolbar Strings
Each button can display a string in addition to, or instead of, an image. A toolbar maintains an internal list that contains all of the strings available to toolbar buttons. You add strings to the internal list by using the TB_ADDSTRING_win32_TB_ADDSTRING message, specifying the address of the buffer containing the strings to add. Each string must be null-terminated, and the last string must be terminated with two null characters.
Each string has a zero-based index. The first string added to the internal list of strings has an index of zero, the second string has an index of one, and so on. TB_ADDSTRING adds strings to the end of the list and returns the index of the first new string. You use a string's index to associate the string with a button.
+$#A Toolbar Buttons
You add buttons to a toolbar by filling an array of TBBUTTON_win32_TBBUTTON_str structures and specifying the address of the array either in the TB_ADDBUTTONS_win32_TB_ADDBUTTONS message or in a call to the CreateToolbarEx_win32_CreateToolbarEx function. You can also use the TB_INSERTBUTTON_win32_TB_INSERTBUTTON message, which passes the address of a TBBUTTON structure to the toolbar. Each TBBUTTON structure defines the attributes of a button, including the indexes of its string and bitmap as well as its style, state, command identifier, and application-defined 32-bit value.
If you use the CreateWindowEx_win32_CreateWindowEx function to create a toolbar, you must send the TB_BUTTONSTRUCTSIZE_win32_TB_BUTTONSTRUCTSIZE message before adding any buttons. The message passes the size of the TBBUTTON structure to the toolbar.
+$#A Toolbar Button Styles
A button's style determines how the button appears and how it responds to user input. The TBSTYLE_BUTTON style creates a toolbar button that behaves like a standard push button. A button that has the TBSTYLE_CHECK style is similar to a standard push button, except it toggles between the pressed and nonpressed states each time the user clicks it.
You can create groups of toolbar buttons by using the TBSTYLE_GROUP or TBSTYLE_CHECKGROUP style, causing a button to stay pressed until the user chooses another button in the group. The TBSTYLE_SEP style creates a small gap between buttons. A button with this style does not receive user input.
+$#A Toolbar Button States
Each button in a toolbar has a current state. The toolbar updates a button's state to reflect user actions, such as clicking the button. The state indicates whether the button is currently pressed or not pressed, enabled or disabled, hidden or visible, and so on. Although an application sets a button's initial state when adding the button to the toolbar, it can change and retrieve the state by sending messages to the toolbar. For a list of toolbar button states, see Toolbar States_win32_Toolbar_States.
An application can use the TB_GETSTATE_win32_TB_GETSTATE and TB_SETSTATE_win32_TB_SETSTATE messages to retrieve and set the state of a button. In addition, a toolbar includes the following messages that retrieve or set a particular state.
TB_CHECKBUTTON_win32_TB_CHECKBUTTON TB_ISBUTTONCHECKED_win32_TB_ISBUTTONCHECKED
TB_ENABLEBUTTON_win32_TB_ENABLEBUTTON TB_ISBUTTONENABLED_win32_TB_ISBUTTONENABLED
TB_HIDEBUTTON_win32_TB_HIDEBUTTON TB_ISBUTTONHIDDEN_win32_TB_ISBUTTONHIDDEN
TB_INDETERMINATE_win32_TB_INDETERMINATE TB_ISBUTTONINDETERMINATE_win32_TB_ISBUTTONINDETERMINATE
TB_PRESSBUTTON_win32_TB_PRESSBUTTON TB_ISBUTTONPRESSED_win32_TB_ISBUTTONPRESSED

+$#A Command Identifier
Each button has a command identifier associated with it. When the user selects a button, the toolbar sends the parent window a WM_COMMAND_win32_WM_COMMAND message that includes the command identifier of the button. The parent window examines the command identifier and carries out the command associated with the button.
+$#A Button Size and Position
A toolbar keeps track of its buttons by assigning each button a position index. The index is zero based; that is, the leftmost button has an index of zero, the next button to the right has an index of one, and so on. An application must specify the index of a button when sending messages to retrieve information about the button or set the button's attributes.
A toolbar updates the position indexes as buttons are inserted and removed. An application can retrieve the current position index of a button by using the TB_COMMANDTOINDEX_win32_TB_COMMANDTOINDEX message. The message specifies the command identifier of a button, and the toolbar window uses the identifier to locate the button and return its position index.
All buttons in a toolbar are the same size. The CreateToolbarEx_win32_CreateToolbarEx function requires you to set the initial size of the buttons when you create the toolbar. When you use the CreateWindowEx_win32_CreateWindowEx function to create a toolbar, the initial size is set to the default dimensions of 24 by 22 pixels. You can use the TB_SETBUTTONSIZE_win32_TB_SETBUTTONSIZE message to change the button size, but you must do so before adding any buttons to the toolbar. The TB_GETITEMRECT_win32_TB_GETITEMRECT message retrieves the current dimensions of the buttons.
A toolbar automatically sets the width of its buttons when you add a string to the toolbar that is longer than any current toolbar string. The width is set to accommodate the longest string in the toolbar.
+$#A Customization
A toolbar has built in customization features that you can make available to the user by giving the toolbar the CCS_ADJUSTABLE common control style. The customization features allow the user to drag a button to a new position or to remove a button by dragging it off the toolbar. In addition, the user can double-click the toolbar to display the Customize Toolbar dialog box, which allows the user to add, delete, and rearrange toolbar buttons. An application can display the dialog box by using the TB_CUSTOMIZE_win32_TB_CUSTOMIZE message.
The toolbar sends notification messages (in the form of WM_NOTIFY_win32_WM_NOTIFY messages) to the parent window at each step in the customization process. If the user holds the SHIFT key down and begins dragging a button, the toolbar automatically handles the drag operation. The toolbar sends the TBN_QUERYDELETE_win32_TBN_QUERYDELETE notification message to the parent window to determine whether the button may be deleted. The drag operation ends if the parent window returns FALSE. Otherwise, the toolbar captures mouse input and waits for the user to release the mouse button.
When the user releases the mouse button, the toolbar determines the location of the mouse cursor. If the cursor is outside the toolbar, the button is deleted. If the cursor is on another toolbar button, the toolbar sends the TBN_QUERYINSERT_win32_TBN_QUERYINSERT notification message to the parent window to determine if a button may be inserted to the left of the given button. The button is inserted if the parent window returns TRUE; otherwise, it is not. The toolbar sends the TBN_TOOLBARCHANGE_win32_TBN_TOOLBARCHANGE notification message to signal the end of the drag operation.
If the user begins a drag operation without holding down the SHIFT key, the toolbar sends the TBN_BEGINDRAG_win32_TBN_BEGINDRAG notification message to the parent window. An application that implements its own button-dragging code can use this message as a signal to begin a drag operation. The toolbar sends the TBN_ENDDRAG_win32_TBN_ENDDRAG notification message to signal the end of the drag operation.
A toolbar sends notification messages when the user customizes a toolbar by using the Customize Toolbar dialog box. The toolbar sends the TBN_BEGINADJUST_win32_TBN_BEGINADJUST notification message after the user double-clicks the toolbar, but before the dialog box is created. Next, the toolbar begins sending a series of TBN_QUERYINSERT_win32_TBN_QUERYINSERT notification messages to determine whether the toolbar allows buttons to be inserted. When the parent window returns TRUE, the toolbar stops sending TBN_QUERYINSERT notifications. If the parent window does not return TRUE for any button, the toolbar destroys the dialog box.
Next, the toolbar determines if any buttons may be deleted by sending one TBN_QUERYDELETE_win32_TBN_QUERYDELETE notification message for each button in the toolbar. The parent window returns TRUE to indicate that a button may be deleted; otherwise, it returns FALSE. The toolbar adds all toolbar buttons to the dialog box, but it grays those that may not be deleted.
Whenever the toolbar needs information about a button in the Customize Toolbar dialog box, it sends the TBN_GETBUTTONINFO_win32_TBN_GETBUTTONINFO notification message, specifying the index of the button for which it needs information and the address of a TBNOTIFY_win32_TBNOTIFY_str structure. The parent window must fill the structure with the relevant information.
The Customize Toolbar dialog box includes a Help button and a Reset button. When the user chooses the Help button, the toolbar sends the TBN_CUSTHELP_win32_TBN_CUSTHELP notification message. The parent window should respond by displaying help information. The dialog box sends the TBN_RESET_win32_TBN_RESET notification message when the user selects the Reset button. This message signals that the toolbar is about to reinitialize the dialog box.
An application can use the TB_SAVERESTORE_win32_TB_SAVERESTORE message to save the current state of a toolbar in an initialization file or to restore the state based on information previously stored in an initialization file. The message saves the state information in the specified initialization file or in the WIN.INI file if no initialization file is specified. An application typically stores the state before the user begins customizing the toolbar in case the user later wants to restore the toolbar to its original state.
+$#A Default Message Handling
This section describes the window message processing performed by a toolbar. Because messages specific to toolbars are discussed elsewhere, they are not included here.
Message Default processing
WM_CHARTOITEM_win32_WM_CHARTOITEM Forwards this message to the parent window.
WM_COMMAND_win32_WM_COMMAND Forwards this message to the parent window.
WM_CREATE_win32_WM_CREATE Allocates internal memory and graphics objects. It also initializes internal variables and data structures.
WM_DESTROY_win32_WM_DESTROY Frees all memory and graphics objects previously allocated for the toolbar.
WM_DRAWITEM_win32_WM_DRAWITEM Forwards this message to the parent window.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Forwards this message to the DefWindowProc_win32_DefWindowProc function and returns the result.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Forwards this message to the tooltip control. If a button was clicked, it sets the state of the button and redraws it. It begins a drag operation if the toolbar has the CCS_ADJUSTABLE style and the SHIFT key is down (or the ALT key, if the toolbar has the TBSTYLE_ALTDRAG style).
WM_LBUTTONUP_win32_WM_LBUTTONUP Forwards this message to the tooltip control. If a button was clicked, it sets the state of the button and redraws it. It also ends the drag operation, if one is underway, and redraws the toolbar.
WM_MEASUREITEM_win32_WM_MEASUREITEM Forwards this message to the parent window.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Forwards this message to the tooltip control. If a button has the mouse capture and the cursor moves out of the button rectangle, it redraws the button. It checks whether the toolbar has lost the mouse capture and, if it has, ends the drag operation.
WM_NCACTIVATE_win32_WM_NCACTIVATE, WM_NCPAINT_win32_WM_NCPAINT Redraws the borders of the toolbar if the toolbar does not have the CCS_NODIVIDER style. Otherwise, it forwards the message to the DefWindowProc_win32_DefWindowProc function.
WM_NOTIFY_win32_WM_NOTIFY Forwards the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message from the tooltip control to the parent of the toolbar.
WM_PAINT_win32_WM_PAINT Draws the toolbar.
WM_SIZE_win32_WM_SIZE Recalculates the size and position of the toolbar and redraws it.
WM_SYSCOLORCHANGE_win32_WM_SYSCOLORCHANGE Updates the colors of the buttons and toolbar to march the system colors.
WM_VKEYTOITEM_win32_WM_VKEYTOITEM Forwards this message to the parent window.
WM_WININICHANGE_win32_WM_WININICHANGE Reinitializes internal variables that depend on system metric values.

+$#A Using Toolbars
· Creating a toolbar_win32_Creating_a_Toolbar
· Processing tooltip notification messages_win32_Processing_Tooltip_Notification_Messages

+$#A Creating a Toolbar
The following example uses the CreateWindowEx_win32_CreateWindowEx function to create a toolbar that the user can customize and that has a tooltip control associated with it. The example uses the TB_ADDBITMAP_win32_TB_ADDBITMAP and TB_ADDSTRING_win32_TB_ADDSTRING messages to add button images and buttons strings to the toolbar. The example also adds three buttons by using the TB_ADDBUTTONS_win32_TB_ADDBUTTONS message.
// CreateAToolBar - creates a toolbar and adds the initial set of
// buttons to it.
// Returns the handle of the toolbar if successful or NULL otherwise.
// hwndParent - handle of the parent window
HWND CreateAToolBar(HWND hwndParent)
{
HWND hwndTB;
TBADDBITMAP tbab;
TBBUTTON tbb[3];
char szBuf[16];
int iCut, iCopy, iPaste;

// Ensure that the common control DLL is loaded.
InitCommonControls();

// Create a toolbar that the user can customize and that has a
// tooltip associated with it.
hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
WS_CHILD | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE,
0, 0, 0, 0, hwndParent, (HMENU) ID_TOOLBAR, g_hinst, NULL);

// Send the TB_BUTTONSTRUCTSIZE message, which is required for
// backward compatibility.
SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE,
(WPARAM) sizeof(TBBUTTON), 0);

// Add the bitmap containing button images to the toolbar.
tbab.hInst = g_hinst;
tbab.nID = IDB_BUTTONS;
SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) NUM_BUTTON_BITMAPS,
(WPARAM) &tbab);

// Add the button strings to the toolbar.
LoadString(g_hinst, IDS_CUT, (LPSTR) &szBuf, MAX_LEN);
iCut = SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM) (LPSTR) szBuf);

LoadString(g_hinst, IDS_COPY, (LPSTR) &szBuf, MAX_LEN);
iCopy = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,
(LPARAM) (LPSTR) szBuf);

LoadString(g_hinst, IDS_PASTE, (LPSTR) &szBuf, MAX_LEN);
iPaste = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,
(LPARAM) (LPSTR) szBuf);

// Fill the TBBUTTON array with button information, and add the
// buttons to the toolbar.
tbb[0].iBitmap = BMP_CUT;
tbb[0].idCommand = IDM_CUT;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = TBSTYLE_BUTTON;
tbb[0].dwData = 0;
tbb[0].iString = iCut;

tbb[1].iBitmap = BMP_COPY;
tbb[1].idCommand = IDM_COPY;
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = TBSTYLE_BUTTON;
tbb[1].dwData = 0;
tbb[1].iString = iCopy;

tbb[2].iBitmap = BMP_PASTE;
tbb[2].idCommand = IDM_PASTE;
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = TBSTYLE_BUTTON;
tbb[2].dwData = 0;
tbb[2].iString = iPaste;

SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM) NUM_BUTTONS,
(LPARAM) (LPTBBUTTON) &tbb);

ShowWindow(hwndTB, SW_SHOW);
return hwndTB;
}

+$#A Processing Tooltip Notification Messages
A toolbar that has the TBSTYLE_TOOLTIPS style creates a tooltip control, which an application can use to display help text for toolbar buttons. The parent window receives the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message when the toolbar needs the help text for a button. The tooltip sends the notification in the form of a WM_NOTIFY_win32_WM_NOTIFY message. The lParam parameter includes the address of a TOOLTIPTEXT_win32_TOOLTIPTEXT_str structure that specifies the command identifier of the button for which help text is needed. An application can copy the help text to the structure, specify the address of a string containing the help text, or specify the instance handle and resource identifier of a string resource.
The following example demonstrates how to process the TTN_NEEDTEXT notification.
case WM_NOTIFY:
switch (((LPNMHDR) lParam)->code) {
case TTN_NEEDTEXT:
{
LPTOOLTIPTEXT lpttt;

lpttt = (LPTOOLTIPTEXT) lParam;
lpttt->hinst = g_hinst;

// Specify the resource identifier of the descriptive
// text for the given button.
idButton = lpttt->hdr.idFrom;
switch (idButton) {
case IDM_CUT:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_CUT);
break;
case IDM_COPY:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_COPY);
break;
case IDM_PASTE:
lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_PASTE);
break;
}
break;
}
.
. // Process other notifications here.
.
default:
break;
}

+$#A Toolbar Reference
The functions, messages, notification messages, and structures are associated with toolbars. These elements can be grouped as follows.
Toolbar Creation
CreateToolbarEx_win32_CreateToolbarExTB_BUTTONSTRUCTSIZE_win32_TB_BUTTONSTRUCTSIZETB_SETPARENT_win32_TB_SETPARENT
Toolbar Size and Position
TB_AUTOSIZE_win32_TB_AUTOSIZETB_SETBUTTONSIZE_win32_TB_SETBUTTONSIZE
Tooltip Style
TB_GETTOOLTIPS_win32_TB_GETTOOLTIPSTB_SETTOOLTIPS_win32_TB_SETTOOLTIPSTTN_NEEDTEXT_win32_TTN_NEEDTEXT
Toolbar Bitmaps
COLORMAP_win32_COLORMAP_strCreateMappedBitmap_win32_CreateMappedBitmapTB_ADDBITMAP_win32_TB_ADDBITMAPTB_CHANGEBITMAP_win32_TB_CHANGEBITMAPTB_GETBITMAP_win32_TB_GETBITMAPTB_GETBITMAPFLAGS_win32_TB_GETBITMAPFLAGSTB_SETBITMAPSIZE_win32_TB_SETBITMAPSIZETBADDBITMAP_win32_TBADDBITMAP_str
Toolbar Strings
TB_ADDSTRING_win32_TB_ADDSTRINGTB_GETBUTTONTEXT_win32_TB_GETBUTTONTEXT
Toolbar Buttons
TB_ADDBUTTONS_win32_TB_ADDBUTTONSTB_BUTTONCOUNT_win32_TB_BUTTONCOUNTTB_COMMANDTOINDEX_win32_TB_COMMANDTOINDEXTB_DELETEBUTTON_win32_TB_DELETEBUTTONTB_GETBUTTON_win32_TB_GETBUTTONTB_GETITEMRECT_win32_TB_GETITEMRECTTB_GETROWS_win32_TB_GETROWSTB_INSERTBUTTON_win32_TB_INSERTBUTTONTB_SETCMDID_win32_TB_SETCMDIDTB_SETROWS_win32_TB_SETROWSTBBUTTON_win32_TBBUTTON_strTBNOTIFY_win32_TBNOTIFY_str
Toolbar Button States
TB_CHECKBUTTON_win32_TB_CHECKBUTTONTB_ENABLEBUTTON_win32_TB_ENABLEBUTTONTB_GETSTATE_win32_TB_GETSTATETB_HIDEBUTTON_win32_TB_HIDEBUTTONTB_INDETERMINATE_win32_TB_INDETERMINATETB_ISBUTTONCHECKED_win32_TB_ISBUTTONCHECKEDTB_ISBUTTONENABLED_win32_TB_ISBUTTONENABLEDTB_ISBUTTONHIDDEN_win32_TB_ISBUTTONHIDDENTB_ISBUTTONINDETERMINATE_win32_TB_ISBUTTONINDETERMINATETB_ISBUTTONPRESSED_win32_TB_ISBUTTONPRESSEDTB_PRESSBUTTON_win32_TB_PRESSBUTTONTB_SETSTATE_win32_TB_SETSTATE
Toolbar Customization
TB_CUSTOMIZE_win32_TB_CUSTOMIZETB_SAVERESTORE_win32_TB_SAVERESTORETBN_BEGINADJUST_win32_TBN_BEGINADJUSTTBN_BEGINDRAG_win32_TBN_BEGINDRAGTBN_CUSTHELP_win32_TBN_CUSTHELPTBN_ENDADJUST_win32_TBN_ENDADJUSTTBN_ENDDRAG_win32_TBN_ENDDRAGTBN_GETBUTTONINFO_win32_TBN_GETBUTTONINFOTBN_QUERYDELETE_win32_TBN_QUERYDELETETBN_QUERYINSERT_win32_TBN_QUERYINSERTTBN_RESET_win32_TBN_RESETTBN_TOOLBARCHANGE_win32_TBN_TOOLBARCHANGETBSAVEPARAMS_win32_TBSAVEPARAMS_str

+$#A Toolbar Functions
The following functions are used with toolbars.
CreateMappedBitmap_win32_CreateMappedBitmapCreateToolbarEx_win32_CreateToolbarEx
+$#A Toolbar Messages
The following messages are used with toolbars.
TB_ADDBITMAP_win32_TB_ADDBITMAPTB_ADDBUTTONS_win32_TB_ADDBUTTONSTB_ADDSTRING_win32_TB_ADDSTRINGTB_AUTOSIZE_win32_TB_AUTOSIZETB_BUTTONCOUNT_win32_TB_BUTTONCOUNTTB_BUTTONSTRUCTSIZE_win32_TB_BUTTONSTRUCTSIZETB_CHANGEBITMAP_win32_TB_CHANGEBITMAPTB_CHECKBUTTON_win32_TB_CHECKBUTTONTB_COMMANDTOINDEX_win32_TB_COMMANDTOINDEXTB_CUSTOMIZE_win32_TB_CUSTOMIZETB_DELETEBUTTON_win32_TB_DELETEBUTTONTB_ENABLEBUTTON_win32_TB_ENABLEBUTTONTB_GETBITMAP_win32_TB_GETBITMAPTB_GETBITMAPFLAGS_win32_TB_GETBITMAPFLAGSTB_GETBUTTON_win32_TB_GETBUTTONTB_GETBUTTONTEXT_win32_TB_GETBUTTONTEXTTB_GETITEMRECT_win32_TB_GETITEMRECTTB_GETROWS_win32_TB_GETROWSTB_GETSTATE_win32_TB_GETSTATETB_GETTOOLTIPS_win32_TB_GETTOOLTIPSTB_HIDEBUTTON_win32_TB_HIDEBUTTONTB_INDETERMINATE_win32_TB_INDETERMINATETB_INSERTBUTTON_win32_TB_INSERTBUTTONTB_ISBUTTONCHECKED_win32_TB_ISBUTTONCHECKEDTB_ISBUTTONENABLED_win32_TB_ISBUTTONENABLEDTB_ISBUTTONHIDDEN_win32_TB_ISBUTTONHIDDENTB_ISBUTTONINDETERMINATE_win32_TB_ISBUTTONINDETERMINATETB_ISBUTTONPRESSED_win32_TB_ISBUTTONPRESSEDTB_PRESSBUTTON_win32_TB_PRESSBUTTONTB_SAVERESTORE_win32_TB_SAVERESTORETB_SETBITMAPSIZE_win32_TB_SETBITMAPSIZETB_SETBUTTONSIZE_win32_TB_SETBUTTONSIZETB_SETCMDID_win32_TB_SETCMDIDTB_SETPARENT_win32_TB_SETPARENTTB_SETROWS_win32_TB_SETROWSTB_SETSTATE_win32_TB_SETSTATETB_SETTOOLTIPS_win32_TB_SETTOOLTIPS
+$#A Toolbar Notification Messages
The parent window receives the following notification messages from a toolbar.
TBN_BEGINADJUST_win32_TBN_BEGINADJUSTTBN_BEGINDRAG_win32_TBN_BEGINDRAGTBN_CUSTHELP_win32_TBN_CUSTHELPTBN_ENDADJUST_win32_TBN_ENDADJUSTTBN_ENDDRAG_win32_TBN_ENDDRAGTBN_GETBUTTONINFO_win32_TBN_GETBUTTONINFOTBN_QUERYDELETE_win32_TBN_QUERYDELETETBN_QUERYINSERT_win32_TBN_QUERYINSERTTBN_RESET_win32_TBN_RESETTBN_TOOLBARCHANGE_win32_TBN_TOOLBARCHANGE
+$#A Toolbar Structures
The following structures are used with toolbars.
COLORMAP_win32_COLORMAP_strTBADDBITMAP_win32_TBADDBITMAP_strTBBUTTON_win32_TBBUTTON_strTBNOTIFY_win32_TBNOTIFY_strTBSAVEPARAMS_win32_TBSAVEPARAMS_str
+$#AK Toolbar Constants
The following style flags and state flags are used with toolbars.
+$#AK Toolbar Styles
A toolbar can have a combination of the following styles.
TBSTYLE_ALTDRAG Allows the user to change the position of a toolbar button by dragging it while holding down the ALT key. If this style is not specified, the user must hold down the SHIFT key while dragging a button. Note that the CCS_ADJUSTABLE style must be specified to enable toolbar buttons to be dragged.
TBSTYLE_TOOLTIPS Creates a tooltip control that an application can use to display descriptive text for the buttons in the toolbar.
TBSTYLE_WRAPABLE Creates a toolbar that can have multiple lines of buttons. Toolbar buttons can "wrap" to the next line when the toolbar becomes too narrow to include all buttons on the same line. Wrapping occurs on separation and non-group boundaries.

A button in a toolbar can have a combination of the following styles.
TBSTYLE_BUTTON Creates a standard push button.
TBSTYLE_CHECK Creates a button that toggles between the pressed and not pressed states each time the user clicks it. The button has a different background color when it is in the pressed state.
TBSTYLE_CHECKGROUP Creates a check button that stays pressed until another button in the group is pressed.
TBSTYLE_GROUP Creates a button that stays pressed until another button in the group is pressed.
TBSTYLE_SEP Creates a separator, providing a small gap between button groups. A button that has this style does not receive user input.

+$#A Toolbar States
A toolbar button can have a combination of the following states.
TBSTATE_CHECKED The button has the TBSTYLE_CHECKED style and is being pressed.
TBSTATE_ENABLED The button accepts user input. A button not having this state does not accept user input and is grayed.
TBSTATE_HIDDEN The button is not visible and cannot receive user input.
TBSTATE_INDETERMINATE The button is grayed.
TBSTATE_PRESSED The button is being pressed.
TBSTATE_WRAP A line break follows the button. The button must also have the TBSTATE_ENABLED state.

+$#AK Tooltip Controls
A tooltip control is a small pop-up window that displays a single line of descriptive text giving the purpose of tools in an application. A tool is either a window, such as a child window or control, or an application-defined rectangular area within a window's client area.
+$#A About Tooltip Controls
A tooltip control is hidden most of the time, appearing only when the user puts the cursor on a tool and leaves it there for approximately one-half second. The tooltip control appears near the cursor and disappears when the user clicks a mouse button or moves the cursor off of the tool. A single tooltip control can support any number of tools. The following illustration shows a tooltip control associated with a button in a toolbar control.
{ewc msdncd, EWGraphic, group10607 0 /a "SDK_01.bmp"}
+$#A Tooltip Creation
You create a tooltip control by using the CreateWindowEx_win32_CreateWindowEx function, specifying the TOOLTIPS_CLASS window class. The class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, include the InitCommonControls_win32_InitCommonControls function in your application.
The window procedure for a tooltip control automatically sets the size, position, and visibility of the tooltip control. The height of the tooltip window is based on the height of the font currently selected into the device context for the tooltip control. The width varies based on the length of the string currently in the tooltip window.
+$#A Style and Activation
A tooltip control has two class-specific styles: TTS_ALWAYSTIP and TTS_NOPREFIX.
A tooltip control with the TTS_ALWAYSTIP style appears when the cursor is on a tool, regardless of whether the tooltip control's owner window is active or inactive. Without this style, the tooltip control appears when the tool's owner window is active, but not when it is inactive.
The TTS_NOPREFIX style prevents the system from stripping the ampersand (&) character from a string. If a tooltip control does not have the TTS_NOPREFIX style, the system automatically strips ampersand characters, allowing an application to use the same string as both a menu item and as text in a tooltip control.
A tooltip control itself can be either active or inactive. When it is active, the tooltip control appears when the cursor is on a tool. When it is inactive, the tooltip control does not appear, even if the cursor is on a tool. The TTM_ACTIVATE_win32_TTM_ACTIVATE message activates and deactivates a tooltip control.
A tooltip control has the WS_POPUP and WS_EX_TOOLWINDOW window styles, regardless of whether you specify them when creating the control.
+$#A Types of Tools
A tooltip control can support any number of tools. To support a particular tool, you must register the tool with the tooltip control by sending the TTM_ADDTOOL_win32_TTM_ADDTOOL message to the tooltip control. The message includes the address of a TOOLINFO_win32_TOOLINFO_str structure, which provides information the tooltip control needs to display text for the tool. The cbSize member is required and must specify the size of the structure.
A tooltip control supports tools implemented as windows (such as child windows or control windows) and as rectangular areas within a window's client area. When you add a tool implemented as a rectangular area, the hwnd member of TOOLINFO must specify the handle of the window that contains the area, and the rect member must specify the client coordinates of the area's bounding rectangle. In addition, the uId member must specify the application-defined identifier for the tool.
When you add a tool implemented as a window, the uId member of TOOLINFO_win32_TOOLINFO_str must contain the window handle of the tool. Also, the uFlags member must specify the TTF_IDISHWND value, which tells the tooltip control to interpret the uId member as a window handle.
+$#A Tool Text
When you add a tool to a tooltip control, the lpszText member of the TOOLINFO_win32_TOOLINFO_str structure must specify the address of the string to display for the tool. You can change the text any time after adding the tool by using the TTM_UPDATETIPTEXT message.
If the high-order word of lpszText is zero, the low-order word must be the identifier of a string resource. When the tooltip control needs the text, the system loads the specified string resource from the application instance identified by the hinst member of TOOLINFO.
If you specify the LPSTR_TEXTCALLBACK value in the lpszText member, the tooltip control notifies the window specified in the hwnd member of TOOLINFO_win32_TOOLINFO_str whenever the tooltip control needs to display text for the tool. The tooltip control sends the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message to the window. The message includes the address of a TOOLTIPTEXT_win32_TOOLTIPTEXT_str structure, which contains the window handle as well as the application-defined identifier for the tool. The window examines the structure to determine the tool for which text is needed, and it fills the appropriate structure members with information that the tooltip control needs to display the string.
Many applications create toolbars containing tools that correspond to menu commands. For such tools, it is convenient for the tooltip control to display the same text as the corresponding menu item. The system automatically strips the ampersand (&) accelerator characters from all strings passed to a tooltip control, unless the control has the TTS_NOPREFIX style.
To retrieve the text for a tool, use the TTM_GETTEXT_win32_TTM_GETTEXT message.
+$#A Relaying Mouse Messages to the Tooltip
A tooltip control needs to receive mouse messages to determine when to display the tooltip window. Because Windows sends mouse messages only to the window that contains the cursor, you must use the TTM_RELAYEVENT_win32_TTM_RELAYEVENT message to relay mouse messages to the tooltip control.
If a tool is implemented as a rectangular area in an application-defined window, the window procedure receives mouse messages and can relay them to the tooltip control. However, if a tool is implemented as a system-defined window, the mouse messages are sent to the window and are not readily available to the application. You must use a message hook to access and relay the mouse messages, or you must subclass the window.
When a tooltip control receives a relayed WM_MOUSEMOVE_win32_WM_MOUSEMOVE message, it determines whether the cursor is in the bounding rectangle of a tool. If the cursor is there, the tooltip control sets a timer. At the end of the time-out duration, the tooltip control checks the position of the cursor to see whether it has moved. If the cursor has not, the tooltip control retrieves the text for the tool, copies the text into the tooltip window, and shows the window. The tooltip control continues to show the window until it receives a relayed button-up or button-down message or until a relayed WM_MOUSEMOVE message indicates that the cursor has moved outside the bounding rectangle of the tool.
A tooltip control actually has three time-out durations associated with it. The initial duration is the length of time that the cursor must remain stationary within the bounding rectangle of a tool before the tooltip window is displayed. The reshow duration is the length of the delay before subsequent tooltip windows are displayed when the cursor moves from one tool to another. The autopopup duration is the length of time that the tooltip window remains displayed before it is hidden. That is, if the cursor remains stationary within the bounding rectangle after the tooltip window is displayed, the tooltip window is automatically hidden at the end of the autopopup duration. You can adjust all of the time-out durations by using the TTM_SETDELAYTIME_win32_TTM_SETDELAYTIME message.
If an application includes a tool implemented as a rectangular area and the size or position of the control changes, it can use the TTM_NEWTOOLRECT_win32_TTM_NEWTOOLRECT message to report the change to the tooltip control. An application does not need to report size and position changes for a tool implemented as a window. Reporting that is not necessary because the tooltip control uses the window handle of a tool to determine if the cursor is on the tool, not the tool's bounding rectangle.
When it is about to be displayed, a tootip control sends the TTN_SHOW_win32_TTN_SHOW notification to the owner window. A tooltip control sends the TTN_POP_win32_TTN_POP notification when it is about to be hidden. Each notification is sent in the context of a WM_NOTIFY_win32_WM_NOTIFY message.
+$#A Tooltip Hit Testing
The TTM_HITTEST_win32_TTM_HITTEST message allows you to retrieve information that a tooltip control maintains about the tool occupying a particular point. The message includes a TTHITTESTINFO_win32_TTHITTESTINFO_str structure that contains a window handle, the coordinates of a point, and the address of a TOOLINFO_win32_TOOLINFO_str structure. The tooltip control determines whether a tool occupies the point and, if it does, fills TOOLINFO with information about the tool.
+$#A Miscellaneous Messages
The TTM_GETCURRENTTOOL_win32_TTM_GETCURRENTTOOL and TTM_GETTOOLINFO_win32_TTM_GETTOOLINFO messages fill a TOOLINFO_win32_TOOLINFO_str structure with information about a tool that has been registered with a tooltip control. The TTM_SETTOOLINFO_win32_TTM_SETTOOLINFO message allows you to change the information that a tooltip control maintains for a particular tool. The TTM_DELTOOL_win32_TTM_DELTOOL message deletes a tool from a tooltip control.
+$#A Default Tooltip Control Message Processing
This section describes the window messages handled by the window procedure for the TOOLTIPS_CLASS window class.
Message Default processing
WM_CREATE_win32_WM_CREATE Ensures that the tooltip control has the WS_EX_TOOLWINDOW and WS_POPUP window styles. It also allocates memory and initializes internal variables.
WM_DESTROY_win32_WM_DESTROY Frees resources allocated for the tooltip control.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the font currently selected into the DC for the tooltip control.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Hides the tooltip window.
WM_PAINT_win32_WM_PAINT Draws the tooltip window.
WM_SETFONT_win32_WM_SETFONT Selects a new font handle into the DC for the tooltip control.
WM_TIMER_win32_WM_TIMER Hides the tooltip window if the tool has changed position or if the cursor has moved outside the tool. Otherwise, it shows the tooltip window.
WM_WININICHANGE_win32_WM_WININICHANGE Resets internal variables that are based on system metrics.

+$#A Using Tooltip Controls
· Creating a tooltip control_win32_Creating_a_Tooltip_Control
· Using a tooltip control with a dialog box_win32_Using_a_Tooltip_Control_with_a_Dialog_Box

+$#A Creating a Tooltip Control
The following example creates a tooltip control and adds several tools to it. The example creates a grid of rectangles in the client area of a window and then uses the TTM_ADDTOOL_win32_TTM_ADDTOOL message to add each rectangle to the tooltip control. Note that the window procedure for the owner of the tooltip control must handle mouse messages and pass them on to the tooltip control by using the TTM_RELAYEVENT_win32_TTM_RELAYEVENT message.
// DoCreateTooltip - creates a tooltip control and adds some tools
// to it.
// Returns the handle of the tooltip control if successful or NULL// otherwise.
// hwndOwner - handle of the owner window
//
// Global variable
// g_hinst - handle of the application instance
extern HINSTANCE g_hinst;

HWND DoCreateTooltip(HWND hwndOwner)
{
HWND hwndTT; // handle of tooltip
int row, col; // rows and columns
TOOLINFO ti; // tool information
int id = 0; // offset to string identifiers
static char *szTips[NUM_TIPS] = // tip text
{
"Cut", "Copy", "Paste", "Undo", "Open", "Save"
};

// Ensure that the common control DLL is loaded, and create
// a tooltip control.
InitCommonControls();

hwndTT = CreateWindow(TOOLTIPS_CLASS, (LPSTR) NULL, TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, (HMENU) NULL, g_hinst, NULL);

if (hwndTT == (HWND) NULL)
return (HWND) NULL;

// Divide the client area into a grid of rectangles, and add each
// rectangle to the tooltip.
for (row = 0; row < MAX_ROWS ; row++ )
for (col = 0; col < MAX_COLS; col++) {
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = 0;
ti.hwnd = hwndOwner;
ti.hinst = g_hinst;
ti.uId = (UINT) id;
ti.lpszText = (LPSTR) szTips[id++];
ti.rect.left = col * CX_COLUMN;
ti.rect.top = row * CY_ROW;
ti.rect.right = ti.rect.left + CX_COLUMN;
ti.rect.bottom = ti.rect.top + CY_ROW;

if (!SendMessage(hwndTT, TTM_ADDTOOL, 0,
(LPARAM) (LPTOOLINFO) &ti))
return NULL;
}

return hwndTT;
}

+$#A Using a Tooltip Control with a Dialog Box
The following example includes a set of application-defined functions that implement a tooltip control for a dialog box. The DoCreateDialogTooltip function creates a tooltip control and uses the EnumChildWindows_win32_EnumChildWindows function to enumerate the controls in the dialog box. The enumeration procedure, EnumChildProc_win32_EnumChildProc, registers each control with the tooltip control. The procedure specifies the dialog box as the parent window of each tooltip control and includes the LPSTR_TEXTCALLBACK value for each tooltip control. As a result, the dialog box receives a WM_NOTIFY_win32_WM_NOTIFY message that contains the TTN_NEEDTEXT_win32_TTN_NEEDTEXT notification message whenever the tooltip control needs the text for a control. The dialog box procedure calls the OnWMNotify function to process the TTN_NEEDTEXT notifications. OnWMNotify provides the appropriate string based on the identifier of the tooltip control.
The tooltip control needs to receive mouse messages that the system sends to the control windows. To access the messages, the DoCreateDialogTooltip function installs a hook procedure of the WH_GETMESSAGE type. The hook procedure, GetMsgProc_win32_GetMsgProc, monitors the message stream for mouse messages intended for one of the control windows and relays the messages to the tooltip control.
// DoCreateDialogTooltip - creates a tooltip control for a dialog box,
// enumerates the child control windows, and installs a hook
// procedure to monitor the message stream for mouse messages posted
// to the control windows.
// Returns TRUE if successful or FALSE otherwise.
//
// Global variables
// g_hinst ¾ handle of the application instance
// g_hwndTT ¾ handle of the tooltip control
// g_hwndDlg ¾ handle of the dialog box
// g_hhk ¾ handle of the hook procedure

BOOL DoCreateDialogTooltip(void)
{

// Ensure that the common control DLL is loaded, and create
// a tooltip control.
InitCommonControls();
g_hwndTT = CreateWindowEx(0, TOOLTIPS_CLASS, (LPSTR) NULL,
TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, g_hwndDlg, (HMENU) NULL, g_hinst, NULL);

if (g_hwndTT == NULL)
return FALSE;

// Enumerate the child windows to register them with the tooltip // control.
if (!EnumChildWindows(g_hwndDlg, (WNDENUMPROC) EnumChildProc, 0))
return FALSE;

// Install a hook procedure to monitor the message stream for mouse
// messages intended for the controls in the dialog box.
g_hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc,
(HINSTANCE) NULL, GetCurrentThreadId());

if (g_hhk == (HHOOK) NULL)
return FALSE;

return TRUE;
}

// EmumChildProc - registers control windows with a tooltip control by// using the TTM_ADDTOOL message to pass the address of a
// TOOLINFO structure.
// Returns TRUE if successful or FALSE otherwise.
// hwndCtrl - handle of a control window
// lParam - application-defined value (not used)
BOOL EnumChildProc(HWND hwndCtrl, LPARAM lParam)
{
TOOLINFO ti;
char szClass[64];

// Skip static controls.
GetClassName(hwndCtrl, szClass, sizeof(szClass));
if (lstrcmp(szClass, "STATIC") {
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND;
ti.hwnd = g_hwndDlg;
ti.uId = (UINT) hwndCtrl;
ti.hinst = 0;
ti.lpszText = LPSTR_TEXTCALLBACK;
SendMessage(g_hwndTT, TTM_ADDTOOL, 0,
(LPARAM) (LPTOOLINFO) &ti);
}
return TRUE;
}

// GetMsgProc - monitors the message stream for mouse messages intended
// for a control window in the dialog box.
// Returns a message-dependent value.
// nCode - hook code
// wParam - message flag (not used)
// lParam - address of an MSG structure
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *lpmsg;

lpmsg = (MSG *) lParam;
if (nCode < 0 || !(IsChild(g_hwndDlg, lpmsg->hwnd)))
return (CallNextHookEx(g_hhk, nCode, wParam, lParam));

switch (lpmsg->message) {
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
if (g_hwndTT != NULL) {
MSG msg;

msg.lParam = lpmsg->lParam;
msg.wParam = lpmsg->wParam;
msg.message = lpmsg->message;
msg.hwnd = hwnd;
SendMessage(g_hwndTT, TTM_RELAYEVENT, 0,
(LPARAM) (LPMSG) &msg);
}
break;
default:
break;
}
return (CallNextHookEx(g_hhk, nCode, wParam, lParam));
}

// OnWMNotify - provides the tooltip control with the appropriate text
// to display for a control window. This function is called by
// the dialog box procedure in response to a WM_NOTIFY message.
// lParam - second message parameter of the WM_NOTIFY message
VOID OnWMNotify(LPARAM lParam)
{
LPTOOLTIPTEXT lpttt;
int idCtrl;

if ((((LPNMHDR) lParam)->code) == TTN_NEEDTEXT) {
idCtrl = GetDlgCtrlID((HWND) ((LPNMHDR) lParam)->idFrom);
lpttt = (LPTOOLTIPTEXT) lParam;

switch (idCtrl) {
case ID_HORZSCROLL:
lpttt->lpszText = "A horizontal scroll bar.";
return;

case ID_CHECK:
lpttt->lpszText = "A check box.";
return;

case ID_EDIT:
lpttt->lpszText = "An edit control.";
return;
}
}
return;
}

+$#A Tooltip Control Reference
The following messages and structures are associated with tooltip controls. These elements can be grouped as follows.
Tooltip Activation
TTM_ACTIVATE_win32_TTM_ACTIVATE
Adding and Deleting Tools
TOOLINFO_win32_TOOLINFO_strTTM_ADDTOOL_win32_TTM_ADDTOOLTTM_DELTOOL_win32_TTM_DELTOOL
Mouse Messages
TTM_NEWTOOLRECT_win32_TTM_NEWTOOLRECTTTM_RELAYEVENT_win32_TTM_RELAYEVENTTTM_SETDELAYTIME_win32_TTM_SETDELAYTIMETTN_POP_win32_TTN_POPTTN_SHOW_win32_TTN_SHOW
Tool Text
TOOLTIPTEXT_win32_TOOLTIPTEXT_strTTM_GETTEXT_win32_TTM_GETTEXTTTM_UPDATETIPTEXT_win32_TTM_UPDATETIPTEXTTTN_NEEDTEXT_win32_TTN_NEEDTEXT
Tooltip Hit Testing
TTHITTESTINFO_win32_TTHITTESTINFO_strTTM_HITTEST_win32_TTM_HITTESTTTM_WINDOWFROMPOINT_win32_TTM_WINDOWFROMPOINT
Tool Information
TTM_ENUMTOOLS_win32_TTM_ENUMTOOLSTTM_GETCURRENTTOOL_win32_TTM_GETCURRENTTOOLTTM_GETTOOLCOUNT_win32_TTM_GETTOOLCOUNTTTM_GETTOOLINFO_win32_TTM_GETTOOLINFOTTM_SETTOOLINFO_win32_TTM_SETTOOLINFO
+$#A Tooltip Control Messages
An application sends messages to activate and deactivate a tooltip control, add and delete tools, and so on.
TTM_ACTIVATE_win32_TTM_ACTIVATETTM_ADDTOOL_win32_TTM_ADDTOOLTTM_DELTOOL_win32_TTM_DELTOOLTTM_ENUMTOOLS_win32_TTM_ENUMTOOLSTTM_GETCURRENTTOOL_win32_TTM_GETCURRENTTOOLTTM_GETTEXT_win32_TTM_GETTEXTTTM_GETTOOLCOUNT_win32_TTM_GETTOOLCOUNTTTM_GETTOOLINFO_win32_TTM_GETTOOLINFOTTM_HITTEST_win32_TTM_HITTESTTTM_NEWTOOLRECT_win32_TTM_NEWTOOLRECTTTM_RELAYEVENT_win32_TTM_RELAYEVENTTTM_SETDELAYTIME_win32_TTM_SETDELAYTIMETTM_SETTOOLINFO_win32_TTM_SETTOOLINFOTTM_UPDATETIPTEXT_win32_TTM_UPDATETIPTEXTTTM_WINDOWFROMPOINT_win32_TTM_WINDOWFROMPOINT
+$#A Tooltip Control Notification Messages
The following notification messages are used with tooltip controls.
TTN_NEEDTEXT_win32_TTN_NEEDTEXTTTN_POP_win32_TTN_POPTTN_SHOW_win32_TTN_SHOW
+$#A Tooltip Control Structures
The following structures are used with tooltip control messages.
TOOLINFO_win32_TOOLINFO_strTOOLTIPTEXT_win32_TOOLTIPTEXT_strTTHITTESTINFO_win32_TTHITTESTINFO_str

+$#AK Trackbars
A trackbar is a window that contains a slider and optional tick marks. When the user moves the slider, using either the mouse or the direction keys, the trackbar sends notification messages to indicate the change. The following illustration shows a typical trackbar.
{ewc msdncd, EWGraphic, group10608 0 /a "SDK_01.bmp"}
+$#A About Trackbars
Trackbars are useful when you want the user to select a discrete value or a set of consecutive values in a range. For example, you might use a trackbar to allow the user to set the repeat rate of the keyboard by moving the slider to a given tick mark.
The slider in a trackbar moves in increments that you specify when you create it. For example, if you specify that the trackbar should have a range of five, the slider can only occupy six positions: a position at the left side of the trackbar and one position for each increment in the range. Typically, each of these positions is identified by a tick mark.
You create a trackbar by using the CreateWindowEx_win32_CreateWindowEx function, specifying the TRACKBAR_CLASS window class. Once you have created a trackbar, you can use trackbar messages to change many of its properties. Changes that you can make include setting the minimum and maximum positions for the slider, drawing tick marks, setting a selection range, and repositioning the slider.
+$#A Trackbar Styles
Trackbars can have either a vertical or horizontal orientation. They can have tick marks on either side, both sides, or neither. They can also be used to specify a range of consecutive values. These properties are controlled by using trackbar styles, which you specify when you create the trackbar.
The TBS_HORZ and TBS_VERT styles determine the orientation of the trackbar. If you do not specify an orientation, the trackbar is oriented horizontally.
The TBS_AUTOTICKS style creates a trackbar that has a tick mark for each increment in its range of values. These tick marks are added automatically when an application sends the TBM_SETRANGE_win32_TBM_SETRANGE message. If you do not specify TBS_AUTOTICKS, you can use messages, such as TBM_SETTIC_win32_TBM_SETTIC and TBM_SETTICFREQ_win32_TBM_SETTICFREQ, to specify the positions of the tick marks. To create a trackbar that does not display tick marks, you can use the TSM_NOTICKS style.
You can display tick marks on either or both sides of the trackbar. For horizontal trackbars, you can specify the TBS_BOTTOM or TBS_TOP style. For vertical trackbars, you can specify the TBS_RIGHT or TBS_LEFT style. (TBS_BOTTOM and TBS_RIGHT are the default settings.) For tick marks on both sides of the trackbar in any orientation, specify the TBS_BOTH style.
A trackbar can display a selection range only if you specify the TBS_ENABLESELRANGE style when you create it. When a trackbar has this style, the tick marks at the starting and ending positions of a selection range are displayed as triangles (instead of vertical dashes) and the selection range is highlighted. For example, selection ranges might be useful in a simple scheduling application. The user could select a range of tick marks corresponding to hours in a day to identify a scheduled meeting time.
By default, the length of a trackbar's slider varies as the selection range changes. If the trackbar has the TBS_FIXEDLENGTH style, the length of the slider remains the same even if the selection range changes. A trackbar that has the TBS_NOTHUMB style does not include a slider.
+$#A Messages to Trackbars
An application can send messages to the trackbar to retrieve information about the window and to change its characteristics.
To retrieve the position of the slider (that is, the value the user has chosen), use the TBM_GETPOS_win32_TBM_GETPOS message. To set the position of the slider, use the TBM_SETPOS_win32_TBM_SETPOS message.
The range of a trackbar is the set of contiguous values that the trackbar can represent. Most applications use the TBM_SETRANGE_win32_TBM_SETRANGE message to set the range of a trackbar when it is first created. Applications can dynamically alter the range after the trackbar has been created by using the TBM_SETRANGEMAX_win32_TBM_SETRANGEMAX and TBM_SETRANGEMIN_win32_TBM_SETRANGEMIN messages. An application that allows the range to be changed dynamically typically retrieves the final range settings when the user has finished working with the trackbar. To retrieve these settings, use the TBM_GETRANGEMAX_win32_TBM_GETRANGEMAX and TBM_GETRANGEMIN_win32_TBM_GETRANGEMIN messages.
An application can use the TBS_AUTOTICKS style to have a trackbar's tick marks displayed automatically. If an application needs to control the position or frequency of the tick marks, however, a number of messages can be used. To set the position of a tick mark, an application can use the TBM_SETTIC_win32_TBM_SETTIC message. The TBM_SETTICFREQ_win32_TBM_SETTICFREQ message allows an application to set tick marks that appear at regular intervals in the trackbar's range. For example, the application can use this message to display only ten tick marks in a range of 1 through 100. To retrieve the index in the range corresponding to a tick mark, use the TBM_GETTIC_win32_TBM_GETTIC message. The TBM_GETPTICS_win32_TBM_GETPTICS message retrieves an array of these indices. To retrieve the position of a tick mark, in client coordinates, use the TBM_GETTICPOS_win32_TBM_GETTICPOS message. An application can retrieve the number of tick marks by using the TBM_GETNUMTICS_win32_TBM_GETNUMTICS message. The TBM_CLEARTICS_win32_TBM_CLEARTICS message removes all of a trackbar's tick marks.
A trackbar's line size determines how far the slider moves when an application receives a TB_LINEDOWN or TB_LINEUP notification message. Similarly, the page size determines the response to the TB_PAGEDOWN and TB_PAGEUP notification messages. Applications can retrieve and set the line and page size values by using the TBM_GETLINESIZE_win32_TBM_GETLINESIZE, TBM_SETLINESIZE_win32_TBM_SETLINESIZE, TBM_GETPAGESIZE_win32_TBM_GETPAGESIZE, and TBM_SETPAGESIZE_win32_TBM_SETPAGESIZE messages.
An application can use messages to retrieve the dimensions of a trackbar. The TBM_GETTHUMBRECT_win32_TBM_GETTHUMBRECT message retrieves the bounding rectangle for the slider. The TBM_GETCHANNELRECT_win32_TBM_GETCHANNELRECT message retrieves the bounding rectangle for the trackbar's channel. (The channel is the area over which the slider moves and which contains the highlight when a range is selected.)
If a trackbar has the TBS_ENABLESELRANGE style, the user can select a range of contiguous values from it. A number of messages allow the selection range to be adjusted dynamically. The TBM_SETSEL_win32_TBM_SETSEL message sets the starting and ending positions of a selection. To set just the starting position or just the ending position of a selection, use the TBM_SETSELSTART_win32_TBM_SETSELSTART or TBM_SETSELEND_win32_TBM_SETSELEND message. When the user has finished setting a selection range, an application can retrieve the settings by using the TBM_GETSELSTART_win32_TBM_GETSELSTART and TBM_GETSELEND_win32_TBM_GETSELEND messages. To clear a user's selection, use the TBM_CLEARSEL_win32_TBM_CLEARSEL message.
+$#A Trackbar Notification Messages
A trackbar notifies its parent window of user actions by sending the parent WM_HSCROLL_win32_WM_HSCROLL messages. The low-order word of the wParam parameter of WM_HSCROLL contains the notification code, and the high-order word specifies the position of the slider. The lParam parameter is the handle of the trackbar.
The system sends the TB_BOTTOM, TB_LINEDOWN, TB_LINEUP, and TB_TOP notification messages only when the user interacts with a trackbar by using the keyboard. The TB_THUMBPOSITION and TB_THUMBTRACK notification messages are only sent when the user is using the mouse. The TB_ENDTRACK, TB_PAGEDOWN, and TB_PAGEUP notification messages are sent in both cases. The following table lists the trackbar notification messages and the events (virtual-key codes or mouse events) that cause the notifications to be sent.
Notification message Reason sent
TB_BOTTOM VK_END
TB_ENDTRACK WM_KEYUP_win32_WM_KEYUP (the user released a key that sent a relevant virtual-key code)
TB_LINEDOWN VK_RIGHT or VK_DOWN
TB_LINEUP VK_LEFT or VK_UP
TB_PAGEDOWN VK_NEXT (the user clicked the channel below or to the right of the slider)
TB_PAGEUP VK_PRIOR (the user clicked the channel above or to the left of the slider)
TB_THUMBPOSITION WM_LBUTTONUP_win32_WM_LBUTTONUP following a TB_THUMBTRACK notification message
TB_THUMBTRACK Slider movement (the user dragged the slider)
TB_TOP VK_HOME

+$#A Default Trackbar Message Processing
This section describes the window message processing performed by a trackbar.
Message Processing performed
WM_CAPTURECHANGED_win32_WM_CAPTURECHANGED Kills the timer if one was set during WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN processing and sends the TB_THUMBPOSITION notification message, if necessary. It always sends the TB_ENDTRACK notification message.
WM_CREATE_win32_WM_CREATE Performs additional initialization. It sets the initial tick frequency to 1, the line size to 1, and the page size to - 1.
WM_DESTROY_win32_WM_DESTROY Frees resources.
WM_ENABLE_win32_WM_ENABLE Repaints the trackbar window.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the window background, using the current background color for the trackbar.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns the DLGC_WANTARROWS value.
WM_KEYDOWN_win32_WM_KEYDOWN Processes the direction keys and sends the TB_TOP, TB_BOTTOM, TB_PAGEUP, TB_PAGEDOWN, TB_LINEUP, and TB_LINEDOWN notification messages, as appropriate.
WM_KEYUP_win32_WM_KEYUP Sends the TB_ENDTRACK notification message if the key was one of the direction keys.
WM_KILLFOCUS_win32_WM_KILLFOCUS Repaints the trackbar window.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Sets the focus and the mouse capture to the trackbar. When necessary, it sets a timer that determines how quickly the slider moves toward the mouse cursor when the user holds down the mouse button in the window.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse capture and kills the timer if one was set during WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN processing. It sends the TB_THUMBPOSITION notification message, if necessary. It always sends the TB_ENDTRACK notification message.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Moves the slider and sends the TB_THUMBTRACK notification message when tracking the mouse (see WM_TIMER_win32_WM_TIMER).
WM_PAINT_win32_WM_PAINT Paints the trackbar.
WM_SETFOCUS_win32_WM_SETFOCUS Repaints the trackbar window.
WM_SIZE_win32_WM_SIZE Sets the dimensions of the trackbar, removing the slider if there is not enough room to display it.
WM_TIMER_win32_WM_TIMER Retrieves the mouse position and updates the position of the slider. (It is received only when the user is dragging the slider.)
WM_WININICHANGE_win32_WM_WININICHANGE Initializes slider dimensions.

+$#A Using Trackbars
· Creating a trackbar_win32_Creating_a_Trackbar
· Processing trackbar notification messages_win32_Processing_Trackbar_Notification_Messages

+$#A Creating a Trackbar
The following example shows how to create a trackbar with the TBS_AUTOTICKS and TBS_ENABLESELRANGE styles. When the trackbar is created, both its range and its selection range are initialized. The page size is also set at this time.
// CreateTrackbar - creates and initializes a trackbar.
//
// Global variable
// g_hinst - instance handle
HWND WINAPI CreateTrackbar(
HWND hwndDlg, // handle of dialog box (parent window)
UINT iMin, // minimum value in trackbar range
UINT iMax, // maximum value in trackbar range
UINT iSelMin, // minimum value in trackbar selection
UINT iSelMax) // maximum value in trackbar selection
{

InitCommonControls(); // loads common control's DLL

hwndTrack = CreateWindowEx(
0, // no extended styles
TRACKBAR_CLASS, // class name
"Trackbar Control", // title (caption)
WS_CHILD | WS_VISIBLE |
TBS_AUTOTICKS | TBS_ENABLESELRANGE, // style
10, 10, // position
200, 30, // size
hwndDlg, // parent window
ID_TRACKBAR, // control identifier
g_hinst, // instance
NULL // no WM_CREATE parameter
);

SendMessage(hwndTrack, TBM_SETRANGE,
(WPARAM) TRUE, // redraw flag
(LPARAM) MAKELONG(iMin, iMax)); // min. & max. positions
SendMessage(hwndTrack, TBM_SETPAGESIZE,
0, (LPARAM) 4); // new page size

SendMessage(hwndTrack, TBM_SETSEL,
(WPARAM) FALSE, // redraw flag
(LPARAM) MAKELONG(iSelMin, iSelMax);
SendMessage(hwndTrack, TBM_SETPOS,
(WPARAM) TRUE, // redraw flag
(LPARAM) iSelMin);

SetFocus(hwndTrack);

return hwndTrack;
}
+$#A Processing Trackbar Notification Messages
The following example is a function that is called whenever a WM_HSCROLL_win32_WM_HSCROLL message is received by the dialog box containing the trackbar. The trackbar has the TBS_ENABLESELRANGE style. The position of the slider is compared against the selection range, and the slider is moved to the starting or ending position of the selection range, when necessary.
// TBNotifications - handles trackbar notifications received
// in the wParam parameter of WM_HSCROLL. This function simply
// ensures that the slider remains within the selection range.

VOID WINAPI TBNotifications(
WPARAM wParam, // wParam of WM_HSCROLL message
HWND hwndTrack, // handle of trackbar window
UINT iSelMin, // minimum value of trackbar selection
UINT iSelMax) // maximum value of trackbar selection
{
DWORD dwPos; // current position of slider

switch (LOWORD(wParam)) {
case TB_ENDTRACK:
dwPos = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
if (dwPos > iSelMax)
SendMessage(hwndTrack, TBM_SETPOS,
(WPARAM) TRUE, // redraw flag
(LPARAM) iSelMax);
else if (dwPos < iSelMin)
SendMessage(hwndTrack, TBM_SETPOS,
(WPARAM) TRUE, // redraw flag
(LPARAM) iSelMin);
break;

default:
break;

}
}
+$#A Trackbar Reference
The following messages are associated with trackbars.
Size and Position
TBM_GETCHANNELRECT_win32_TBM_GETCHANNELRECTTBM_GETTHUMBLENGTH_win32_TBM_GETTHUMBLENGTHTBM_GETTHUMBRECT_win32_TBM_GETTHUMBRECTTBM_SETTHUMBLENGTH_win32_TBM_SETTHUMBLENGTH
Slider Position
TBM_GETPOS_win32_TBM_GETPOSTBM_SETPOS_win32_TBM_SETPOS
Line and Page Size
TBM_GETLINESIZE_win32_TBM_GETLINESIZETBM_GETPAGESIZE_win32_TBM_GETPAGESIZETBM_SETLINESIZE_win32_TBM_SETLINESIZETBM_SETPAGESIZE_win32_TBM_SETPAGESIZE
Range
TBM_GETRANGEMAX_win32_TBM_GETRANGEMAXTBM_GETRANGEMIN_win32_TBM_GETRANGEMINTBM_SETRANGE_win32_TBM_SETRANGETBM_SETRANGEMAX_win32_TBM_SETRANGEMAXTBM_SETRANGEMIN_win32_TBM_SETRANGEMIN
Selection
TBM_CLEARSEL_win32_TBM_CLEARSELTBM_GETSELEND_win32_TBM_GETSELENDTBM_GETSELSTART_win32_TBM_GETSELSTARTTBM_SETSEL_win32_TBM_SETSELTBM_SETSELEND_win32_TBM_SETSELENDTBM_SETSELSTART_win32_TBM_SETSELSTART
Tick Marks
TBM_CLEARTICS_win32_TBM_CLEARTICSTBM_GETNUMTICS_win32_TBM_GETNUMTICSTBM_GETPTICS_win32_TBM_GETPTICSTBM_GETTIC_win32_TBM_GETTICTBM_GETTICPOS_win32_TBM_GETTICPOSTBM_SETTIC_win32_TBM_SETTICTBM_SETTICFREQ_win32_TBM_SETTICFREQ
+$#A Trackbar Messages
An application sends messages to the trackbar to retrieve information about the trackbar and to change its characteristics. The following are the trackbar messages.
TBM_CLEARSEL_win32_TBM_CLEARSELTBM_CLEARTICS_win32_TBM_CLEARTICSTBM_GETCHANNELRECT_win32_TBM_GETCHANNELRECTTBM_GETLINESIZE_win32_TBM_GETLINESIZETBM_GETNUMTICS_win32_TBM_GETNUMTICSTBM_GETPAGESIZE_win32_TBM_GETPAGESIZETBM_GETPOS_win32_TBM_GETPOSTBM_GETPTICS_win32_TBM_GETPTICSTBM_GETRANGEMAX_win32_TBM_GETRANGEMAXTBM_GETRANGEMIN_win32_TBM_GETRANGEMINTBM_GETSELEND_win32_TBM_GETSELENDTBM_GETSELSTART_win32_TBM_GETSELSTARTTBM_GETTHUMBLENGTH_win32_TBM_GETTHUMBLENGTHTBM_GETTHUMBRECT_win32_TBM_GETTHUMBRECTTBM_GETTIC_win32_TBM_GETTICTBM_GETTICPOS_win32_TBM_GETTICPOSTBM_SETLINESIZE_win32_TBM_SETLINESIZETBM_SETPAGESIZE_win32_TBM_SETPAGESIZETBM_SETPOS_win32_TBM_SETPOSTBM_SETRANGE_win32_TBM_SETRANGETBM_SETRANGEMAX_win32_TBM_SETRANGEMAXTBM_SETRANGEMIN_win32_TBM_SETRANGEMINTBM_SETSEL_win32_TBM_SETSELTBM_SETSELEND_win32_TBM_SETSELENDTBM_SETSELSTART_win32_TBM_SETSELSTARTTBM_SETTHUMBLENGTH_win32_TBM_SETTHUMBLENGTHTBM_SETTIC_win32_TBM_SETTICTBM_SETTICFREQ_win32_TBM_SETTICFREQ

+$#AK Tree-View Controls
A tree-view control is a window that displays a hierarchical list of items, such as the headings in a document, the entries in an index, or the files and directories on a disk. Each item consists of a label and an optional bitmapped image, and each item can have a list of subitems associated with it. By clicking an item, the user can expand and collapse the associated list of subitems. The following illustration shows a tree-view control that displays a table of contents.
{ewc msdncd, EWGraphic, group10609 0 /a "SDK_01.bmp"}
+$#A About Tree-View Controls
You create a tree-view control by using the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_TREEVIEW window class. The class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, include the InitCommonControls_win32_InitCommonControls function in your application.
After creating a tree-view control, you add, remove, arrange, or otherwise manipulate items by sending messages to it. Each message has one or more corresponding macros that you can use instead of sending the message explicitly. The macros are documented with the messages in the Reference section of this topic.
+$#A Tree-View Styles
Tree-view styles govern aspects of a tree-view control's appearance. You set the initial styles when you create the tree-view control. You can retrieve and change the styles after creating the tree-view control by using the GetWindowLong_win32_GetWindowLong and SetWindowLong_win32_SetWindowLong functions.
The TVS_HASLINES style enhances the graphic representation of a tree-view control's hierarchy by drawing lines that link child items to their corresponding parent item. This style does not link items at the root of the hierarchy. To do so, you need to combine the TVS_HASLINES and TVS_LINESATROOT styles.
The user can expand or collapse a parent item's list of child items by double-clicking the parent item. A tree-view control that has the TVS_HASBUTTONS style adds a button to the left side of each parent item. The user can click the button to expand or collapse the child items as an alternative to double-clicking the parent item. TVS_HASBUTTONS does not add buttons to items at the root of the hierarchy. To do so, you must combine TVS_HASLINES, TVS_LINESATROOT, and TVS_HASBUTTONS.
The TVS_EDITLABELS style makes it possible for the user to edit the labels of tree-view items. For more information about editing labels, see Tree-View Label Editing_win32_Tree_View_Label_Editing.
The TVS_SHOWSELALWAYS style causes a selected item to remain selected when the tree-view control loses focus.
+$#A Parent and Child Items
Any item in a tree-view control can have a list of subitems, which are called child items, associated with it. An item that has one or more child items is called a parent item. A child item is displayed below its parent item and is indented to indicate it is subordinate to the parent. An item that has no parent is at the top of the hierarchy and is called a root item.
You add an item to a tree-view control by sending the TVM_INSERTITEM_win32_TVM_INSERTITEM message to the tree-view control. The message returns a handle of the HTREEITEM type, which uniquely identifies the item. When adding an item, you must specify the handle of the new item's parent item. If you specify NULL or the TVI_ROOT value instead of an parent item handle in the TV_INSERTSTRUCT_win32_TV_INSERTSTRUCT_str structure, the item is added as a root item.
At any given time, the state of a parent item's list of child items can be either expanded or collapsed. When the state is expanded, the child items are displayed below the parent item. When it is collapsed, the child items are not displayed. The list automatically toggles between the expanded and collapsed states when the user double-clicks the parent item or, if the parent has the TVS_HASBUTTONS style, when the user clicks the button associated with the parent item. An application can expand or collapse the child items by using the TVM_EXPAND_win32_TVM_EXPAND message.
A tree-view control sends the parent window a TVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDING notification message when a parent item's list of child items is about to be expanded or collapsed. The notification gives an application the opportunity to prevent the change or to set any attributes of the parent item that depend on the state of the list of child items. After changing the state of the list, the tree-view control sends the parent window a TVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDED notification message.
When a list of child items is expanded, it is indented relative to the parent item. You can set the amount of indentation by using the TVM_SETINDENT_win32_TVM_SETINDENT message or retrieve the current amount by using the TVM_GETINDENT_win32_TVM_GETINDENT message.
A tree-view control uses memory allocated from the heap of the process that creates the tree-view control. The maximum number of items in a tree view is based on the amount of memory available in the heap. Each item takes 64 bytes.
+$#A Item Labels
You typically specify the text of an item's label when adding the item to the tree-view control. The TVM_INSERTITEM_win32_TVM_INSERTITEM message includes a TV_ITEM_win32_TV_ITEM_str structure that defines the item's properties, including a string containing the text of the label.
A tree-view control allocates memory for storing each item; the text of the item labels takes up a significant portion of this memory. If your application maintains a copy of the strings in the tree-view control, you can decrease the memory requirements of the control by specifying the LPSTR_TEXTCALLBACK value in the pszText member of TV_ITEM instead of passing actual strings to the tree view. Using LPSTR_TEXTCALLBACK causes the tree-view control to retrieve the text of an item's label from the parent window whenever the item needs to be redrawn. To retrieve the text, the tree-view control sends a TVN_GETDISPINFO_win32_TVN_GETDISPINFO notification message, which includes the address of a TV_DISPINFO_win32_TV_DISPINFO_str structure. The parent window must fill the appropriate members of the included structure.
+$#A Tree-View Label Editing
The user can directly edit the labels of items in a tree-view control that has the TVS_EDITLABELS style. The user begins editing by clicking the label of the item that has the focus. An application begins editing by using the TVM_EDITLABEL_win32_TVM_EDITLABEL message. The tree-view control notifies the parent window when editing begins and when it is canceled or completed. When editing is completed, the parent window is responsible for updating the item's label, if appropriate.
When label editing begins, a tree-view control sends its parent window a TVN_BEGINLABELEDIT_win32_TVN_BEGINLABELEDIT notification message. By processing this notification, an application can allow editing of some labels and prevent editing of others. Returning zero allows editing, and returning nonzero prevents it.
When label editing is canceled or completed, a tree-view control sends its parent window a TVN_ENDLABELEDIT_win32_TVN_ENDLABELEDIT notification message. The lParam parameter is the address of a TV_DISPINFO_win32_TV_DISPINFO_str structure. The item member is a TV_ITEM_win32_TV_ITEM_str structure that identifies the item and includes the edited text. The parent window is responsible for updating the item's label, if appropriate, perhaps after validating the edited string. The pszText member of TV_ITEM is zero if editing is canceled.
During label editing, typically in response to the TVN_BEGINLABELEDIT_win32_TVN_BEGINLABELEDIT notification message, you can get the handle of the edit control used for label editing by using the TVM_GETEDITCONTROL_win32_TVM_GETEDITCONTROL message. You can send the edit control an EM_SETLIMITTEXT_win32_EM_SETLIMITTEXT message to limit the amount of text a user can enter or subclass the edit control to intercept and discard invalid characters. Note, however, that the edit control is displayed only after TVN_BEGINLABELEDIT is sent.
+$#A Tree-View Item Position
An item's initial position is set when the item is added to the tree-view control by using the TVM_INSERTITEM_win32_TVM_INSERTITEM message. The message includes a TV_INSERTSTRUCT_win32_TV_INSERTSTRUCT_str structure that specifies the handle of the parent item and the handle of the item after which the new item is to be inserted. The second handle must identify either a child item of the given parent or one of these values: TVI_FIRST, TVI_LAST, or TVI_SORT.
When TVI_FIRST or TVI_LAST is specified, the tree-view control places the new item at the beginning or end of the given parent item's list of child items. When TVI_SORT is specified, the tree-view control inserts the new item into the list of child items in alphabetical order based on the text of the item labels.
You can put a parent item's list of child items into alphabetical order by using the TVM_SORTCHILDREN_win32_TVM_SORTCHILDREN message. The message includes a parameter that specifies whether all levels of child items descending from the given parent item are also sorted in alphabetical order.
The TVM_SORTCHILDRENCB_win32_TVM_SORTCHILDRENCB message allows you to sort child items based on criteria that you define. When you use this message, you specify an application-defined callback function that the tree-view control can call whenever the relative order of two child items needs to be decided. The callback function receives two 32-bit application-defined values for the items being compared and a third 32-bit value that you specify when sending TVM_SORTCHILDRENCB.
+$#A Tree-View Item States Overview
Each item in a tree-view control has a current state. For example, an item can be selected, disabled, expanded, and so on. For the most part, the tree-view control automatically sets an item's state to reflect user actions, such as selection of an item. However, you can also set an item's state by using the TVM_SETITEM_win32_TVM_SETITEM message and retrieve the current state of an item by using the TVM_GETITEM_win32_TVM_GETITEM message. For a complete list of item states, see Tree-View Control Constants_win32_Tree_View_Control_Constants.
An item's current state is specified by the state member of the TV_ITEM_win32_TV_ITEM_str structure. A tree-view control might change an item's state to reflect a user action, such as selecting the item or setting the focus to the item. In addition, an application might change an item's state to disable or hide the item or to specify an overlay image or state image.
When you specify or change an item's state, the stateMask member of TV_ITEM specifies which state bits to set, and the state member contains the new values for those bits. To set an item's overlay image, stateMask must include the TVIS_OVERLAYMASK value, and state must include the one-based index of the overlay image shifted left eight bits by using the INDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK macro. The index can be zero to specify no overlay image. The overlay image must have been added to the tree-view control's list of overlay images by a previous call to the ImageList_SetOverlayImage_win32_ImageList_SetOverlayImage function. The function specifies the one-based index of the image to add; this is the index used with the INDEXTOOVERLAYMASK macro. A tree-view control can have up to four overlay images.
To set an item's state image, stateMask of TV_ITEM must include the TVIS_STATEIMAGEMASK value, and state must include the one-based index of the state image shifted left twelve bits by using the INDEXTOSTATEIMAGEMASK_win32_INDEXTOSTATEIMAGEMASK macro. The index can be zero to specify no state image. For more information about overlay and state images, see Tree-View Image Lists_win32_Tree_View_Image_Lists.
+$#A Item Selection
A tree-view control notifies the parent window when the selection changes from one item to another by sending the TVN_SELCHANGING_win32_TVN_SELCHANGING and TVN_SELCHANGED_win32_TVN_SELCHANGED notification messages. Both notifications include a value that specifies whether the change is the result of a mouse click or a keystroke. The notifications also include information about the item that is gaining the selection and the item that is losing the selection. You can use this information to set item attributes that depend on the selection state of the item. Returning TRUE in response to TVN_SELCHANGING prevents the selection from changing; returning FALSE allows the change.
An application can change the selection by sending the TVM_SELECTITEM_win32_TVM_SELECTITEM message.
+$#A Item Information
Tree-view controls are used with a number of messages that retrieve information about items in the control. TVM_GETITEM_win32_TVM_GETITEM retrieves the attributes of an item, including its current state, its selected and nonselected bitmapped images, a count of its child items, a pointer to its label string, and its application-defined 32-bit value.
The TVM_GETNEXTITEM_win32_TVM_GETNEXTITEM message retrieves the tree-view item that bears the specified relationship to the current item. The message can retrieve an item's parent, the next or previous visible item, the first child item, and so on.
The TVM_GETITEMRECT_win32_TVM_GETITEMRECT message retrieves the bounding rectangle for a tree-view item. The TVM_GETCOUNT_win32_TVM_GETCOUNT and TVM_GETVISIBLECOUNT_win32_TVM_GETVISIBLECOUNT messages retrieve a count of the items in a tree-view control and a count of the items that are currently visible in the tree-view control's window, respectively. You can ensure that a particular item is visible by using the TVM_ENSUREVISIBLE_win32_TVM_ENSUREVISIBLE message.
+$#A Tree-View Image Lists
Each item in a tree-view control can have a pair of bitmapped images associated with it. The images appear on the left side of an item's label. One image is displayed when the item is selected, and the other is displayed when the item is not selected. For example, an item might display an open folder when it is selected and a closed folder when it is not selected.
To use item images, you must create an image list by using the ImageList_Create_win32_ImageList_Create function, add the desired bitmaps to the list, and associate the list with the tree-view control by using the TVM_SETIMAGELIST_win32_TVM_SETIMAGELIST message. By default, all items display the first image in the image list for both the selected and nonselected states. You can change the default behavior for a particular item by specifying the indexes of the selected and nonselected images when adding the item to the tree view using the TVM_INSERTITEM_win32_TVM_INSERTITEM message. You can change the indexes after adding an item by using the TVM_SETITEM_win32_TVM_SETITEM message.
A tree-view control's image lists can also contain overlay images, which are designed to be superimposed on item images. A nonzero value in bits 8 through 11 of a tree-view item's state specifies the one-based index of an overlay image (zero indicates no overlay image). Because a 4-bit, one-based index is used, overlay images must be among the first 15 images in the image lists. For more information about tree-view item states, see Tree-View Item States Overview_win32_Tree_View_Item_States_Overview.
If a state image list is specified, a tree-view control reserves space to the left of each item's icon for a state image. An application can use state images, such as checked and cleared check boxes, to indicate application-defined item states. A nonzero value in bits 12 through 15 specifies the one-based index of a state image (zero indicates no state image).
By specifying the I_IMAGECALLBACK value instead of the index of an image in the TV_ITEM_win32_TV_ITEM_str structure, you can put off specifying the selected or nonselected image until the item is about to be redrawn. I_IMAGECALLBACK directs the tree view to query the parent window for the index by sending the TVN_GETDISPINFO_win32_TVN_GETDISPINFO notification message.
The TVM_GETIMAGELIST_win32_TVM_GETIMAGELIST message retrieves the handle of a tree-view control's image list. This message is useful if you need to add more images to the list. For more information about image lists, see Image Lists_win32_Image_Lists.
+$#A Drag and Drop Operations
A tree-view control notifies the parent window when the user starts to drag an item. The parent window receives a TVN_BEGINDRAG_win32_TVN_BEGINDRAG notification message when the user begins dragging an item with the left mouse button and a TVN_BEGINRDRAG_win32_TVN_BEGINRDRAG notification message when the user begins dragging with the right button. You can prevent a tree-view control from sending these notifications by giving the tree-view control the TVS_DISABLEDRAGDROP style.
You obtain an image to display during a drag operation by using the TVM_CREATEDRAGIMAGE_win32_TVM_CREATEDRAGIMAGE message. The tree-view control creates a dragging bitmap based on the label of the item being dragged. Then the tree-view control creates an image list, adds the bitmap to it, and returns the handle of the image list.
You must provide the code that actually drags the item. This typically involves using the dragging capabilities of the image list functions and processing the WM_MOUSEMOVE_win32_WM_MOUSEMOVE and WM_LBUTTONUP_win32_WM_LBUTTONUP (or WM_RBUTTONUP_win32_WM_RBUTTONUP) messages sent to the parent window after the drag operation has begun. For more information about the image list functions, see Image Lists_win32_Image_Lists. For more information about dragging a tree-view item, see Dragging a Tree-View Item_win32_Dragging_a_Tree_View_Item.
If items in a tree-view control are to be the targets of a drag and drop operation, you need to know when the mouse cursor is on a target item. You can find out by using the TVM_HITTEST_win32_TVM_HITTEST message. You specify the address of a TV_HITTESTINFO_win32_TV_HITTESTINFO_str structure that contains the current coordinates of the mouse cursor. When the SendMessage_win32_SendMessage function returns, the structure contains a flag indicating the location of the mouse cursor relative to the tree-view control. If the cursor is over an item in the tree-view control, the structure contains the handle of the item as well.
You can indicate that an item is the target of a drag and drop operation by using the TVM_SETITEM_win32_TVM_SETITEM message to set the state to the TVIS_DROPHILITED value. An item that has this state is drawn in the style used to indicate a drag and drop target.
+$#A About Tree-View Control Notification Messages
A tree-view control sends the following notification messages to its parent window in the form of WM_NOTIFY_win32_WM_NOTIFY messages.
Notification Description
TVN_BEGINDRAG_win32_TVN_BEGINDRAG Signals the start of a drag and drop operation.
TVN_BEGINLABELEDIT_win32_TVN_BEGINLABELEDIT Signals the start of in-place label editing.
TVN_BEGINRDRAG_win32_TVN_BEGINRDRAG Signals the start of a drag and drop operation, using the right mouse button.
TVN_DELETEITEM_win32_TVN_DELETEITEM Signals the deletion of a specific item.
TVN_ENDLABELEDIT_win32_TVN_ENDLABELEDIT Signals the end of label editing.
TVN_GETDISPINFO_win32_TVN_GETDISPINFO Requests information that the tree-view control requires to display an item.
TVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDED Signals that a parent item's list of child items was expanded or collapsed.
TVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDING Signals that a parent item's list of child items is about to be expanded or collapsed.
TVN_KEYDOWN_win32_TVN_KEYDOWN Signals a keyboard event.
TVN_SELCHANGED_win32_TVN_SELCHANGED Signals that the selection has changed from one item to another.
TVN_SELCHANGING_win32_TVN_SELCHANGING Signals that the selection is about to be changed from one item to another.
TVN_SETDISPINFO_win32_TVN_SETDISPINFO Notifies a parent window that it must update the information it maintains for an item.

+$#A Default Tree-View Controls Message Processing
This section describes the window message processing performed by a tree-view control. Because messages specific to tree-view controls are discussed elsewhere, they are not included here.
Message Processing performed
WM_COMMAND_win32_WM_COMMAND Processes the EN_UPDATE_win32_EN_UPDATE and EN_KILLFOCUS_win32_EN_KILLFOCUS edit control notification messages and forwards all other edit control notifications to the parent window. There is no return value.
WM_CREATE_win32_WM_CREATE Allocates memory and initializes internal data structures. It returns zero if successful or - 1 otherwise.
WM_DESTROY_win32_WM_DESTROY Frees all system resources associated with the control. It returns zero.
WM_ENABLE_win32_WM_ENABLE Enables or disables the control.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the window background using the current background color for the tree-view control. It returns TRUE.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the current label font.
WM_HSCROLL_win32_WM_HSCROLL Scrolls the tree-view control. It returns TRUE if scrolling occurs or FALSE otherwise.
WM_KEYDOWN_win32_WM_KEYDOWN Sends the NM_RETURN_win32_NM_RETURN notification message when the user presses the ENTER key. It moves the caret when the user presses the direction keys or the PAGE UP, PAGE DOWN, HOME, END, or BACKSPACE key. It scrolls the tree-view control when the user presses the CTRL key in combination with those keys. It sends the TVN_KEYDOWN_win32_TVN_KEYDOWN notification message to the parent window. It returns TRUE if a key is processed or FALSE otherwise.
WM_KILLFOCUS_win32_WM_KILLFOCUS Repaints the focused item, if any, and sends an NM_KILLFOCUS_win32_NM_KILLFOCUS notification message to the parent window.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Cancels label editing and, if an item was double-clicked, sends the NM_DBLCLK_win32_NM_DBLCLK or NM_RDBLCLK_win32_NM_RDBLCLK notification message to the parent window. If the parent window returns TRUE, the tree-view control toggles the expanded state of the item, sending the parent window the TVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDING and TVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDED notification messages. There is no return value.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Toggles the expanded state if the user clicked the button associated with a parent item. If the user clicked an item label, the tree-view control selects and sets the focus to the item. If the user moves the mouse before releasing the mouse button, the tree-view control begins a drag and drop operation. There is no return value.
WM_PAINT_win32_WM_PAINT Paints the invalid region of the tree-view control. It returns zero.
WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN Checks to see if an item was clicked and a drag operation was begun. If the operation has begun, it sends a TVN_BEGINRDRAG_win32_TVN_BEGINRDRAG notification message to the parent window and highlights the drop target. Otherwise, it sends an NM_RCLICK_win32_NM_RCLICK notification message to the parent window. There is no return value.
WM_SETFOCUS_win32_WM_SETFOCUS Repaints the focused item, if any, and sends an NM_SETFOCUS_win32_NM_SETFOCUS notification message to the parent window.
WM_SETFONT_win32_WM_SETFONT Saves the specified font handle and repaints the tree-view control using the new font.
WM_SETREDRAW_win32_WM_SETREDRAW Sets or clears the redraw flag. The tree-view control is redrawn after the redraw flag is set. It returns zero.
WM_SIZE_win32_WM_SIZE Recomputes internal variables that depend on the size of the tree-view control's client area. It returns TRUE.
WM_STYLECHANGED_win32_WM_STYLECHANGED Cancels label editing and redraws the tree-view control using the new styles. It returns zero.
WM_SYSCOLORCHANGE_win32_WM_SYSCOLORCHANGE Redraws the tree-view control using the new color if the redraw flag is set. There is no return value.
WM_TIMER_win32_WM_TIMER Begins editing an item label. If the user clicks the label of the focused item, the tree-view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the tree view to avoid entering edit mode if the user double-clicks the label. It returns zero.

WM_VSCROLL_win32_WM_VSCROLL Scrolls the tree-view control. It returns TRUE if scrolling occurs or FALSE otherwise.

+$#A Using Tree-View Controls
· Creating a tree-view control_win32_Creating_a_Tree_View_Control
· Initializing the image list_win32_Initializing_the_Image_List
· Adding tree-view items_win32_Adding_Tree_View_Items
· Dragging a tree-view item_win32_Dragging_a_Tree_View_Item

+$#A Creating a Tree-View Control
To create a tree-view control, use the CreateWindowEx_win32_CreateWindowEx function, specifying the WC_TREEVIEW value for the window class. The tree-view window class is registered in the application's address space when the common control dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls_win32_InitCommonControls function.
The following example creates a tree-view control that is sized to fit the client area of the parent window. It also uses application-defined functions to associate an image list with the control and add items to the control.
// CreateATreeView - creates a tree-view control.
// Returns the handle of the new control if successful or NULL// otherwise.
// hwndParent - handle of the control's parent window
// lpszFileName - name of the file to parse for tree-view items
HWND CreateATreeView(HWND hwndParent, LPSTR lpszFileName)
{
RECT rcClient; // dimensions of client area
HWND hwndTV; // handle of tree-view control

// Ensure that the common control DLL is loaded.
InitCommonControls();

// Get the dimensions of the parent window's client area, and create
// the tree-view control.
GetClientRect(hwndParent, &rcClient);
hwndTV = CreateWindowEx(0, WC_TREEVIEW, "Tree View",
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,
0, 0, rcClient.right, rcClient.bottom,
hwndParent, (HMENU) ID_TREEVIEW, g_hinst, NULL);

// Initialize the image list, and add items to the control.
// InitTreeViewImageLists and InitTreeViewItems are application-
// defined functions.
if (!InitTreeViewImageLists(hwndTV) ||
!InitTreeViewItems(hwndTV, lpszFileName)) {
DestroyWindow(hwndTV);
return FALSE;
}
return hwndTV;
}

+$#A Initializing the Image List
Every item in a tree-view control can have two images associated with it. An item displays one image when it is selected and the other when it is not. To include images with tree-view items, you must use the image list functions to create an image list and add images to it. Then you must associate the image list with the tree-view control by using the TVM_SETIMAGELIST_win32_TVM_SETIMAGELIST message.
The following example creates an image list, adds three bitmaps to the list, and associates the image list with a tree-view control.
// InitTreeViewImageLists - creates an image list, adds three bitmaps to // it, and associates the image list with a tree-view control.
// Returns TRUE if successful or FALSE otherwise.
// hwndTV - handle of the tree-view control
//// Global variables and constants
// g_nOpen, g_nClosed, and g_nDocument - integer variables for
// indexes of the images
// CX_BITMAP and CY_BITMAP - width and height of an icon
// NUM_BITMAPS - number of bitmaps to add to the image list
BOOL InitTreeViewImageLists(HWND hwndTV)
{
HIMAGELIST himl; // handle of image list
HBITMAP hbmp; // handle of bitmap

// Create the image list.
if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP,
FALSE, NUM_BITMAPS, 0)) == NULL)
return FALSE;

// Add the open file, closed file, and document bitmaps.
hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_OPEN_FILE));
g_nOpen = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
DeleteObject(hbmp);

hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_CLOSED_FILE));
g_nClosed = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
DeleteObject(hbmp);

hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_DOCUMENT));
g_nDocument = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
DeleteObject(hbmp);

// Fail if not all of the images were added.
if (ImageList_GetImageCount(himl) < 3)
return FALSE;

// Associate the image list with the tree-view control.
TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);

return TRUE;
}

+$#A Adding Tree-View Items
You add an item to a tree-view control by sending the TVM_INSERTITEM_win32_TVM_INSERTITEM message to the control. The message includes a pointer to a TV_INSERTSTRUCT_win32_TV_INSERTSTRUCT_str structure, specifying the parent item, the item after which the new item is inserted, and a TV_ITEM_win32_TV_ITEM_str structure that defines the attributes of the item. The attributes include the item's label, its selected and nonselected images, and a 32-bit application-defined value.
The example in this section creates a table of contents based on the information in a text file. The example includes two functions. The first function searches a file for headings. When it finds one, it extracts the text of the heading and the value that indicates the level of the heading and then passes them to the second function. Headings are assumed to be in the following form, .[heading].n, where heading is the text of the heading and n indicates the heading level. The example ignores heading levels greater than level four.
The second function adds an item to a tree-view control, using the heading text as the item's label and the heading level to determine the parent item for the new item. A level one heading is added to the root of the tree-view control, a level two heading is added as a child item of the previous level one item, and so on. The function assigns an image to an item based on whether it has any child items. If an item has child items, it gets an image representing a closed folder. Otherwise, it gets an image representing a document. An item uses the same image for both the selected and nonselected states.
// InitTreeViewItems - extracts headings from the specified file and
// passes them to a function that adds them to a tree-view control.
// Returns TRUE if successful or FALSE otherwise.
// hwndTV - handle of the tree-view control
// lpszFileName - name of file with headings
BOOL InitTreeViewItems(HWND hwndTV, LPSTR lpszFileName)
{
HANDLE hf; // handle of file
DWORD cbRead; // number of bytes read
char szItemText[128]; // label text of tree-view item
int nLevel; // heading level
LPCH pch; // pointer to data read from file
LPCH pchTmp; // temporary pointer
DWORD i, j; // counters

// Open the file to parse.
if ((hf = CreateFile(lpszFileName, GENERIC_READ,
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL)) == (HANDLE) INVALID_HANDLE_VALUE)
return FALSE;

// Parse the file looking for headings.
pch = (LPCH) LocalAlloc(LPTR, sizeof(char) * 2048);
pchTmp = pch;
do {
// Read a chunk of the file.
ReadFile(hf, pchTmp, sizeof(char) * 2048, &cbRead,
(LPOVERLAPPED) NULL);
// Parse the chunk looking for ".[".
for (i = 0, j = 0; i < cbRead; i++) {
if ((i + 2) > cbRead) // break if end of chunk
break;

// Extract the heading text from between the brackets.
if ((*pchTmp == '.') && (*(pchTmp+1) == '[')) {
pchTmp = pchTmp + 2;
i += 2;
while (*pchTmp != ']' && ((i++) <= cbRead) )
szItemText[j++] = *pchTmp++;
szItemText[j] = '\0';
j = 0;
nLevel = atoi(pchTmp + 2);

// Add the item to the tree-view control.
AddItemToTree(hwndTV, (LPSTR) &szItemText, nLevel);
} else
++pchTmp;
}
pchTmp = pch;
} while (cbRead != 0);
CloseHandle((HANDLE) hf);

return TRUE;
}

// AddItemToTree - adds items to a tree-view control.
// Returns the handle of the newly added item.
// hwndTV - handle of the tree-view control
// lpszItem - text of the item to add
// nLevel - level at which to add the item
HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel)
{
TV_ITEM tvi;
TV_INSERTSTRUCT tvins;
static HTREEITEM hPrev = (HTREEITEM) TVI_FIRST;
static HTREEITEM hPrevRootItem = NULL;
static HTREEITEM hPrevLev2Item = NULL;
HTREEITEM hti;

tvi.mask = TVIF_TEXT | TVIF_IMAGE
| TVIF_SELECTEDIMAGE | TVIF_PARAM;

// Set the text of the item.
tvi.pszText = lpszItem;
tvi.cchTextMax = lstrlen(lpszItem);

// Assume the item is not a parent item, so give it a
// document image.
tvi.iImage = g_nDocument;
tvi.iSelectedImage = g_nDocument;

// Save the heading level in the item's application-defined
// data area.
tvi.lParam = (LPARAM) nLevel;

tvins.item = tvi;
tvins.hInsertAfter = hPrev;

// Set the parent item based on the specified level.
if (nLevel == 1)
tvins.hParent = TVI_ROOT;
else if (nLevel == 2)
tvins.hParent = hPrevRootItem;
else
tvins.hParent = hPrevLev2Item;

// Add the item to the tree-view control.
hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0,
(LPARAM) (LPTV_INSERTSTRUCT) &tvins);

// Save the handle of the item.
if (nLevel == 1)
hPrevRootItem = hPrev;
else if (nLevel == 2)
hPrevLev2Item = hPrev;

// The new item is a child item. Give the parent item a
// closed folder bitmap to indicate it now has child items.
if (nLevel > 1) {
hti = TreeView_GetParent(hwndTV, hPrev);
tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.hItem = hti;
tvi.iImage = g_nClosed;
tvi.iSelectedImage = g_nClosed;
TreeView_SetItem(hwndTV, &tvi);
}

return hPrev;
}

+$#A Dragging a Tree-View Item
Dragging a tree-view item typically involves processing the TVN_BEGINDRAG_win32_TVN_BEGINDRAG (or TVN_BEGINRDRAG_win32_TVN_BEGINRDRAG) notification message, the WM_MOUSEMOVE_win32_WM_MOUSEMOVE message, and the WM_LBUTTONUP_win32_WM_LBUTTONUP (or WM_RBUTTONUP_win32_WM_RBUTTONUP) message. It also involves using the image list functions to draw the item as it is being dragged. For more information about image lists, see Image Lists_win32_Image_Lists.
The remainder of this section provides an example that demonstrates how to drag a tree-view item. The example consists of three functions. The first function begins the drag operation, the second drags the image, and the third ends the drag operation.
+$#A Beginning the Tree-View Drag Operation
A tree-view control sends the parent window a TVN_BEGINDRAG_win32_TVN_BEGINDRAG (or TVN_BEGINRDRAG_win32_TVN_BEGINRDRAG) notification message whenever the user starts to drag an item. The parent window receives the notification in the form of a WM_NOTIFY_win32_WM_NOTIFY message whose lParam parameter is the address of an NM_TREEVIEW_win32_NM_TREEVIEW_str structure. The members of this structure include the screen coordinates of the mouse cursor and a TV_ITEM_win32_TV_ITEM_str structure that contains information about the item to be dragged.
The following example shows how to process the WM_NOTIFY message to obtain TVN_BEGINDRAG.
case WM_NOTIFY:
switch (((LPNMHDR) lParam)->code) {
case TVN_BEGINDRAG:
Main_OnBeginDrag(hwndTV, // application-defined function
(NM_TREEVIEW *) lParam);
break;
.
. // Handle other notifications here.
.
}
break;

Beginning the drag operation involves using the ImageList_BeginDrag_win32_ImageList_BeginDrag function. The function's parameters include the handle of the image list containing the image to use during the drag operation and the index of the image. You can either provide your own image list and image, or you can have the tree-view control create them for you by using the TVM_CREATEDRAGIMAGE message.
Because the drag image replaces the mouse cursor for the duration of the drag operation, ImageList_BeginDrag requires you to specify a hot spot within the image. The coordinates of the hot spot are relative to the upper left corner of the image. ImageList_BeginDrag also requires you to specify the initial location of the drag image. An application typically sets the initial location so that the hot spot of the drag image corresponds to that of the mouse cursor at the time the user began the drag operation.
The following function demonstrates how to begin dragging a tree-view item. It uses the drag image provided by the tree-view control and obtains the bounding rectangle of the item to determine the appropriate point for the hot spot. (The dimensions of the bounding rectangle are the same as those of the image.) Note that the bounding rectangle does not account for the indentation of child items. For this reason, the function adds the amount of indentation to the x-coordinate of the hot spot.
The function captures mouse input, causing mouse messages to be sent to the parent window. The parent window needs the subsequent WM_MOUSEMOVE_win32_WM_MOUSEMOVE messages to determine where to drag the image and the WM_LBUTTONUP_win32_WM_LBUTTONUP message to determine when to end the drag operation.
// Main_OnBeginDrag - begins dragging an item in a tree-view control.
// hwndTV - handle of the image list
// lpnmtv - address of information about the item being dragged
void Main_OnBeginDrag(HWND hwndTV, NM_TREEVIEW *lpnmtv)
{
HIMAGELIST himl; // handle of image list
RECT rcItem; // bounding rectangle of item
DWORD dwLevel; // heading level of item
DWORD dwIndent; // amount that child items are indented

// Tell the tree-view control to create an image to use
// for dragging.
himl = TreeView_CreateDragImage(hwndTV, lpnmtv->itemNew.hItem);

// Get the bounding rectangle of the item being dragged.
TreeView_GetItemRect(hwndTV, lpnmtv->itemNew.hItem, &rcItem, TRUE);

// Get the heading level and the amount that the child items are
// indented.
dwLevel = lpnmtv->itemNew.lParam;
dwIndent = (DWORD) SendMessage(hwndTV, TVM_GETINDENT, 0, 0);

// Start the drag operation.
ImageList_BeginDrag(himl, hwndTV, 0,
lpnmtv->ptDrag.x,
lpnmtv->ptDrag.y,
lpnmtv->ptDrag.x - ((dwLevel - 1) * dwIndent),
lpnmtv->ptDrag.y - rcItem.top);

// Hide the mouse cursor, and direct mouse input to the
// parent window.
ShowCursor(FALSE);
SetCapture(GetParent(hwndTV));
g_fDragging = TRUE;
return;
}

+$#A Dragging the Tree-View Item
You drag a tree-view item by calling the ImageList_DragMove_win32_ImageList_DragMove function when the parent window receives a WM_MOUSEMOVE_win32_WM_MOUSEMOVE message, as the following example shows. The example also performs hit testing during the drag operation to determine whether to highlight other items in the tree view as targets of a drag and drop operation.
// Main_OnMouseMove - drags an item in a tree-view control,
// highlighting the item that is the target.
// hwndParent - handle of the parent window
// hwndTV - handle of the tree-view control
// xCur and yCur - x- and y-coordinates of the mouse cursor
void Main_OnMouseMove(HWND hwndParent, HWND hwndTV, LONG xCur, LONG yCur)
{
HTREEITEM htiTarget; // handle of target item
TV_HITTESTINFO tvht; // hit test information

if (g_fDragging) {

// Drag the item to the current position of the mouse cursor.
ImageList_DragMove(xCur, yCur);

// Find out if the cursor is on the item. If it is, highlight
// the item as a drop target.
tvht.pt.x = xCur;
tvht.pt.y = yCur;
if ((htiTarget = TreeView_HitTest(hwndTV, &tvht)) != NULL) {
TreeView_SelectDropTarget(hwndTV, htiTarget);
}
}
return;
}

+$#A Ending the Tree-View Drag Operation
The follow example ends a drag operation. The ImageList_EndDrag_win32_ImageList_EndDrag function is called when the parent window receives a WM_LBUTTONUP_win32_WM_LBUTTONUP message.
// Main_OnLButtonUp - stops dragging a tree-view item, releases the
// mouse capture, and shows the mouse cursor.
//// Global variable
// g_fDragging - indicates whether a drag operation is underway.
void Main_OnLButtonUp(void)
{
if (g_fDragging) {
ImageList_EndDrag();
ReleaseCapture();
ShowCursor(TRUE);
g_fDragging = FALSE;
}
return;
}

+$#A Tree-View Control Reference
The following messages, macros, notification messages, and structures are associated with tree-view controls. These elements can be grouped as follows.
Item Labels
TV_ITEM_win32_TV_ITEM_strTVN_GETDISPINFO_win32_TVN_GETDISPINFO
Label Editing
TVM_EDITLABEL_win32_TVM_EDITLABELTVM_ENDEDITLABELNOW_win32_TVM_ENDEDITLABELNOWTVM_GETEDITCONTROL_win32_TVM_GETEDITCONTROLTVN_BEGINLABELEDIT_win32_TVN_BEGINLABELEDITTVN_ENDLABELEDIT_win32_TVN_ENDLABELEDIT
Item States
TVM_GETITEM_win32_TVM_GETITEMTVM_SELECTITEM_win32_TVM_SELECTITEMTVM_SETITEM_win32_TVM_SETITEMTVN_SELCHANGED_win32_TVN_SELCHANGEDTVN_SELCHANGING_win32_TVN_SELCHANGING
Tree-View Image Lists
TVM_GETIMAGELIST_win32_TVM_GETIMAGELISTTVM_SETIMAGELIST_win32_TVM_SETIMAGELIST
Item Position
TV_INSERTSTRUCT_win32_TV_INSERTSTRUCT_strTVM_INSERTITEM_win32_TVM_INSERTITEMTVM_SORTCHILDREN_win32_TVM_SORTCHILDRENTVM_SORTCHILDRENCB_win32_TVM_SORTCHILDRENCB
Parent and Child Items
TVM_EXPAND_win32_TVM_EXPANDTVM_GETINDENT_win32_TVM_GETINDENTTVM_SETINDENT_win32_TVM_SETINDENTTVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDEDTVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDING
Item Information
TV_DISPINFO_win32_TV_DISPINFO_strTVM_ENSUREVISIBLE_win32_TVM_ENSUREVISIBLETVM_GETCOUNT_win32_TVM_GETCOUNTTVM_GETNEXTITEM_win32_TVM_GETNEXTITEMTVM_GETVISIBLECOUNT_win32_TVM_GETVISIBLECOUNT
Drag and Drop Operations
TV_HITTESTINFO_win32_TV_HITTESTINFO_strTVM_CREATEDRAGIMAGE_win32_TVM_CREATEDRAGIMAGETVM_HITTEST_win32_TVM_HITTESTTVN_BEGINDRAG_win32_TVN_BEGINDRAGTVN_BEGINRDRAG_win32_TVN_BEGINRDRAG
Miscellaneous
NM_TREEVIEW_win32_NM_TREEVIEW_strTV_KEYDOWN_win32_TV_KEYDOWN_strTV_SORTCB_win32_TV_SORTCB_strTVM_DELETEITEM_win32_TVM_DELETEITEMTVM_GETISEARCHSTRING_win32_TVM_GETISEARCHSTRINGTVM_GETITEMRECT_win32_TVM_GETITEMRECTTVN_DELETEITEM_win32_TVN_DELETEITEMTVN_KEYDOWN_win32_TVN_KEYDOWNTVN_SETDISPINFO_win32_TVN_SETDISPINFO
+$#A Tree-View Control Messages
An application sends messages to add items and to alter the appearance and behavior of a tree-view control. Each message has a corresponding macro, which you can use instead of sending the message explicitly. Some messages may have related macros, which are a more specialized form of a corresponding macro.
TVM_CREATEDRAGIMAGE_win32_TVM_CREATEDRAGIMAGETVM_DELETEITEM_win32_TVM_DELETEITEMTVM_EDITLABEL_win32_TVM_EDITLABELTVM_ENDEDITLABELNOW_win32_TVM_ENDEDITLABELNOWTVM_ENSUREVISIBLE_win32_TVM_ENSUREVISIBLETVM_EXPAND_win32_TVM_EXPANDTVM_GETCOUNT_win32_TVM_GETCOUNTTVM_GETEDITCONTROL_win32_TVM_GETEDITCONTROLTVM_GETIMAGELIST_win32_TVM_GETIMAGELISTTVM_GETINDENT_win32_TVM_GETINDENTTVM_GETISEARCHSTRING_win32_TVM_GETISEARCHSTRINGTVM_GETITEM_win32_TVM_GETITEMTVM_GETITEMRECT_win32_TVM_GETITEMRECTTVM_GETNEXTITEM_win32_TVM_GETNEXTITEMTVM_GETVISIBLECOUNT_win32_TVM_GETVISIBLECOUNTTVM_HITTEST_win32_TVM_HITTESTTVM_INSERTITEM_win32_TVM_INSERTITEMTVM_SELECTITEM_win32_TVM_SELECTITEMTVM_SETIMAGELIST_win32_TVM_SETIMAGELISTTVM_SETINDENT_win32_TVM_SETINDENTTVM_SETITEM_win32_TVM_SETITEMTVM_SORTCHILDREN_win32_TVM_SORTCHILDRENTVM_SORTCHILDRENCB_win32_TVM_SORTCHILDRENCB
+$#A Tree-View Control Notification Messages
A tree-view control sends notifications messages to its parent window to notify it about events.
TVN_BEGINDRAG_win32_TVN_BEGINDRAGTVN_BEGINLABELEDIT_win32_TVN_BEGINLABELEDITTVN_BEGINRDRAG_win32_TVN_BEGINRDRAGTVN_DELETEITEM_win32_TVN_DELETEITEMTVN_ENDLABELEDIT_win32_TVN_ENDLABELEDITTVN_GETDISPINFO_win32_TVN_GETDISPINFOTVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDEDTVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDINGTVN_KEYDOWN_win32_TVN_KEYDOWNTVN_SELCHANGED_win32_TVN_SELCHANGEDTVN_SELCHANGING_win32_TVN_SELCHANGINGTVN_SETDISPINFO_win32_TVN_SETDISPINFO
+$#A Tree-View Control Structures
The following structures are used with tree-view control messages.
NM_TREEVIEW_win32_NM_TREEVIEW_strTV_DISPINFO_win32_TV_DISPINFO_strTV_HITTESTINFO_win32_TV_HITTESTINFO_strTV_INSERTSTRUCT_win32_TV_INSERTSTRUCT_strTV_ITEM_win32_TV_ITEM_strTV_KEYDOWN_win32_TV_KEYDOWN_strTV_SORTCB_win32_TV_SORTCB_str
+$#A Tree-View Control Constants
The following window style flags and item state flags are used with tree-view controls.
+$#A Tree-View Window Styles
A tree view's window style can include one or more of the following values.
TVS_DISABLEDRAGDROP Prevents the tree-view control from sending TVN_BEGINDRAG_win32_TVN_BEGINDRAG notification messages.
TVS_EDITLABELS Allows the user to edit the labels of tree-view items.
TVS_HASBUTTONS Displays plus (+) and minus (-) buttons next to parent items. The user clicks the buttons to expand or collapse a parent item's list of child items. To include buttons with items at the root of the tree view, TVS_LINESATROOT must also be specified.
TVS_HASLINES Uses lines to show the hierarchy of items.
TVS_LINESATROOT Uses lines to link items at the root of the tree-view control. This value is ignored if TVS_HASLINES is not also specified.
TVS_SHOWSELALWAYS Causes a selected item to remain selected when the tree-view control loses focus.

+$#A Tree-View Item States
An item's state determines its appearance and functionality. It can be zero, or one or more of the following values.
TVIS_BOLD The item is bold.
TVIS_CUT The item is selected as part of a cut and paste operation.
TVIS_DROPHILITED The item is selected as a drag and drop target.
TVIS_EXPANDED The item's list of child items is currently expanded; that is, the child items are visible. This value applies only to parent items.
TVIS_EXPANDEDONCE The item's list of child items has been expanded at least once. The TVN_ITEMEXPANDING_win32_TVN_ITEMEXPANDING and TVN_ITEMEXPANDED_win32_TVN_ITEMEXPANDED notification messages are not sent for parent items that have specified this value. This value applies only to parent items.
TVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle. Only one item can have the focus.
TVIS_OVERLAYMASK The item's overlay image is included when the item is drawn. The index of the overlay image must be specified in the state member of the TV_ITEM_win32_TV_ITEM_str structure by using the INDEXTOOVERLAYMASK_win32_INDEXTOOVERLAYMASK macro. The overlay image must be added to the tree view's image list by using the ImageList_SetOverlayImage_win32_ImageList_SetOverlayImage function. This value should not be combined with any other value.
TVIS_SELECTED The item is selected. The appearance of a selected item depends on whether it has the focus and on whether the system colors are used for selection.
TVIS_STATEIMAGEMASK The item's state image is included when the item is drawn. The index of the state image must be specified in the state member of the TV_ITEM_win32_TV_ITEM_str structure by using theINDEXTOSTATEIMAGEMASK_win32_INDEXTOSTATEIMAGEMASK macro. This value should not be combined with any other value.
TVIS_USERMASK Same as TVIS_STATEIMAGEMASK.

+$#AK Up-Down Controls
An up-down control is a pair of arrow buttons that the user can click to increment or decrement a value, such as a scroll position or a number displayed in a companion control. The value associated with an up-down control is called its current position. An up-down control is most often used with a companion control, which is called a buddy window.
+$#A About Up-Down Controls
To the user, an up-down control and its buddy window often look like a single control. You can specify that an up-down control automatically position itself next to its buddy window and that it automatically set the caption of the buddy window to its current position. For example, you can use an up-down control with an edit control to prompt the user for numeric input. For more information about edit controls, see Edit Controls_win32_Edit_Controls The following illustration shows an up-down control with an edit control as its buddy window, a combination that is sometimes referred to as a spinner control.
{ewc msdncd, EWGraphic, group10610 0 /a "SDK_01.bmp"}
An up-down control without a buddy window functions as a sort of simplified scroll bar. For example, a tab control sometimes displays an up-down control to enable the user to scroll additional tabs into view. The following illustration shows an up-down control in the upper right corner of a tab control.
{ewc msdncd, EWGraphic, group10610 1 /a "SDK_02.bmp"}
You can create an up-down control and specify its buddy window in several ways. The UPDOWN_CLASS value specifies an up-down control's window class. You can specify this window class in a dialog box template or in a call to the CreateWindowEx_win32_CreateWindowEx function. For more information about dialog box templates, see Dialog Boxes_win32_Dialog_Boxes. Another way is to use the CreateUpDownControl_win32_CreateUpDownControl function to create an up-down control and, at the same time, specify its buddy window, current position, and minimum and maximum positions.
The UPDOWN_CLASS window class is registered when the common controls dynamic-link library (DLL) is loaded. If you create an up-down control without using the CreateUpDownControl function, you must ensure that the DLL is loaded. You can do so by using the InitCommonControls_win32_InitCommonControls function. For more information about common controls, see Common Controls_win32_Common_Controls.
CreateUpDownControl enables you to specify a buddy window. If you create an up-down control without using this function, you can assign a buddy window by specifying the UDS_AUTOBUDDY window style or by using the UDM_SETBUDDY_win32_UDM_SETBUDDY message. If UDS_AUTOBUDDY is specified, the up-down control automatically selects the previous window in the Z order as its buddy window. This window might be the previous control in a dialog box template. You can use UDM_SETBUDDY to assign a specific buddy window to an up-down control. To determine an up-down control's current buddy window, use the UDM_GETBUDDY_win32_UDM_GETBUDDY message. An up-down control and its buddy window must have the same parent window.
An up-down control notifies its parent window when its current position changes by sending it a UDN_DELTAPOS_win32_UDN_DELTAPOS notification message and a WM_VSCROLL_win32_WM_VSCROLL message.
+$#A About Up-Down Control Styles
Using window styles, you can control characteristics of an up-down control, such as how it positions itself relative to its buddy window, whether it sets the text of its buddy window, and whether it processes the UP ARROW and DOWN ARROW keys.
An up-down control with the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style aligns with the left or right edge of its buddy window. The width of the buddy window is decreased to accommodate the width of the up-down control.
An up-down control with the UDS_SETBUDDYINT style sets the caption of its buddy window (using the WM_SETTEXT_win32_WM_SETTEXT message) whenever the current position changes. The control inserts a thousands separator between every three digits of a decimal string unless the UDS_NOTHOUSANDS style is specified. If the buddy window is a list box, an up-down control sets its current selection instead of its caption.
You can specify the UDS_ARROWKEYS style to provide a keyboard interface for an up-down control. If this style is specified, the control processes the UP ARROW and DOWN ARROW keys. The control also subclasses the buddy window so that it can process these keys when the buddy window has the focus.
If you use an up-down control for horizontal scrolling, you can specify the UDS_HORZ style. This style causes the up-down control's arrows to point left and right instead of up and down.
By default, the current position does not change if the user attempts to increment it or decrement it beyond the maximum or minimum value. You can change this behavior by using the UDS_WRAP style, so the position "wraps" to the opposite extreme. For example, incrementing past the upper limit wraps the position back to the lower limit.
+$#A Position and Acceleration
After an up-down control is created, you can change the control's current position, minimum position, and maximum position by sending messages. You can also change the radix base used to display the current position in the buddy window and the rate at which the current position changes when the up or down arrow is clicked.
To retrieve the current position of an up-down control, use the UDM_GETPOS_win32_UDM_GETPOS message. For an up-down control with a buddy window, the current position is the number in the buddy window's caption. Because the caption may have changed (for example, the user may have edited the text of an edit control), the up-down control retrieves the current caption and updates its current position accordingly.
The buddy window's caption may be either a decimal or hexadecimal string, depending on the radix base (that is, either base 10 or 16) of the up-down control. You can set the radix base by using the UDM_SETBASE_win32_UDM_SETBASE message and retrieve the radix base by using the UDM_GETBASE_win32_UDM_GETBASE message.
The UDM_SETPOS_win32_UDM_SETPOS message sets the current position of a buddy window. Note that, unlike a scroll bar, an up-down control automatically changes its current position when the up and down arrows are clicked. An application, therefore, does not need to set the current position when processing the WM_VSCROLL_win32_WM_VSCROLL message.
You can change the minimum and maximum positions of an up-down control by using the UDM_SETRANGE_win32_UDM_SETRANGE message. The maximum position may be less than the minimum, and in that case clicking the up arrow decreases the current position. To put it another way, up means moving towards the maximum position. To retrieve the minimum and maximum positions for an up-down control, use the UDM_GETRANGE_win32_UDM_GETRANGE message.
You can control the rate at which the position changes when the user holds down an arrow button by setting the up-down control's acceleration. The acceleration is defined by an array of UDACCEL_win32_UDACCEL_str structures. Each structure specifies a time interval and the number of units by which to increment or decrement at the end of that interval. To set the acceleration, use the UDM_SETACCEL_win32_UDM_SETACCEL message. To retrieve acceleration information, use the UDM_GETACCEL_win32_UDM_GETACCEL message.
+$#A Default Up-Down Controls Message Processing
This section describes the window message processing performed by an up-down control.
Message Processing performed
WM_CREATE_win32_WM_CREATE Allocates and initializes a private data structure and saves its address as window data.
WM_DESTROY_win32_WM_DESTROY Frees data allocated during WM_CREATE_win32_WM_CREATE processing.
WM_ENABLE_win32_WM_ENABLE Invalidates the window.
WM_KEYDOWN_win32_WM_KEYDOWN Changes the current position in the case of an UP ARROW or DOWN ARROW key.
WM_KEYUP_win32_WM_KEYUP Ends changing the position.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Captures the mouse. If the buddy window is an edit control or list box, it sets the focus to the buddy window. If the mouse is over the up or down button, it begins changing the position and sets a timer.
WM_LBUTTONUP_win32_WM_LBUTTONUP Ends changing the position and releases the mouse capture if the up-down control has captured the mouse. If the buddy window is an edit control, it selects all the text in the edit control.
WM_PAINT_win32_WM_PAINT Paints the up-down control.
WM_TIMER_win32_WM_TIMER Changes the current position if the mouse is being held down over a button and a sufficient interval has elapsed.

+$#A Up-Down Control Reference
The following functions, messages, notification messages, and structures are associated with up-down controls. These elements can be grouped as follows.
Up-Down Control Creation
CreateUpDownControl_win32_CreateUpDownControlUDM_GETBUDDY_win32_UDM_GETBUDDYUDM_SETBUDDY_win32_UDM_SETBUDDY
Current Position and Acceleration
UDACCEL_win32_UDACCEL_strUDM_GETACCEL_win32_UDM_GETACCELUDM_GETBASE_win32_UDM_GETBASEUDM_GETPOS_win32_UDM_GETPOSUDM_GETRANGE_win32_UDM_GETRANGEUDM_SETACCEL_win32_UDM_SETACCELUDM_SETBASE_win32_UDM_SETBASEUDM_SETPOS_win32_UDM_SETPOSUDM_SETRANGE_win32_UDM_SETRANGE
Notification Messages
UDN_DELTAPOS_win32_UDN_DELTAPOSWM_VSCROLL_win32_WM_VSCROLL
+$#A Up-Down Control Functions
The following function is used with up-down controls.
CreateUpDownControl_win32_CreateUpDownControl
+$#A Up-Down Control Messages
The following messages are used with up-down controls.
UDM_GETACCEL_win32_UDM_GETACCELUDM_GETBASE_win32_UDM_GETBASEUDM_GETBUDDY_win32_UDM_GETBUDDYUDM_GETPOS_win32_UDM_GETPOSUDM_GETRANGE_win32_UDM_GETRANGEUDM_SETACCEL_win32_UDM_SETACCELUDM_SETBASE_win32_UDM_SETBASEUDM_SETBUDDY_win32_UDM_SETBUDDYUDM_SETPOS_win32_UDM_SETPOSUDM_SETRANGE_win32_UDM_SETRANGE
+$#A Up-Down Control Notification Messages
The following notification message is used with up-down controls.
UDN_DELTAPOS_win32_UDN_DELTAPOS
+$#A Up-Down Control Structures
The following structure is used with up-down controls.
UDACCEL_win32_UDACCEL_str
+$#A Up-Down Control Styles
The following window styles are used with up-down controls.
UDS_ALIGNLEFT Positions the up-down control next to the left edge of the buddy window. The buddy window is moved to the right and its width decreased to accommodate the width of the up-down control.
UDS_ALIGNRIGHT Positions the up-down control next to the right edge of the buddy window. The width of the buddy window is decreased to accommodate the width of the up-down control.
UDS_ARROWKEYS Causes the up-down control to increment and decrement the position when the UP ARROW and DOWN ARROW keys are pressed.
UDS_AUTOBUDDY Automatically selects the previous window in the Z order as the up-down control's buddy window.
UDS_HORZ Causes the up-down control's arrows to point left and right instead of up and down.
UDS_NOTHOUSANDS Does not insert a thousands separator between every three decimal digits.
UDS_SETBUDDYINT Causes the up-down control to set the text of the buddy window (using the WM_SETTEXT_win32_WM_SETTEXT message) when the position changes. The text consists of the position formatted as a decimal or hexadecimal string.
UDS_WRAP Causes the position to "wrap" if it is incremented or decremented beyond the ending or beginning of the range.

+$#AK Buttons
Microsoft® Windows® provides dialog boxes and controls to support communication between an application and the user. A button is a control the user can turn on or off to provide input to an application.
+$#A About Buttons
There are several types of buttons and, within each type, one or more styles to distinguish among buttons of the same type. The user turns a button on or off by selecting it using the mouse or keyboard. Selecting a button typically changes its visual appearance and state (from checked to unchecked, for example). Windows, the button, and the application cooperate in changing the button's appearance and state. A button can send messages to its parent window, and a parent window can send messages to a button. Some buttons are painted by Windows, some by the application. Buttons can be used alone or in groups and can appear with or without application-defined text (a label). They belong to the BUTTON window class.
Although an application can use buttons in overlapped, pop-up, and child windows, they are designed for use in dialog boxes, where Windows standardizes their behavior. If an application uses buttons outside dialog boxes, it increases the risk that the application may behave in a nonstandard fashion. Applications typically either use buttons in dialog boxes or use window subclassing to create customized buttons.
For general information about controls, see Controls_win32_Controls. For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes. For more information about window subclassing, see Window Procedures_win32_Window_Procedures.
+$#A Button Types and Styles
Windows provides five kinds of buttons: push buttons, check boxes, radio buttons, group boxes, and owner-drawn buttons. Each type has one or more styles.
+$#A Push Buttons
A push button is a rectangle containing application-defined text (label), an icon, or a bitmap that indicates what the button does when the user selects it. A push button can be one of two styles: standard or default, as defined by the constants BS_PUSHBUTTON and BS_DEFPUSHBUTTON. A standard push button is typically used to start an operation. It receives the keyboard focus when the user selects it. A default push button, on the other hand, is typically used to indicate the most common or default choice. It is a button that the user can select by simply pressing ENTER when a dialog box has the input focus.
When the user selects a push button (of either style), it receives the keyboard focus from Windows, which sends the button's parent window a WM_COMMAND_win32_WM_COMMAND message containing the BN_CLICKED_win32_BN_CLICKED notification code. In response, the dialog box typically closes and carries out the operation indicated by the button.
+$#A Check Boxes
A check box consists of a square box and application-defined text (label), an icon, or a bitmap that indicates a choice the user can make by selecting the button. Applications typically display check boxes in a group box to permit the user to choose from a set of related, but independent options. For example, an application might present a group of check boxes from which the user can select error conditions that produce warning beeps.
A check box can be one of four styles: standard, automatic, three-state, and automatic three-state, as defined by the constants BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, and BS_AUTO3STATE, respectively. Each style can assume two check states: checked (an check mark inside the box) or unchecked (no check mark). In addition, a three-state check box can assume an indeterminate state (a grayed box inside the check box). Repeatedly selecting a standard or automatic check box toggles it from checked to unchecked and back again. Repeatedly selecting a three-state check box toggles it from checked to unchecked to indeterminate and back again.
When the user selects a check box (of any style), the check box receives the keyboard focus from Windows, which sends the check box's parent window a WM_COMMAND_win32_WM_COMMAND message containing the BN_CLICKED_win32_BN_CLICKED notification code. The parent window doesn't acknowledge this message if it comes from an automatic check box or automatic three-state check box, because Windows automatically sets the check state for those styles. But the parent window must acknowledge the message if it comes from a check box or three-state check box because the parent window, not Windows, is responsible for setting the check state for those styles. Regardless of the check box style, Windows automatically repaints the check box once its state is changed.
+$#A Radio Buttons
A radio button consists of a round button and application-defined text (a label), an icon, or a bitmap that indicates a choice the user can make by selecting the button. An application typically uses radio buttons in a group box to permit the user to choose from a set of related, but mutually exclusive options. For example, the application might present a group of radio buttons from which the user can select a format preference for text selected in the client area. The user could select a left-aligned, right-aligned, or centered format by selecting the corresponding radio button. Typically, the user can select only one option at a time from a set of radio buttons.
A radio button can be one of two styles: standard or automatic, as defined by the constants BS_RADIOBUTTON and BS_AUTORADIOBUTTON. Each style can assume two check states: checked (a dot in the button) or unchecked (no dot in the button). Repeatedly selecting a radio button (standard or automatic) toggles it from checked to unchecked and back again.
When the user selects either state, the radio button receives the keyboard focus from Windows, which sends the button's parent window a WM_COMMAND_win32_WM_COMMAND message containing the BN_CLICKED_win32_BN_CLICKED notification code. The parent window doesn't acknowledge this message if it comes from an automatic radio button because Windows automatically sets the check state for that style. But the parent window should acknowledge the message if it comes from a radio button because the parent window, not Windows, is responsible for setting the check state 1for that style. Regardless of the radio button style, Windows automatically repaints the button as its state changes.
When the user selects an automatic radio button, Windows automatically sets the check state of all other radio buttons within the same group to unchecked. The same behavior is available for standard radio buttons by using the WS_GROUP style, as discussed in Dialog Boxes_win32_Dialog_Boxes.
+$#A Owner-Drawn Buttons
Unlike radio buttons, an owner-drawn button is painted by the application, not by Windows, and has no predefined appearance or usage. Its purpose is to provide a button whose appearance and behavior are defined by the application alone. There is only one owner-drawn button style: BS_OWNERDRAW.
When the user selects an owner-drawn button, Windows sends the button's parent window a WM_COMMAND_win32_WM_COMMAND message containing the BN_CLICKED_win32_BN_CLICKED notification code, just as it does for a button that is not owner-drawn. The application must respond appropriately.
+$#A Group Boxes
A group box is a rectangle that surrounds a set of controls, such as check boxes or radio buttons, with application-defined text (label) in its upper left corner. The sole purpose of a group box is to organize controls related by a common purpose (usually indicated by the label). The group box has only one style, defined by the constant BS_GROUPBOX. Because a group box cannot be selected, it has no check state, focus state, or push state. An application cannot send messages to a group box.
+$#A Button Styles
Many developers create dialog boxes by using standalone tools, not requiring them to specify button styles. However, if an application creates a button by using the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function, the following table of constants can be used to define the button style.
Style Meaning
BS_3STATE Creates a button that is the same as a check box, except that the box can be grayed as well as checked or unchecked. Use the grayed state to show that the state of the check box is not determined.
BS_AUTO3STATE Creates a button that is the same as a three-state check box, except that the box changes its state when the user selects it. The state cycles through checked, grayed, and unchecked.
BS_AUTOCHECKBOX Creates a button that is the same as a check box, except that the check state automatically toggles between checked and unchecked each time the user selects the check box.
BS_AUTORADIOBUTTON Creates a button that is the same as a radio button, except that when the user selects it, Windows automatically sets the button's check state to checked and automatically sets the check state for all other buttons in the same group to unchecked.
BS_CHECKBOX Creates a small, empty check box with text. By default, the text is displayed to the right of the check box. To display the text to the left of the check box, combine this flag with the BS_LEFTTEXT style (or on Windows 95 only, with the equivalent BS_RIGHTBUTTON style).
BS_DEFPUSHBUTTON Creates a push button that behaves like a BS_PUSHBUTTON style button, but also has a heavy black border. If the button is in a dialog box, the user can select the button by pressing the ENTER key, even when the button does not have the input focus. This style is useful for enabling the user to quickly select the most likely (default) option.
BS_GROUPBOX Creates a rectangle in which other controls can be grouped. Any text associated with this style is displayed in the rectangle's upper left corner.
BS_LEFTTEXT Places text on the left side of the radio button or check box when combined with a radio button or check box style. Same as the Windows 95 BS_RIGHTBUTTON style.
BS_OWNERDRAW Creates an owner-drawn button. The owner window receives a WM_MEASUREITEM_win32_WM_MEASUREITEM message when the button is created and a WM_DRAWITEM_win32_WM_DRAWITEM message when a visual aspect of the button has changed. Do not combine the BS_OWNERDRAW style with any other button styles.
BS_PUSHBUTTON Creates a push button that posts a WM_COMMAND_win32_WM_COMMAND message to the owner window when the user selects the button.
BS_RADIOBUTTON Creates a small circle with text. By default, the text is displayed to the right of the circle. To display the text to the left of the circle, combine this flag with the BS_LEFTTEXT style (or on Windows 95 only, with the equivalent BS_RIGHTBUTTON style). Use radio buttons for groups of related, but mutually exclusive choices.
BS_USERBUTTON Obsolete, but provided for compatibility with 16-bit versions of Windows. Win32-based applications should use BS_OWNERDRAW instead.
BS_BITMAP Windows 95 only: Specifies that the button displays a bitmap.
BS_BOTTOM Windows 95 only: Places text at the bottom of the button rectangle.
BS_CENTER Windows 95 only: Centers text horizontally in the button rectangle.
BS_ICON Windows 95 only: Specifies that the button displays an icon.
BS_LEFT Windows 95 only: Left-justifies the text in the button rectangle. However, if the button is a check box or radio button that does not have the BS_RIGHTBUTTON style, the text is left justified on the right side of the check box or radio button.
BS_MULTILINE Windows 95 only: Wraps the button text to multiple lines if the text string is too long to fit on a single line in the button rectangle.
BS_NOTIFY Windows 95 only: Enables a button to send BN_DBLCLK, BN_KILLFOCUS, and BN_SETFOCUS notification messages to its parent window. Note that buttons send the BN_CLICKED notification message regardless of whether it has this style.
BS_PUSHLIKE Windows 95 only: Makes a button (such as a check box, three-state check box, or radio button) look and act like a push button. The button looks raised when it isn't pushed or checked, and sunken when it is pushed or checked.
BS_RIGHT Windows 95 only: Right-justifies text in the button rectangle. However, if the button is a check box or radio button that does not have the BS_RIGHTBUTTON style, the text is right justified on the right side of the check box or radio button.
BS_RIGHTBUTTON Windows 95 only: Positions a radio button's circle or a check box's square on the right side of the button rectangle. Same as the BS_LEFTTEXT style.
BS_TEXT Windows 95 only: Specifies that the button displays text.
BS_TOP Windows 95 only: Places text at the top of the button rectangle.
BS_VCENTER Windows 95 only: Places text in the middle (vertically) of the button rectangle.

+$#A Button States
This section discusses how selecting a button changes its state and how the application should respond.
+$#A Button Selection
The user can select a button in three ways: by clicking it with the mouse, by tabbing to it and then pressing the ENTER key, or (if the button is part of a group defined by the WS_GROUP style) by tabbing to the selected button in the group and using the arrow keys to move within that group. The two tabbing methods are part of the predefined keyboard interface provided by Windows. For a complete description of this interface, see Dialog Boxes_win32_Dialog_Boxes.
Selecting a button typically causes the following events:
· Windows gives the button the keyboard focus.
· The button sends its parent window a message to notify it of the selection.
· The parent window (or Windows) sends the button a message to change its state.
· The parent window (or Windows) repaints the button to reflect its new state.

The following topics discuss these events and button states in greater detail.
+$#A Elements of a Button State
A button's state can be characterized by its focus state, push state, and check state. The focus state applies to a check box, radio button, push button, or owner-drawn button. A button receives the keyboard focus when the user selects it and loses the focus when the user selects another control. Only one control can have the keyboard focus at a time.
When a button has the keyboard focus, the system typically highlights the text, icon, or bitmap of a button by surrounding it with a dotted line. In addition, a push button has a heavy dark border when it has the focus. Windows automatically changes the highlight for an automatic button, but the application must change the highlight for a nonautomatic button by sending messages.
The push state applies to a push button, check box, radio button, or three-state check box, but does not apply to other buttons. The push state of a button can be either pushed or not pushed. When a push button (or any button with the BS_PUSHLIKE style) is pushed, the button is drawn as a sunken button. When it is not pushed, it is drawn as a raised button. When a check box, radio button, or three-state check box is pushed, the background of the button is grayed. When it is not pushed, the background of the button is not grayed.
The check state applies to a check box, radio button, or three-state check box, but does not apply to other buttons. The state can be checked, unchecked, or (for three-state check boxes) indeterminate. A check box is checked when it contains a check mark, and is unchecked when it does not. A radio button is checked when it contains a black dot; and is unchecked when it does not. A three-state check box is checked when it contains a check mark, is unchecked when it does not, and is indeterminate when it contains a grayed box. Windows automatically changes the check state of an automatic button, but the application must change the check state of a nonautomatic button.
+$#A Changes to a Button State
When the user selects a button, it is generally necessary to change one or more of the button's state elements. Windows automatically changes the focus state for all button types, the push state for push buttons or buttons with the BS_PUSHLIKE style, and the check state for all automatic buttons. The application must make all other state changes, taking into account the button's type, style, and current state. The following list shows the state elements that must be changed for each button type:
· A check box must change the check state.
· A radio button must change the check state. It may also be necessary to change the check state of other radio buttons in the same group to ensure the mutually exclusive nature of radio buttons.
· Because the state of an owner-drawn button is application dependent, what the application must change in the button can vary. No elements of a group box must be changed, because users cannot select group boxes.

An application can determine a button's state by sending it a BM_GETCHECK_win32_BM_GETCHECK or BM_GETSTATE_win32_BM_GETSTATE message; the application can set a button's state by sending it a BM_SETCHECK_win32_BM_SETCHECK or BM_SETSTATE_win32_BM_SETSTATE message.
+$#A Notification Messages from Buttons
When the user selects a button, its state changes, and the button sends notification messages to its parent window. For example, a push button control sends the BN_CLICKED_win32_BN_CLICKED notification message whenever the user chooses the button. In all cases, the low-order word of the wParam parameter contains the control identifier, the high-order word of wParam contains the notification code, and the lParam parameter contains the control window handle.

Portability Issue The placement of notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x - based applications that process notification messages must be modified in this respect when they are ported to Win32-based applications.

Both the message and the parent window's response depend on the type, style, and current state of the button. Following are the button notification messages an application should monitor and process.
Message Description
BN_CLICKED_win32_BN_CLICKED The user clicked a button.
BN_DBLCLK_win32_BN_DBLCLK The user double-clicked a button.
BN_DISABLE_win32_BN_DISABLE A button is disabled.
BN_PUSHED_win32_BN_PUSHED The user pushed a button.
BN_KILLFOCUS_win32_BN_KILLFOCUS The button lost the keyboard focus.
BN_PAINT_win32_BN_PAINT The button should be painted.
BN_SETFOCUS_win32_BN_SETFOCUS The button gained the keyboard focus.
BN_UNPUSHED_win32_BN_UNPUSHED The button is no longer pushed.

A button sends the BN_DISABLE_win32_BN_DISABLE, BN_PUSHED_win32_BN_PUSHED, BN_KILLFOCUS_win32_BN_KILLFOCUS, BN_PAINT_win32_BN_PAINT, BN_SETFOCUS_win32_BN_SETFOCUS, and BN_UNPUSHED_win32_BN_UNPUSHED notification messages only if it has the BS_NOTIFY style. It sends the BN_CLICKED_win32_BN_CLICKED and BN_DBLCLK_win32_BN_DBLCLK notification messages regardless of the BS_NOTIFY style.
For automatic buttons, the operating system performs pushing, unpushing, and painting. In this case, the application typically processes only the BN_CLICKED and BN_DBLCLK notification messages. For buttons that are not automatic, the application typically responds to the notification message by sending a message to change the state of the button. For information about sending messages to buttons, Messages to Buttons_win32_Messages_to_Buttons.
When the user selects an owner-drawn button, the button sends its parent window a WM_DRAWITEM_win32_WM_DRAWITEM message containing the identifier of the control to be drawn and information about its dimensions and state. For more information about this message, see Using Owner-Drawn Buttons_win32_Using_Owner_Drawn_Buttons.
+$#A Messages to Buttons
A parent window can send messages to a button in an overlapped or child window by using the SendMessage_win32_SendMessage function, or it can send messages to a button in a dialog box by using the SendDlgItemMessage_win32_SendDlgItemMessage, CheckDlgButton_win32_CheckDlgButton, CheckRadioButton_win32_CheckRadioButton, and IsDlgButtonChecked_win32_IsDlgButtonChecked functions.
An application can use the BM_GETCHECK_win32_BM_GETCHECK message to retrieve the check state of check box or radio button. An application can also use the BM_GETSTATE_win32_BM_GETSTATE message to retrieve the button's current states (the check state, push state, and focus state). To get information about a specific state, use a bitmask on the returned state value.
The BM_SETCHECK_win32_BM_SETCHECK message sets the check state of a check box or radio button; the message returns zero. The BM_SETSTATE_win32_BM_SETSTATE message sets the push state of a button; this message also returns zero. The BM_SETSTYLE_win32_BM_SETSTYLE message changes the style of a button. It is designed for changing button styles within a type (for example, changing a check box to an automatic check box). It is not designed for changing between types (for example, changing a check box to a radio button). An application should not change a button from one type to another.
A button of the BS_BITMAP or BS_ICON style displays a bitmap or icon instead of text. The BM_SETIMAGE_win32_BM_SETIMAGE message associates the handle of a bitmap or icon with a button. The BM_GETIMAGE_win32_BM_GETIMAGE message retrieves the handle of the bitmap or icon associated with a button.
An application can also use the DM_GETDEFID_win32_DM_GETDEFID message to retrieve the identifier of the default push button control in a dialog box. An application can use the DM_SETDEFID_win32_DM_SETDEFID message to set the default push button for a dialog box.
Calling the CheckDlgButton_win32_CheckDlgButton or CheckRadioButton_win32_CheckRadioButton function is equivalent to sending a BM_SETCHECK message. Calling the IsDlgButtonChecked_win32_IsDlgButtonChecked function is equivalent to sending a BM_GETCHECK_win32_BM_GETCHECK message.
+$#A Button Color Messages
Windows provides default color values for buttons. The system sends a WM_CTLCOLORBTN_win32_WM_CTLCOLORBTN message to a button's parent window before the button is drawn. This message contains a handle of the button's device context and a handle of the child window. The parent window can use these handles to change the button's text and background colors. The following table shows the default button-color values.
Value Element colored
COLOR_BTNFACE Button faces.
COLOR_BTNHIGHLIGHT Highlight area (the top and left edges) of a button.
COLOR_BTNSHADOW Shadow area (the bottom and right edges) of a button.
COLOR_BTNTEXT Regular (nongrayed) text in buttons.
COLOR_GRAYTEXT Disabled (gray) text in buttons. This color is set to 0 if the current display driver does not support a solid gray color.
COLOR_WINDOW Window backgrounds.
COLOR_WINDOWFRAME Window frames.
COLOR_WINDOWTEXT Text in windows.

An application can retrieve the default values for these colors by calling the GetSysColor_win32_GetSysColor function, or set the values by calling the SetSysColors_win32_SetSysColors function. For more information about system colors, see System Information_win32_System_Information. For more information about how colors are used with controls, see Controls_win32_Controls.

Portability Issue The WM_CTLCOLOR message has been replaced by the set of control-color messages. When porting your Windows 3.x-based application to the Win32 API, you must modify any code that processes the WM_CTLCOLOR message.

+$#A Button Default Message Processing
The window procedure for the predefined button control window class carries out default processing for all messages that the button control procedure does not process. When the button control procedure returns FALSE for any message, the predefined window procedure checks the messages and performs the default actions listed in the following table.
Message Default action

BM_CLICK_win32_BM_CLICK Sends the button a WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN and a WM_LBUTTONUP_win32_WM_LBUTTONUP message, and sends the parent window a BN_CLICKED_win32_BN_CLICKED notification message.
BM_GETCHECK_win32_BM_GETCHECK Returns the check state of the button.
BM_GETIMAGE_win32_BM_GETIMAGE Returns the handle of the bitmap or icon associated with the button or NULL if the button has no bitmap or icon.
BM_GETSTATE_win32_BM_GETSTATE Returns the current check state, push state, and focus state of the button.
BM_SETCHECK_win32_BM_SETCHECK Sets the check state for all styles of radio buttons and check boxes. If the wParam parameter is greater than zero for radio buttons, the button is given the WS_TABSTOP style.
BM_SETIMAGE_win32_BM_SETIMAGE Associates the specified bitmap or icon handle with the button and returns the handle of the previous bitmap or icon.
BM_SETSTATE_win32_BM_SETSTATE Sets the push state of the button. For owner-drawn buttons, a WM_DRAWITEM_win32_WM_DRAWITEM message is sent to the parent window if the state of the button has changed.
BM_SETSTYLE_win32_BM_SETSTYLE Sets the button style. If the low-order word of the lParam parameter is TRUE, the button is redrawn.
WM_CHAR_win32_WM_CHAR Checks a check box or automatic check box when the user presses the plus (+) or equal (=) keys. Unchecks a check box or automatic check box when the user presses the minus (-) key.
WM_ENABLE_win32_WM_ENABLE Paints the button.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the background for owner-drawn buttons. The backgrounds of other buttons are erased as part of the WM_PAINT_win32_WM_PAINT and WM_ENABLE_win32_WM_ENABLE processing.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns values indicating the type of input processed by the default button procedure, as shown in the following table.

Button style Returns
BS_AUTOCHECKBOX
DLGC_WANTCHARS | DLGC_BUTTON
BS_AUTORADIOBUTTON
DLGC_RADIOBUTTON
BS_CHECKBOX
DLGC_WANTCHARS | DLGC_BUTTON
BS_DEFPUSHBUTTON
DLGC_DEFPUSHBUTTON
BS_GROUPBOX
DLGC_STATIC
BS_PUSHBUTTON
DLGC_UNDEFPUSHBUTTON
BS_RADIOBUTTON
DLGC_RADIOBUTTON

WM_GETFONT_win32_WM_GETFONT Returns a handle of the current font.
WM_KEYDOWN_win32_WM_KEYDOWN Pushes the button if the user presses the SPACEBAR.
WM_KEYUP_win32_WM_KEYUP Releases the mouse capture for all cases except the TAB key.
WM_KILLFOCUS_win32_WM_KILLFOCUS Removes the focus rectangle from a button. For push buttons and default push buttons, the focus rectangle is invalidated. If the button has the mouse capture, the capture is released, the button is not clicked, and any push state is removed.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Sends a BN_DBLCLK_win32_BN_DBLCLK notification message to the parent window for radio buttons and owner-drawn buttons. For other buttons, a double-click is processed as a WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Highlights the button if the position of the mouse cursor is within the button's client rectangle.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse capture if the button had the mouse capture.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Performs the same action as WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN, if the button has the mouse capture. Otherwise, no action is performed.
WM_NCCREATE_win32_WM_NCCREATE Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button.
WM_NCHITTEST_win32_WM_NCHITTEST Returns HTTRANSPARENT, if the button control is a group box.
WM_PAINT_win32_WM_PAINT Draws the button according to its style and current state.
WM_SETFOCUS_win32_WM_SETFOCUS Draws a focus rectangle on the button getting the focus. For radio buttons and automatic radio buttons, the parent window is sent a BN_CLICKED_win32_BN_CLICKED notification message.
WM_SETFONT_win32_WM_SETFONT Sets a new font and optionally updates the window.
WM_SETTEXT_win32_WM_SETTEXT Sets the text of the button. In the case of a group box, the message paints over the preexisting text before repainting the group box with the new text.
WM_SYSKEYUP_win32_WM_SYSKEYUP Releases the mouse capture for all cases except the TAB key.

The predefined window procedure passes all other messages to the DefWindowProc_win32_DefWindowProc function for default processing.
+$#A Using Buttons
· Creating a button outside a dialog box_win32_Creating_a_Button_Outside_a_Dialog_Box
· Using buttons that are not owner-drawn_win32_Using_Buttons_that_Are_Not_Owner_Drawn
· Using owner-drawn buttons_win32_Using_Owner_Drawn_Buttons

+$#A Creating a Button Outside a Dialog Box
The following example shows how to use the CreateWindow_win32_CreateWindow function to create a default push button.
hwndButton = CreateWindow(
"BUTTON", // predefined class
"OK", // button text
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // styles

// Size and position values are given explicitly, because
// the CW_USEDEFAULT constant gives zero values for buttons.
10, // starting x position
10, // starting y position
100, // button width
100, // button height
hwnd, // parent window
NULL, // No menu
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
NULL); // pointer not needed

+$#A Using Buttons that Are Not Owner-Drawn
The example in this section is the window procedure for a dialog box, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10611 0 /a "SDK_04.bmp"}
The check boxes and radio buttons in the Buttons dialog box are automatic. The check boxes are three-state. The Clear colors push button is a default push button. The check boxes, radio buttons, and push buttons are defined in the header file of the application, as follows.
#define IDB_BOX1 101 // first check box
#define IDB_BOX2 102 // second check box
#define IDB_BOX3 103 // third check box
#define IDB_REDBACK 104 // top radio button
#define IDB_BLUEBACK 105 // bottom radio button
#define IDB_CLEARBOXES 107 // top push button
#define IDB_CLEARBACK 108 // bottom push button

HBRUSH hbrRed, hbrBlue, hbrWhite;
BOOL fRedBack, fBlueBack, fClearColor; // background-state flags

Note that it is not necessary to define IDOK, the identifier for the OK push button.
In the following window procedure, the WM_CTLCOLORDLG_win32_WM_CTLCOLORDLG message notifies the application that the dialog box is about to be drawn. If the user presses the Clear colors button (signified by the fClearColor flag), the procedure uses the SendDlgItemMessage_win32_SendDlgItemMessage function to uncheck the check boxes and radio buttons. The BN_CLICKED_win32_BN_CLICKED notification message contains the identifiers of the buttons.
LRESULT APIENTRY ButtonProc(hDlg, message, wParam, lParam)
HWND hDlg; // window handle of dialog box
UINT message; // type of message
UINT wParam; // message-specific information
LONG lParam;
{
LRESULT lState;

switch (message) {
case WM_INITDIALOG:
hbrRed = CreateSolidBrush(RGB(255, 0, 0));
hbrBlue = CreateSolidBrush(RGB(0, 0, 255));
hbrWhite = GetStockObject(WHITE_BRUSH);
return TRUE;

case WM_CTLCOLORDLG:
if (fRedBack) {
fRedBack = FALSE;
return (LRESULT) hbrRed;
}
else if (fBlueBack) {
fBlueBack = FALSE;
return (LRESULT) hbrBlue;
}
else if (fClearColor) {
fClearColor = FALSE;

// Uncheck all check boxes and radio buttons.
SendDlgItemMessage(hDlg, // window handle
IDB_BOX1, // button identifier
BM_SETCHECK, // message
0, // check state unchecked)
0); // must be zero
SendDlgItemMessage(hDlg, IDB_BOX2,
BM_SETCHECK, 0, 0);
SendDlgItemMessage(hDlg, IDB_BOX3,
BM_SETCHECK, 0, 0);
SendDlgItemMessage(hDlg, IDB_REDBACK,
BM_SETCHECK, 0, 0);
SendDlgItemMessage(hDlg, IDB_BLUEBACK,
BM_SETCHECK, 0, 0);
}
return (LRESULT) hbrWhite;

case WM_COMMAND:
if (wParam == IDOK) {
EndDialog(hDlg, TRUE);
return TRUE;
}

if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case IDB_BOX1:

// Retrieve the state of the
// check box.
lState = SendDlgItemMessage(
hDlg, IDB_BOX1, BM_GETSTATE,
0, 0);

// The box-painting function is
// application defined.
BoxPainter(
hDlg, // window handle
1, // box to paint
lState); // state of box
break;

case IDB_BOX2:
lState = SendDlgItemMessage(
hDlg, IDB_BOX2, BM_GETSTATE,
0, 0);
BoxPainter(hDlg, 2, lState);
break;

case IDB_BOX3:
lState = SendDlgItemMessage(
hDlg, IDB_BOX3, BM_GETSTATE,
0, 0);
BoxPainter(hDlg, 3, lState);
break;

case IDB_REDBACK:
fRedBack = TRUE;
InvalidateRect(hDlg, NULL,
TRUE);
break;

case IDB_BLUEBACK:
fBlueBack = TRUE;
InvalidateRect(hDlg, NULL,
TRUE);
break;

case IDB_CLEARBACK:
fClearColor = TRUE;
InvalidateRect(hDlg, NULL,
TRUE);
break;

case IDB_CLEARBOXES:
BoxPainter(hDlg, 4,
(LRESULT) 0);
break;
}
}

case WM_DESTROY:
DeleteObject(hbrRed);
DeleteObject(hbrBlue);

// Do not delete hbrWhite, because it is a
// stock object.
break;

}
return FALSE; // did not process a message
UNREFERENCED_PARAMETER(lParam);
}

+$#A Using Owner-Drawn Buttons
The parent window of an owner-drawn button typically responds to at least three messages for the button: WM_INITDIALOG_win32_WM_INITDIALOG, WM_COMMAND_win32_WM_COMMAND, and WM_DRAWITEM_win32_WM_DRAWITEM. It is not necessary to process the WM_MEASUREITEM_win32_WM_MEASUREITEM message for owner-drawn buttons.
When you must paint an owner-drawn button, Windows sends the parent window a WM_DRAWITEM message whose lParam points to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure. Use this structure with all owner-drawn controls to provide the application with the information it requires to paint the control. The itemAction and itemState members of the DRAWITEMSTRUCT structure define how to paint an owner-drawn button.
The following example shows how to process WM_INITDIALOG, WM_DRAWITEM, and WM_COMMAND messages for owner-drawn buttons. This example demonstrates how to draw one of two bitmaps for a control, depending on whether the control is selected. You would typically use the wParam parameter of the WM_DRAWITEM message to identify the control; in this example, only one control is assumed.
LRESULT APIENTRY OwnDrawProc(hDlg, message, wParam, lParam)
HWND hDlg; // window handle of dialog box
UINT message; // type of message
UINT wParam; // message-specific information
LONG lParam;
{
HDC hdcMem;
LPDRAWITEMSTRUCT lpdis;

switch (message) {
case WM_INITDIALOG:

// hinst, hbm1 and hbm2 are defined globally.
hbm1 = LoadBitmap((HANDLE) hinst, "OwnBit1");
hbm2 = LoadBitmap((HANDLE) hinst, "OwnBit2");
return TRUE;

case WM_DRAWITEM:
lpdis = (LPDRAWITEMSTRUCT) lParam;
hdcMem = CreateCompatibleDC(lpdis->hDC);

if (lpdis->itemState & ODS_SELECTED) // if selected
SelectObject(hdcMem, hbm2);
else
SelectObject(hdcMem, hbm1);

// Destination
StretchBlt(
lpdis->hDC, // destination DC
lpdis->rcItem.left, // x upper left
lpdis->rcItem.top, // y upper left

// The next two lines specify the width and
// height.
lpdis->rcItem.right - lpdis->rcItem.left,
lpdis->rcItem.bottom - lpdis->rcItem.top,
hdcMem, // source device context
0, 0, // x and y upper left
32, // source bitmap width
32, // source bitmap height
SRCCOPY); // raster operation

DeleteDC(hdcMem);
return TRUE;

case WM_COMMAND:
if (wParam == IDOK
|| wParam == IDCANCEL) {
EndDialog(hDlg, TRUE);
return TRUE;
}
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case IDB_OWNERDRAW:

.
. // application-defined processing
.

break;
}
}
break;

case WM_DESTROY:
DeleteObject(hbm1); // delete bitmaps
DeleteObject(hbm2);

break;

}
return FALSE;
UNREFERENCED_PARAMETER(lParam);
}

+$#AK Button Reference
The following functions and messages are associated with buttons.
+$#A Button Functions
Following are the functions used with buttons.
CheckDlgButton_win32_CheckDlgButtonCheckRadioButton_win32_CheckRadioButtonIsDlgButtonChecked_win32_IsDlgButtonChecked
+$#A Button Messages
Following are the messages used with buttons.
BM_CLICK_win32_BM_CLICKBM_GETCHECK_win32_BM_GETCHECKBM_GETSTATE_win32_BM_GETSTATEBM_SETCHECK_win32_BM_SETCHECKBM_SETSTATE_win32_BM_SETSTATEBM_SETSTYLE_win32_BM_SETSTYLEBN_CLICKED_win32_BN_CLICKEDBN_DISABLE_win32_BN_DISABLEBN_HILITE_win32_BN_HILITEBN_PAINT_win32_BN_PAINTBN_UNHILITE_win32_BN_UNHILITEDM_GETDEFID_win32_DM_GETDEFIDDM_SETDEFID_win32_DM_SETDEFIDWM_CTLCOLORBTN_win32_WM_CTLCOLORBTNBN_DBLCLK_win32_BN_DBLCLKBN_DOUBLECLICKED_win32_BN_DOUBLECLICKEDBN_KILLFOCUS_win32_BN_KILLFOCUSBN_SETFOCUS_win32_BN_SETFOCUS

+$#AK Combo Boxes
A combo box is a unique type of control, defined by the COMBOBOX class, that combines much of the functionality of a list box and an edit control.
This overview describes the types and styles of combo box, the parts of a combo box, the use of an owner-drawn combo box, and how to subclass a combo box. Additional features of combo boxes are also discussed.
+$#A About Combo Boxes
The Microsoft® Win32® application programming interface (API) provides three types of combo box: simple combo boxes (CBS_SIMPLE), drop-down combo boxes (CBS_DROPDOWN), and drop-down list boxes (CBS_DROPDOWNLIST).
There are also a number of combo box styles that define specific properties. For example, two styles enable an application to create an owner-drawn combo box, making the application responsible for displaying information in the control.
A combo box consists of a list and a selection field. The list presents the options a user can select and the selection field displays the current selection. Except in drop-down list boxes, the selection field is an edit control and can be used to enter text not in the list.
+$#A Combo Box Types and Styles
Combo boxes can be characterized by type and style. Combo box types determine whether the combo box list is a drop-down list and whether the selection field is an edit control. A drop-down list appears only when the user opens it, so it uses less screen space than a list that is always visible. If the selection field is an edit control, the user can enter information not in the list; otherwise, the user can only select items in the list.
The following table shows the three combo box types and indicates whether each includes a drop-down list and an edit control:
Combo box type Drop-down list Edit control
Drop-down combo box Yes Yes
Drop-down list box Yes No
Simple combo box No Yes

Combo box styles define specific properties of a combo box. An application can combine styles; however some styles only apply to certain combo box types. The following table describes the combo box styles:
Style Description
CBS_AUTOHSCROLL Automatically scrolls the text in an edit control to the right when the user types a character at the end of the line. If this style is not set, only text that fits within the rectangular boundary is allowed.
CBS_DISABLENOSCROLL Shows a disabled vertical scroll bar in the list box when the box does not contain enough items to scroll. Without this style, the scroll bar is hidden when the list box does not contain enough items.
CBS_DROPDOWN Specifies a drop-down combo box.
CBS_DROPDOWNLIST Specifies a drop-down list box.
CBS_HASSTRINGS Specifies that an owner-drawn combo box contains items consisting of strings. The combo box maintains the memory and address for the strings so the application can use the CB_GETLBTEXT_win32_CB_GETLBTEXT message to retrieve the text for a particular item.
CBS_LOWERCASE Converts to lowercase all text in both the selection field and the list.
CBS_NOINTEGRALHEIGHT Specifies that the size of the combo box is exactly the size specified by the application when it created the combo box. Normally, Windows sizes a combo box so that it does not display partial items.
CBS_OEMCONVERT Converts text entered in the combo box edit control from the Windows character set to the OEM character set and then back to the Windows set. This ensures proper character conversion when the application calls the CharToOem_win32_CharToOem function to convert a Windows string in the combo box to OEM characters. This style is most useful for combo boxes that contain filenames and applies only to combo boxes created with the CBS_SIMPLE or CBS_DROPDOWN style.
CBS_OWNERDRAWFIXED Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are all the same height. The owner window receives a WM_MEASUREITEM_win32_WM_MEASUREITEM message when the combo box is created and a WM_DRAWITEM_win32_WM_DRAWITEM message when a visual aspect of the combo box has changed.
CBS_OWNERDRAWVARIABLE Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are variable in height. The owner window receives a WM_MEASUREITEM message for each item in the combo box when you create the combo box and a WM_DRAWITEM message when a visual aspect of the combo box has changed.
CBS_SIMPLE Specifies a simple combo box.
CBS_SORT Automatically sorts strings added to the list box.
CBS_UPPERCASE Converts to uppercase all text in both the selection field and the list.

+$#A Combo Box List
A list is the portion of a combo box that displays the items a user can select. Typically, an application initializes the contents of the list when it creates a combo box. Any list item selected by the user is the current selection. In simple and drop-down combo boxes, the user can type in the selection field instead of selecting a list item. In these cases, there is no current selection. For more information, see Current Selection_win32_Current_Selection.
+$#A List Contents
When an application creates a combo box, it typically initializes the combo box by adding one or more items to the list. Later, an application may add or delete list items, reinitialize the list, or retrieve item information from it.
An application adds list items to a combo box by sending the CB_ADDSTRING_win32_CB_ADDSTRING message to it. The specified item is added to the end of the list or, in a sorted combo box, in its correct sorted position based on the item's string. In an unsorted combo box, an application can use the CB_INSERTSTRING_win32_CB_INSERTSTRING message to insert an item at a specific position. Once added, a list item is identified by its position.
By using the CB_FINDSTRING_win32_CB_FINDSTRING or CB_FINDSTRINGEXACT_win32_CB_FINDSTRINGEXACT message, an application can determine the position of a list item. CB_FINDSTRING finds an item whose string begins with the specified string. CB_FINDSTRINGEXACT finds an item whose string matches the string exactly. Neither message is case sensitive.
An application can remove a list item by using the CB_DELETESTRING_win32_CB_DELETESTRING message. If an application needs to reinitialize the combo box list, it can first clear its entire contents by using the CB_RESETCONTENT_win32_CB_RESETCONTENT message. When adding multiple items to the list after a combo box has already been shown, an application can clear the redraw flag to prevent the combo box from being repainted after each item is added. For more information about redrawing, see the description of the WM_SETREDRAW_win32_WM_SETREDRAW message.
To retrieve the string associated with a list item, an application can use the CB_GETLBTEXT_win32_CB_GETLBTEXT message. The item's string is copied to the buffer specified by the application. To ensure that the buffer is large enough to receive the string, the application can first use the CB_GETLBTEXTLEN_win32_CB_GETLBTEXTLEN message to determine the length of the string. To get the number of list items in a combo box, an application can use the CB_GETCOUNT_win32_CB_GETCOUNT message.
+$#A Current Selection
The current selection is a list item the user has selected; the selected text appears in the selection field of the combo box. However, in the case of a simple combo box or a drop down combo box, the current selection is only one form of possible user input in a combo box. The user can also type text in the selection field.
The current selection is identified by the zero-based index of the selected list item. An application can set and retrieve it at any time. The parent window or dialog box procedure receives notifications when user changes the current selection for a combo box. The parent window or dialog box is not notified when the application changes the selection.
When a combo box is created, there is no current selection. This is also true for a simple or drop-down combo box, if the user has edited the contents of the selection field. To set the current selection, an application sends the CB_SETCURSEL_win32_CB_SETCURSEL message to the combo box. An application can also use the CB_SELECTSTRING_win32_CB_SELECTSTRING message to set the current selection to a list item whose string begins with a specified string. To determine the current selection, an application sends the CB_GETCURSEL_win32_CB_GETCURSEL message to the combo box. If there is no current selection, this message returns CB_ERR.
When the user changes the current selection in a combo box, the parent window or dialog-box procedure receives a WM_COMMAND_win32_WM_COMMAND message with the notification message CBN_SELCHANGE_win32_CBN_SELCHANGE in the high-order word of the wParam parameter. As a result, the application can respond to this notification message and execute a specific process each time the user selects a list item. This notification message is not sent when the current selection is set using the CB_SETCURSEL message.
In a drop-down combo box or drop-down list box, an application wait for the use to close the list box before processing a change in the current selection. This can be useful when significant processing is required. For example, to update a directory list based on the selected drive, an application can process the CBN_CLOSEUP_win32_CBN_CLOSEUP message instead of CBN_SELCHANGE to update a directory list after the user selects a drive.
An application could also process the notification messages CBN_SELENDOK_win32_CBN_SELENDOK and CBN_SELENDCANCEL_win32_CBN_SELENDCANCEL. The system sends CBN_SELENDOK when the user selects a list item, or selects an item and then closes the list. This indicates the user has finished, and the selection should be processed. CBN_SELENDCANCEL is sent when the user selects an item, but then selects another control, presses the ESCAPE key while the drop-down list is open, or closes the dialog box. This indicates the user's selection should be ignored. In a simple combo box, CBN_SELENDOK is sent before every CBN_SELCHANGE message. If the WS_EX_NOPARENTNOTIFY window style is specified for the combo box, the system does not send CBN_SELENDOK and CBN_SELENDCANCEL messages.
In a simple combo box, the system sends the CBN_DBLCLK_win32_CBN_DBLCLK notification message when the user double-clicks a list item. In a drop-down combo box or drop-down list, a single click hides the list so it is not possible to double-click an item.
+$#A Drop-Down Lists
Certain notifications and messages apply only to combo boxes containing drop-down lists. When a drop-down list is open or closed, the parent window of a combo box receives a notification in the form of a WM_COMMAND_win32_WM_COMMAND message. If the list is being opened, the high-order word of wParam is CBN_DROPDOWN_win32_CBN_DROPDOWN. If the list is being closed, it is CBN_CLOSEUP_win32_CBN_CLOSEUP.
An application can open the list of a drop-down combo box or drop-down list box by using the CB_SHOWDROPDOWN_win32_CB_SHOWDROPDOWN message. It can determine whether the list is open by using the CB_GETDROPPEDSTATE_win32_CB_GETDROPPEDSTATE message and can determine the coordinates of a drop-down list by using the CB_GETDROPPEDCONTROLRECT_win32_CB_GETDROPPEDCONTROLRECT message. An application can also increase the width of a drop-down list by using the CB_SETDROPPEDWIDTH_win32_CB_SETDROPPEDWIDTH message.
+$#A Edit Control Selection Fields
The selection field is the portion of a combo box that displays the currently selected list item. In simple and drop-down combo boxes, the selection field is an edit control and can be used to enter text that is not in the list.
An application can retrieve or set the contents of the selection field and can determine or set the edit selection. The application can also limit the amount of text a user can type in the selection field. When the contents of the selection field change, Windows sends notification messages to the parent window or dialog box procedure.
To retrieve the content of the selection field, an application can send the WM_GETTEXT_win32_WM_GETTEXT message to the combo box. To set the contents of the selection field of a simple or drop-down combo box, an application can send the WM_SETTEXT_win32_WM_SETTEXT message to the combo box.
The edit selection is the range of selected text, if any, in the selection field of a simple or drop-down combo box. An application can determine the starting and ending character positions of the current selection by using the CB_GETEDITSEL_win32_CB_GETEDITSEL message. It can also select characters in the edit selection by using the CB_SETEDITSEL_win32_CB_SETEDITSEL message.
Initially, the amount of text that the user can type into the selection field is limited by the size of the selection field. However, if the combobox has the CBS_AUTOHSCROLL style, the text can continue beyond the size of the selection field. An application can use the CB_LIMITTEXT_win32_CB_LIMITTEXT message to limit the amount of text a user can type into the selection field, regardless of whether the control has the CBS_AUTOHSCROLL style.
When the user edits the content of the selection field, the parent window or dialog box procedure receives notification messages. CBN_EDITUPDATE_win32_CBN_EDITUPDATE is sent first, indicating that the text in the selection field has been edited. After the altered text is displayed, Windows sends CBN_EDITCHANGE_win32_CBN_EDITCHANGE. When the selection field content changes as the result of a list item being selected, these messages are not sent.
+$#A Owner-Drawn Combo Boxes
An application can create an owner-drawn combo box to take responsibility for painting list items. The parent window or dialog box of an owner-drawn combo box (its owner) receives WM_DRAWITEM_win32_WM_DRAWITEM messages when a portion of the combo box needs to be painted. An owner-drawn combo box can list information other than, or in addition to, text strings. Owner-drawn combo boxes can be of any type. However, the edit control in a simple or drop-down combo box can only display text, while the owner paints the selection field in a drop-down list box.
The owner of an owner-drawn combo box must process the WM_DRAWITEM message. This message is sent whenever a portion of the combo box must be redrawn. The owner may need to process other messages, depending on the styles specified for the combo box.
An application can create an owner-drawn combo box by specifying the CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE style. If all list items in the combo box are the same height, such as strings or icons, an application can use the CBS_OWNERDRAWFIXED style. If list items are of varying height, bitmaps of different size, for example, an application can use the CBS_OWNERDRAWVARIABLE style.
The owner of an owner-drawn combo box can process a WM_MEASUREITEM_win32_WM_MEASUREITEM message to specify the dimensions of list items in the combo box. If the application creates the combo box by using the CBS_OWNERDRAWFIXED style, Windows sends the WM_MEASUREITEM message only once. The dimensions specified by the owner are used for all list items. If the CBS_OWNERDRAWVARIABLE style is used, Windows sends a WM_MEASUREITEM message for each list item added to the combo box. The owner can determine or set the height of a list item at any time by using the CB_GETITEMHEIGHT_win32_CB_GETITEMHEIGHT and CB_SETITEMHEIGHT_win32_CB_SETITEMHEIGHT messages, respectively.
If the information displayed in an owner-drawn combo box includes text, an application can keep track of the text for each list item by specifying the CBS_HASSTRINGS style. Combo boxes with the CBS_SORT style are sorted based on this text. If a combo box is sorted and not of the CBS_HASSTRINGS style, the owner must process the WM_COMPAREITEM_win32_WM_COMPAREITEM message.
In an owner-drawn combo box, the owner must keep track of list items containing information other than or in addition to text. One convenient way to do this is to save the handle of the information as item data. For more information about item data, see Data Associated with List Items_win32_Data_Associated_with_List_Items. To free data objects associated with items in a combo box, the owner can process the WM_DELETEITEM_win32_WM_DELETEITEM message.
For an example of an owner-drawn combo box, see Creating an Owner-Drawn Combo Box_win32_Creating_an_Owner_Drawn_Combo_Box.
+$#A Subclassed Combo Boxes
Subclassing is a procedure that allows an application to intercept and process messages sent or posted to a window. By using subclassing, an application can substitute its own processing for certain messages, while leaving most message processing to the class-defined window procedure.
When the operating system creates a window, it saves information about it in an internal data structure that includes the address of the window procedure. To subclass a window, an application calls the SetClassLong_win32_SetClassLong function to replace the address of that procedure with the instance address of an application-defined subclass procedure. Thereafter, all messages to the window are sent to the subclass procedure. This procedure then uses the CallWindowProc_win32_CallWindowProc function to pass unprocessed messages to the original window procedure. For a description of the message processing performed by the COMBOBOX class window procedure, see Default Combo Box Behavior_win32_Default_Combo_Box_Behavior.
When the combo box is outside a dialog box, an application cannot process the TAB, ENTER, and ESCAPE keys unless it uses a subclass procedure. When a simple or drop-down combo box receives the input focus, it immediately sets the focus to its child edit control. Therefore, an application must subclass the edit control to intercept keyboard input for a simple or drop-down combo box. For an example of this, see Subclassing a Combo Box_win32_Subclassing_a_Combo_Box.
If a subclass procedure processes the WM_PAINT_win32_WM_PAINT message, it must use the BeginPaint_win32_BeginPaint function to prepare for painting. Before calling the EndPaint_win32_EndPaint function, it passes the device-context (DC) handle as the wParam parameter for the window procedure. If EndPaint is called first, the class window procedure does no painting because EndPaint validates the entire window.
A technique related to subclassing is superclassing. A superclass resembles any other class except that its window procedure does not call DefWindowProc_win32_DefWindowProc to handle unprocessed messages. Instead, it passes unprocessed messages to the window procedure for the parent window class. Follow the guidelines in Window Procedures_win32_Window_Procedures to avoid problems that can occur with subclassing and superclassing.
+$#A Special Combo Box Features
The Win32 API provides special-purpose messages and functions that enable an application to display a directory listing in a combo box, associate data with list items in a combo box, and change the keyboard interface for a drop-down combo box or drop-down list box.
+$#A Directory Lists
An application can add the names of files or subdirectories to a combo box by sending the CB_DIR_win32_CB_DIR message to it. The wParam parameter for the window procedure specifies the attributes of the files to add, and the lParam parameter points to the text string that defines the file specification.
Within a dialog box, an application can also use DlgDirListComboBox_win32_DlgDirListComboBox. If the specified filename includes a drive, path, or both, DlgDirListComboBox changes the current drive or directory and removes the drive and path from the filename string. To show the directory name, the function updates the specified static control, if any. It then resets the contents of the combo box, and sends the CB_DIR message to the combo box to add the specified filenames.
To remove the selected filename, directory name, or drive letter from a combo box that has been filled by using DlgDirListComboBox, an application can use the DlgDirSelectComboBoxEx_win32_DlgDirSelectComboBoxEx function.
The DlgDirListComboBox and DlgDirSelectComboBoxEx functions and the CB_DIR message are similar to the DlgDirList_win32_DlgDirList and DlgDirSelectEx_win32_DlgDirSelectEx functions and the LB_DIR_win32_LB_DIR message used with list boxes.
+$#A Data Associated with List Items
An application can associate data with the list items in a combo box. The CB_SETITEMDATA_win32_CB_SETITEMDATA message associates a DWORD value with a list item, and the CB_GETITEMDATA_win32_CB_GETITEMDATA retrieves the value associated with a list item.
The example in Creating an Owner-Drawn Combo Box_win32_Creating_an_Owner_Drawn_Combo_Box uses item data to associate a constant with each item in a drop-down list box. Such a unique value identifies each item independent of its sorted position.
Other applications might use item data to associate a handle or pointer with a list item. If so, an application can process a WM_DELETEITEM_win32_WM_DELETEITEM message to delete or free the specified object when the list item is deleted.
+$#A The Extended User Interface
Drop-down combo boxes and drop-down list boxes support an alternative keyboard interface called the extended user interface. By default, the F4 key opens or closes the list, and the DOWN ARROW changes the current selection. In a combo box with the extended user interface, however, the F4 key is disabled and pressing the DOWN ARROW key opens the drop-down list.
To select the user interface for a combo box, an application can send the CB_SETEXTENDEDUI_win32_CB_SETEXTENDEDUI message to the combo box. A TRUE value for the wParam parameter enables the extended user interface; a FALSE value sets the default user interface. To determine whether a combo box uses the extended user interface, an application can send the CB_GETEXTENDEDUI_win32_CB_GETEXTENDEDUI message to the combo box.
+$#A Combo Box Notifications
Messages from combo boxes are sent as notifications in the form of WM_COMMAND_win32_WM_COMMAND messages. The notification message is stored in the high word of the wParam parameter, and an application can process the following combo box notification messages.
Notification message Description
CBN_CLOSEUP_win32_CBN_CLOSEUP Indicates the list in a drop-down combo box or drop-down list box is about to close.
CBN_DBLCLK_win32_CBN_DBLCLK Indicates the user has double-clicked a list item in a simple combo box.
CBN_DROPDOWN_win32_CBN_DROPDOWN Indicates the list in a drop-down combo box or drop-down list box is about to open.
CBN_EDITCHANGE_win32_CBN_EDITCHANGE Indicates the user has changed the text in the edit control of a simple or drop-down combo box. This notification message is sent after the altered text is displayed.
CBN_EDITUPDATE_win32_CBN_EDITUPDATE Indicates the user has changed the text in the edit control of a simple or drop-down combo box. This notification message is sent before the altered text is displayed.
CBN_ERRSPACE_win32_CBN_ERRSPACE Indicates the combo box cannot allocate enough memory to carry out a request, such as adding a list item.
CBN_KILLFOCUS_win32_CBN_KILLFOCUS Indicates the combo box is about to lose the input focus.
CBN_SELCHANGE_win32_CBN_SELCHANGE Indicates the current selection has changed.
CBN_SELENDCANCEL_win32_CBN_SELENDCANCEL Indicates that the selection made in the drop down list, while it was dropped down, should be ignored.
CBN_SELENDOK_win32_CBN_SELENDOK Indicates that the selection made drop down list, while it was dropped down, should be accepted.
CBN_SETFOCUS_win32_CBN_SETFOCUS Indicates the combo box has received the input focus.

+$#A Default Combo Box Behavior
This section contains a table that describes the messages specifically handled by the predefined COMBOBOX class window procedure.
Message Description
CB_ADDSTRING_win32_CB_ADDSTRING Sends an LB_ADDSTRING_win32_LB_ADDSTRING message to the list window to add a list item.
CB_DELETESTRING_win32_CB_DELETESTRING Sends an LB_DELETESTRING_win32_LB_DELETESTRING message to the list window to delete a list item.
CB_DIR_win32_CB_DIR Adds the filenames matching the specified attributes and path to the list.
CB_FINDSTRING_win32_CB_FINDSTRING Sends an LB_FINDSTRING_win32_LB_FINDSTRING message to the list window. This message returns the index of the first list item that begins with the specified text.
CB_FINDSTRINGEXACT_win32_CB_FINDSTRINGEXACT Sends an LB_FINDSTRING message to the list window. This message returns the index of the first list item exactly matching the specified text.
CB_GETCOUNT_win32_CB_GETCOUNT Sends an LB_GETCOUNT_win32_LB_GETCOUNT message to the list window. It returns the number of list items.
CB_GETCURSEL_win32_CB_GETCURSEL Sends an LB_GETCURSEL_win32_LB_GETCURSEL message to the list window. It returns the index of the currently selected item, if any.
CB_GETDROPPEDCONTROLRECT_win32_CB_GETDROPPEDCONTROLRECT Fills the specified rectangle structure with the screen coordinates of a drop-down list.
CB_GETDROPPEDSTATE_win32_CB_GETDROPPEDSTATE Returns TRUE if a drop-down list is open; otherwise, it returns FALSE.
CB_GETDROPPEDWIDTH_win32_CB_GETDROPPEDWIDTH Returns the minimum allowable width, in pixels, of the drop down list.
CB_GETEDITSEL_win32_CB_GETEDITSEL Sends an EM_GETSEL_win32_EM_GETSEL message to the edit control, and it returns the starting and ending position of the current selection. In drop-down list boxes, the window procedure returns CB_ERR.
CB_GETEXTENDEDUI_win32_CB_GETEXTENDEDUI Returns TRUE if the combo box is a drop-down combo box or drop-down list box and the extend user-interface flag is set; otherwise, it returns FALSE.
CB_GETHORIZONTALEXTENT_win32_CB_GETHORIZONTALEXTENT Sends an LB_GETHORIZONTALEXTENT_win32_LB_GETHORIZONTALEXTENT message to the list window. It returns the scrollable width, in pixels, of the drop down list.
CB_GETITEMDATA_win32_CB_GETITEMDATA Sends an LB_GETITEMDATA_win32_LB_GETITEMDATA message to the list window. It returns the 32-bit value associated with the specified list item.
CB_GETITEMHEIGHT_win32_CB_GETITEMHEIGHT Sends an LB_GETITEMHEIGHT_win32_LB_GETITEMHEIGHT message to the list window. It returns the height, in pixels, of the specified owner-drawn list item.
CB_GETLBTEXT_win32_CB_GETLBTEXT Sends an LB_GETTEXT_win32_LB_GETTEXT message to the list window. It copies the specified list text to the specified buffer.
CB_GETLBTEXTLEN_win32_CB_GETLBTEXTLEN Sends an LB_GETTEXTLEN_win32_LB_GETTEXTLEN message to the list window. It returns the length, in bytes, of the specified list text.
CB_GETLOCALE_win32_CB_GETLOCALE Sends an LB_GETLOCALE_win32_LB_GETLOCALE message to the list window. It returns the current locale for the list.
CB_GETTOPINDEX_win32_CB_GETTOPINDEX Sends an LB_GETTOPINDEX_win32_LB_GETTOPINDEX message to the list window. It returns the index of the first visible item in the drop down list.
CB_INITSTORAGE_win32_CB_INITSTORAGE Sends an LB_INITSTORAGE_win32_LB_INITSTORAGE message to the list window. It initializes space for the specified number of items and the specified number of bytes for item strings.
CB_INSERTSTRING_win32_CB_INSERTSTRING Sends an LB_INSERTSTRING_win32_LB_INSERTSTRING message to the list window. It inserts a list item at the specified position.
CB_LIMITTEXT_win32_CB_LIMITTEXT Sends an EM_LIMITTEXT_win32_EM_LIMITTEXT message to the edit control. It sets the maximum number of characters a user can enter in the edit control. In drop-down list boxes, the window procedure returns CB_ERR.
CB_RESETCONTENT_win32_CB_RESETCONTENT Sends an LB_RESETCONTENT_win32_LB_RESETCONTENT message to the list window, and it removes the contents of the list.
CB_SELECTSTRING_win32_CB_SELECTSTRING Sends an LB_SELECTSTRING_win32_LB_SELECTSTRING message to the list window. It selects the first list item, if any, that begins with the characters in the specified text.
CB_SETCURSEL_win32_CB_SETCURSEL Sends an LB_SETCURSEL_win32_LB_SETCURSEL message to the list window, and it sets the current selection.
CB_SETDROPPEDWIDTH_win32_CB_SETDROPPEDWIDTH Sets the minimum allowable width, in pixels, of the drop down list.
CB_SETEDITSEL_win32_CB_SETEDITSEL Sends an EM_SETSEL_win32_EM_SETSEL message to the edit control. It selects the specified range of text. In drop-down list boxes, the window procedure returns CB_ERR.
CB_SETEXTENDEDUI_win32_CB_SETEXTENDEDUI Sets or clears the extended user-interface flag. This flag changes the keys that open and close the list in a drop-down combo box or drop-down list box. If the combo box is a simple combo box, the window procedure returns CB_ERR.
CB_SETHORIZONTALEXTENT_win32_CB_SETHORIZONTALEXTENT Sends an LB_SETHORIZONTALEXTENT_win32_LB_SETHORIZONTALEXTENT message to the list window. It sets the scrollable width, in pixels, of the drop down list.
CB_SETITEMDATA_win32_CB_SETITEMDATA Sends an LB_SETITEMDATA_win32_LB_SETITEMDATA message to the list window. It associates the specified 32-bit value with a list item.
CB_SETITEMHEIGHT_win32_CB_SETITEMHEIGHT Sends an LB_SETITEMHEIGHT_win32_LB_SETITEMHEIGHT message to the list window. It sets the height of the specified owner-drawn list item or the selection field.
CB_SETLOCALE_win32_CB_SETLOCALE Sends an LB_SETLOCALE_win32_LB_SETLOCALE message to the list window, and it sets the current locale for the list. The locale affects how the list is sorted if it has the CBS_SORT style and strings are added using CB_ADDSTRING_win32_CB_ADDSTRING.
CB_SETTOPINDEX_win32_CB_SETTOPINDEX Sends an LB_SETTOPINDEX_win32_LB_SETTOPINDEX message to the list window. It scrolls the drop down list so the specified item is at the top of the visible range.
CB_SHOWDROPDOWN_win32_CB_SHOWDROPDOWN Shows or hides the drop-down list. This message has no effect on simple combo boxes.
WM_CHAR_win32_WM_CHAR Processes character input. In drop-down list boxes, this message is passed to the list window, which moves the selection to the first item beginning with the specified character. In simple and drop-down combo boxes, this message is passed to the edit control.
WM_CLEAR_win32_WM_CLEAR Deletes the edit selection. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_COMMAND_win32_WM_COMMAND Processes notification messages from the edit control and list window and sends corresponding combo box notification messages to the parent window.
For edit control notifications, the window procedure may update the list window's current selection, caret index, and top index. For list notification messages, the window procedure may update the content of the selection field.
WM_COMPAREITEM_win32_WM_COMPAREITEM Passes the message to the parent window, enabling the application to specify the relative sort position of two owner-drawn list items. The combo box window receives this message from the list window.
WM_COPY_win32_WM_COPY Copies the edit selection to the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_CREATE_win32_WM_CREATE Initializes the combo box.
WM_CUT_win32_WM_CUT Deletes the edit selection and places it on the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_DELETEITEM_win32_WM_DELETEITEM Passes the message to the parent window, notifying the application that a list item has been deleted. The combo box window receives this message from the list window.
WM_DRAWITEM_win32_WM_DRAWITEM Passes the message on to the parent window enabling the application to paint the specified list item. The combo box window receives this message from the list window. The window procedure can also originate this message to have the application paint the selection field of a drop-down list box.
WM_ENABLE_win32_WM_ENABLE Sets the state to enable or prohibit mouse and keyboard input.
WM_ERASEBKGND_win32_WM_ERASEBKGND Returns 1, indicating that the background is erased.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns a combination of the DLG_WANTCHARS and DLGC_WANTARROWS values.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the current font with which the combo box will draw its text.
WM_GETTEXT_win32_WM_GETTEXT Copies the contents of the selection field to the specified buffer. In simple and drop-down combo boxes, the edit control processes this message.
WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH Returns the length, in characters, of the text in the selection field. In simple and drop-down combo boxes, the edit control processes this message.
WM_KEYDOWN_win32_WM_KEYDOWN Processes noncharacter keyboard input. In drop-down list boxes, this message is sent to the list window, which may show or hide itself, or change its current selection or caret index. In simple and drop-down combo boxes, this message is passed to the edit control. The edit control passes certain keys to the list window, such as the UP and DOWN ARROW keys and the F4 key.
WM_KILLFOCUS_win32_WM_KILLFOCUS Hides the highlight in the selection field and closes the drop-down list, if necessary. If the window receiving the input focus is part of the combo box (for example, the edit control), this message is ignored.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Same as WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Sets the focus to the combo box and, for drop-down combo boxes and drop-down lists, can open or close the list. If it opens the list, the window procedure captures the mouse to enable selection by dragging and releasing the mouse button.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse capture if the mouse opened the list.
WM_MEASUREITEM_win32_WM_MEASUREITEM Posts the message to the parent window, enabling the application to modify the contents of the specified MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure. The combo box window receives this message from the list window.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Posts the message to the list window if the mouse has opened the list and the mouse button is still down. This enables a user to select an item by dragging the mouse pointer to a list item and then releasing the button.
WM_NCCREATE_win32_WM_NCCREATE Allocates an internal data structure used by the combo box window procedure.
WM_NCDESTROY_win32_WM_NCDESTROY Frees resources allocated in response to the WM_NCCREATE_win32_WM_NCCREATE message.
WM_PAINT_win32_WM_PAINT Paints the invalid region of the combo box. If wParam is not NULL, it is assumed to be a DC handle passed from a subclass function. The window procedure uses the specified DC instead of calling BeginPaint_win32_BeginPaint and EndPaint_win32_EndPaint.
WM_PASTE_win32_WM_PASTE Replaces the edit selection with the contents of the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_SETFOCUS_win32_WM_SETFOCUS Sets the focus to the edit control or, in drop-down list boxes, inverts the selection field and turns on the caret in the list window.
WM_SETFONT_win32_WM_SETFONT Saves the specified font handle in an internal structure, adjusts the dimensions of the selection field and list, and invalidates the combo box window. Text in the selection field and the list is displayed in the saved font.
WM_SETREDRAW_win32_WM_SETREDRAW Sets or clears the redraw flag. If the redraw flag is cleared, the combo box is not repainted until the flag is set again.
WM_SETTEXT_win32_WM_SETTEXT Sets the contents of the edit control. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_SIZE_win32_WM_SIZE Resizes the child windows, if necessary.
WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN Opens or closes the drop-down list depending on which arrow key the user pressed.

All other messages are passed to the DefWindowProc_win32_DefWindowProc function for default processing.
+$#A Using Combo Boxes
· Creating a simple combo box_win32_Creating_a_Simple_Combo_Box
· Creating an owner-drawn combo box_win32_Creating_an_Owner_Drawn_Combo_Box
· Subclassing a combo box_win32_Subclassing_a_Combo_Box

+$#A Creating a Simple Combo Box
This section describes how to use a simple combo box in a dialog box. The example in Creating a Spell Dialog Box_win32_Creating_a_Spell_Dialog_Box uses a simple combo box in a dialog box for a spelling checker. When it finds a misspelled word, the spelling checker suggests alternative spellings. The user can ignore the misspelling, change to one of the suggested spellings, or type a spelling not suggested. A simple combo box is well suited for receiving this kind of user input.
+$#A Creating the Simple Dialog Box
The dialog box template defines the window styles and control identifier for the combo box. In this example, the combo box uses the CBS_SIMPLE, CBS_SORT, WS_VSCROLL, and WS_TABSTOP styles.
The dialog box also contains three buttons: Change (IDOK), Ignore (IDSKIP), and Cancel (IDCANCEL). The IDOK and IDCANCEL constants are defined by the Win32 API. The IDSKIP constant is defined in the application's header file, as is the control identifier for the combo box, IDCOMBO.
For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes.
+$# Processing the WM_INITDIALOG and WM_DESTROY Messages in a Simple Dialog Box
When you use a combo box in a dialog box, you usually respond to the WM_INITDIALOG_win32_WM_INITDIALOG message by initializing the combo box. The code example in Creating a Spell Dialog Box_win32_Creating_a_Spell_Dialog_Box calls the application-defined OpenDictionary function to load the dictionary. It then calls the application-defined SelectNextWord and InitSpellList functions to select the first misspelled word.
SelectNextWord selects the next word in the edit control and copies it to the specified buffer. InitSpellList determines whether the selected word is in the dictionary. If not, it places the word in the selection field of the combo box and adds suggested spellings to the list.
The dialog box procedure processes the WM_DESTROY_win32_WM_DESTROY message to free the resources allocated to the spelling dictionary.
+$# Processing the WM_COMMAND Message in a Simple Dialog Box
When an event occurs in a dialog box control, the control sends a WM_COMMAND_win32_WM_COMMAND message to the dialog box procedure. The high-order word of the wParam parameter is a notification code, indicating the type of event that occurred. The low-order word of wParam is a constant identifying the control. The lParam parameter is the window handle for the control.
When processing the WM_COMMAND message, the spelling checker example examines the control identifier in the low-order word of wParam to determine the origin of the message. The constants IDCOMBO, IDOK, IDIGNORE, and IDCANCEL identify the combo box, the Change button, the Ignore button, and the Cancel button controls, respectively.
A combo box may send a WM_COMMAND message for a number of different reasons. To determine the type of event, the dialog box procedure examines the notification code in the high-order word of wParam. The example processes only the CBN_DBLCLK_win32_CBN_DBLCLK notification message, which is sent when the user double-clicks a list item. The dialog box procedure processes this notification message in the same way as a Change button click.
The buttons in this example send WM_COMMAND_win32_WM_COMMAND messages only when they are chosen by the user. When the user chooses the Change button, the dialog box procedure replaces the current selection in the application's edit control with the content of the selection field in the combo box. The selection field may contain either the selected list item or text the user has typed. The dialog box procedure then selects the next misspelled word in the same way it processes an Ignore button click.
When the user chooses the Ignore button, the dialog box procedure calls the application-defined SelectNextWord and InitSpellList functions to select the next misspelled word in the application's edit control. InitSpellList places the misspelled word in the selection field of the combo box and adds suggested spellings to the combo box list.
When the user chooses the Cancel button, the dialog box procedure calls the EndDialog_win32_EndDialog function to close the dialog box.
+$#A Creating a Spell Dialog Box
Following are the dialog box procedure and supporting functions for the spelling checker dialog box.
HWND hwndMain;
HWND hwndEdit;
char achTemp[256]; /* temporary buffer */

LPSTR lpstrDict; /* buffer for dictionary file */
LPSTR *paLpDict; /* array of pointers to words */
UINT cWords; /* number of words */

/********************************************************

FUNCTION: SpellDlgProc

PURPOSE: Dialog procedure for Spell dialog box.

*********************************************************/

BOOL CALLBACK SpellDlgProc(hwndDlg, msg, wParam, lParam)
HWND hwndDlg;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
switch (msg) {
case WM_INITDIALOG:
if (!OpenDictionary()) {
EndDialog(hwndDlg, 0);
break;
}
SendMessage(hwndEdit, EM_SETSEL, 0, 0);
do
if (!SelectNextWord(hwndEdit, achTemp)) {
GlobalFree((HGLOBAL) lpstrDict);
LocalFree((HLOCAL) paLpDict);
EndDialog(hwndDlg, 0);
break;
}
while (!InitSpellList(
GetDlgItem(hwndDlg, IDCOMBO), achTemp));
break;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCOMBO:
if (HIWORD(wParam) != CBN_DBLCLK)
break;

/* For a double-click, process the OK case. */

case IDOK:
SendDlgItemMessage(hwndDlg, IDCOMBO,
WM_GETTEXT, sizeof(achTemp),
(LPARAM) achTemp);
SendMessage(hwndEdit, EM_REPLACESEL, 0,
(LPARAM) achTemp);

/* Fall through to get the next word. */

case IDIGNORE:
do
if (!SelectNextWord(hwndEdit, achTemp)) {
EndDialog(hwndDlg, 0);
break;
}
while (!InitSpellList(GetDlgItem(hwndDlg,
IDCOMBO), achTemp));
break;

case IDCANCEL:
EndDialog(hwndDlg, 0);
}
break;

case WM_DESTROY:
GlobalFree((HGLOBAL) lpstrDict);
LocalFree((HLOCAL) paLpDict);
break;

default:
return FALSE;
}
return TRUE;
}

/********************************************************

FUNCTION: InitSpellList

PURPOSE: Initializes the selection field and list
of suggestions for the specified word, if
it is not in the dictionary.

RETURNS: If the list is initialized, the return
value is TRUE. If an error occurs or the
word is in the dictionary, the return
value is FALSE.

*********************************************************/

BOOL PASCAL InitSpellList(HWND hwndCombo, LPSTR lpszWord)
{
int min = 0; /* beginning of search range */
int max = cWords; /* end of search range */
int n; /* index of word */
int cmp; /* result of comparison */
char ch; /* first character in word */

ch = *lpszWord;
CharLowerBuff(&ch, 1);

/*
* Perform a binary search for the word.
*
* The global array paLpDict contains pointers to words
* in the global array lpstrDict. These two variables are
* initialized by the OpenDictionary function.
*/

n = max / 2;
while (min < max) {
cmp = lstrcmpi(lpszWord, paLpDict[n]);
if (cmp == 0)
return FALSE; /* not misspelled */

if (cmp < 0)
max = n;
else
min = n + 1;
n = (min + max) / 2;
}

/* List words beginning with the same letter as lpszWord. */

SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
while (n > 0 && *paLpDict[n - 1] == ch)
n--;
while (*paLpDict[n] == ch)
SendMessage(hwndCombo, CB_ADDSTRING,
0, (LPARAM) paLpDict[n++]);

/* Place the word in the selection field. */

SendMessage(hwndCombo, WM_SETTEXT, 0, (LPARAM) lpszWord);

return TRUE;
}

+$#A Creating an Owner-Drawn Combo Box
This section describes how to use an owner-drawn combo box. The example in Creating a Square Meal Dialog Box_win32_Creating_a_Square_Meal_Dialog_Box uses an owner-drawn drop-down list box to display the four food groups, each represented by a bitmap and a name. Selecting a food group causes the foods in that group to appear in a list.
+$#A Creating the Owner-Drawn Dialog Box
The dialog box template defines the window styles, buttons, and control identifiers for the combo box. The combo box in this example uses the CBS_DROPDOWNLIST, CBS_OWNERDRAWFIXED, CBS_SORT, CBS_HASSTRINGS, WS_VSCROLL, and WS_TABSTOP styles.
{ewc msdncd, EWGraphic, group10612 0 /a "SDK_01.bmp"}
The dialog box also contains a list box (IDLIST) and two buttons: OK (IDOK) and Cancel (IDCANCEL). The IDOK and IDCANCEL constants are defined by the Win32 API. The constant IDLIST is defined in the application's header file, as is the control identifier, IDCOMBO.
For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes.
+$# Processing the WM_INITDIALOG and WM_DESTROY Messages in an Owner-Drawn Dialog Box
When you use a combo box in a dialog box, you usually respond to a WM_INITDIALOG_win32_WM_INITDIALOG message by initializing the combo box. The example in Creating a Square Meal Dialog Box_win32_Creating_a_Square_Meal_Dialog_Box loads the bitmaps used for the owner-drawn combo box, then calls the application-defined InitGroupList function to initialize the combo box. It also selects the first list item in the combo box, then calls the application-defined InitFoodList function to initialize the list box.
In the example, the owner-drawn combo box is a drop-down list box containing the names of each of the four food groups. InitGroupList adds the name of each food group, and calls the application-defined SetItemData function to associate a constant with each list item that identifies a corresponding food group.
The list box in the example contains the names of foods in the selected food group. InitFoodList resets the contents of the list box, then adds the names of the current food selection in the current food group drop-down list box.
The dialog box procedure processes the WM_DESTROY_win32_WM_DESTROY message to delete the bitmaps in the owner-drawn combo box.
+$#A Processing the WM_MEASUREITEM Message
An owner-drawn combo box sends the WM_MEASUREITEM_win32_WM_MEASUREITEM message to its parent window or dialog box procedure so the application can set the dimensions of each list item. Because the example combo box has the CBS_OWNERDRAWFIXED style, the system sends the WM_MEASUREITEM message only once. Combo boxes with the CBS_OWNERDRAWVARIABLE style send a WM_MEASUREITEM message for each list item.
The lParam parameter points to a MEASUREITEMSTRUCT_win32_MEASUREITEMSTRUCT_str structure that identifies the control and list item. It also contains the default dimensions of the list item. The example in Creating a Square Meal Dialog Box modifies the itemHeight structure member to ensure that the list items are high enough to accommodate the food-group bitmaps.
+$#A Processing the WM_DRAWITEM Message
An owner-drawn combo box sends the WM_DRAWITEM_win32_WM_DRAWITEM message to its parent window or dialog box procedure each time the application must repaint a list item. The lParam parameter points to a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure that identifies the control and list item. It also contains information needed to paint the item.
The example in Creating a Square Meal Dialog Box_win32_Creating_a_Square_Meal_Dialog_Box displays the list-item text and the bitmap associated with the food group. If the item has the focus, it also draws a focus rectangle. Before displaying the text, the example sets the foreground and background colors, based on the item selected. Because the combo box has the CBS_HASSTRINGS style, the combo box maintains the text for each list item that can be retrieved using the CB_GETLBTEXT_win32_CB_GETLBTEXT message.
The bitmaps used for the list item depend on the food group. InitGroupList uses the CB_SETITEMDATA_win32_CB_SETITEMDATA message to associate a constant with each list item, identifying the corresponding food group. The window procedure uses this value, contained in the itemData member of the DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure, to determine which bitmaps to display. The system uses two bitmaps for each food group symbol: a monochrome bitmap with the SRCAND raster operation to erase the irregular region behind the image, and a color bitmap with the SRCPAINT raster operation to paint the image.
+$# Processing the WM_COMMAND Message in an Owner-Drawn Dialog Box
When an event occurs in a dialog box control, the control notifies the dialog box procedure by means of a WM_COMMAND_win32_WM_COMMAND message. The example in Creating a Square Meal Dialog Box_win32_Creating_a_Square_Meal_Dialog_Box processes notification messages from the combo box, the list box, and the OK button. The control identifier is in the low-order word of wParam, and the notification message is in the high-order word of wParam.
If the control identifier is IDCOMBO, an event has occurred in the combo box. In response, the dialog box procedure ignores all other combo box events except CBN_SELENDOK_win32_CBN_SELENDOK, which indicates that a selection was made, the drop down was closed up, and the changes made should be accepted. The dialog box procedure calls InitFoodList to reset the contents of the list box and to add the names of the current selection in the drop-down list box.
If the control identifier is IDLIST, an event has occurred in the list box. This causes the dialog box procedure to ignore all list box events except LBN_DBLCLK_win32_LBN_DBLCLK, which indicates that the user has double-clicked a list item. This event is processed in the same way as if an OK button has been chosen.
If the control identifier is IDOK, the user has chosen the OK button. In response, the dialog box procedure inserts the name of the selected food into the application's multiline edit control, then calls the EndDialog_win32_EndDialog function to close the dialog box.
If the control identifier is IDCANCEL, the user has clicked the Cancel button. In response, the dialog box procedure calls EndDialog to close the dialog box.
+$#A Creating a Square Meal Dialog Box
Following are the dialog box procedure and supporting functions for the Square Meal dialog box.
HWND hwndMain;
HWND hwndEdit;
char achTemp[256]; /* temporary buffer */

HBITMAP hbmBread;
HBITMAP hbmDairy;
HBITMAP hbmFruit;
HBITMAP hbmMeat;
HBITMAP hbmBreadMask;
HBITMAP hbmDairyMask;
HBITMAP hbmFruitMask;
HBITMAP hbmMeatMask;

/********************************************************

FUNCTION: FoodDlgProc

PURPOSE: Dialog procedure for Food dialog box.

*********************************************************/

BOOL CALLBACK FoodDlgProc(hwndDlg, msg, wParam, lParam)
HWND hwndDlg;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
LPMEASUREITEMSTRUCT lpmis;
LPDRAWITEMSTRUCT lpdis;
HBITMAP hbmIcon;
HBITMAP hbmMask;
COLORREF clrBackground;
COLORREF clrForeground;
TEXTMETRIC tm;
HDC hdc;
HWND hwnd;
int x;
int y;

switch (msg) {
case WM_INITDIALOG:

/*
* Call an application-defined function to load
* bitmap resources.
*/

if (!LoadIconBitmaps()) {
EndDialog(hwndDlg, -1);
break;
}

/* Initialize the drop-down list box. */

if (!InitGroupList(hwndDlg)) {
DeleteIconBitmaps();
EndDialog(hwndDlg, -1);
break;
}

/* Select the first food group. */

SendDlgItemMessage(hwndDlg, IDCOMBO, CB_SETCURSEL,
0, 0);

/* List the foods and select the first food. */

InitFoodList(hwndDlg);
SendDlgItemMessage(hwndDlg, IDLIST, LB_SETCURSEL,
0, 0);
break;

case WM_MEASUREITEM:
lpmis = (LPMEASUREITEMSTRUCT) lParam;

if (lpmis->itemHeight < CY_BITMAP + 2)
lpmis->itemHeight = CY_BITMAP + 2;

break;

case WM_DRAWITEM:
lpdis = (LPDRAWITEMSTRUCT) lParam;
if (lpdis->itemID == -1) /* empty item */
break;

/* Determine the bitmaps used to draw the icon. */

switch (lpdis->itemData) {
case ID_BREAD:
hbmIcon = hbmBread;
hbmMask = hbmBreadMask;
break;

case ID_DAIRY:
hbmIcon = hbmDairy;
hbmMask = hbmDairyMask;
break;

case ID_FRUIT:
hbmIcon = hbmFruit;
hbmMask = hbmFruitMask;
break;

default: /* meat */
hbmIcon = hbmMeat;
hbmMask = hbmMeatMask;
break;
}

/*
* The colors depend on whether the item is
* selected.
*/

clrForeground = SetTextColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));

clrBackground = SetBkColor(lpdis->hDC,
GetSysColor(lpdis->itemState & ODS_SELECTED ?
COLOR_HIGHLIGHT : COLOR_WINDOW));

/* Calculate the vertical and horizontal position. */

GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top -
tm.tmHeight) / 2;
x = LOWORD(GetDialogBaseUnits()) / 4;

/* Get and display the text for the list item. */

SendMessage(lpdis->hwndItem, CB_GETLBTEXT,
lpdis->itemID, (LPARAM) (LPCSTR) achTemp);

ExtTextOut(lpdis->hDC, CX_BITMAP + 2 * x, y,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
achTemp, lstrlen(achTemp), NULL);

/* Restore the previous colors. */

SetTextColor(lpdis->hDC, clrForeground);
SetBkColor(lpdis->hDC, clrBackground);

/* Show the icon. */

hdc = CreateCompatibleDC(lpdis->hDC);
if (hdc == NULL)
break;

SelectObject(hdc, hbmMask);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCAND);

SelectObject(hdc, hbmIcon);
BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,
CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCPAINT);

DeleteDC(hdc);

/* If the item has the focus, draw focus rectangle. */

if (lpdis->itemState & ODS_FOCUS)
DrawFocusRect(lpdis->hDC, &lpdis->rcItem);

break;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCOMBO:
if (HIWORD(wParam) == CBN_SELENDOK) {
InitFoodList(hwndDlg);
SendDlgItemMessage(hwndDlg, IDLIST,
LB_SETCURSEL, 0, 0);
}
break;

case IDLIST:
if (HIWORD(wParam) != LBN_DBLCLK)
break;

/* For a double-click, process the OK case. */

case IDOK:

/* Get the text for the selected list item. */

hwnd = GetDlgItem(hwndDlg, IDLIST);
SendMessage(hwnd, LB_GETTEXT,
SendMessage(hwnd, LB_GETCURSEL, 0, 0),
(LPARAM) achTemp);

/* Insert the text into the edit window. */

SendMessage(hwndEdit, EM_REPLACESEL, 0,
(LPARAM) achTemp);

EndDialog(hwndDlg, 0);
break;

case IDCANCEL:
hwnd = GetDlgItem(hwndDlg, IDCOMBO);
if (SendMessage(hwnd, CB_GETDROPPEDSTATE,
0, 0))
SendMessage(hwnd, CB_SHOWDROPDOWN,
FALSE, 0);
else
EndDialog(hwndDlg, 0);
}
break;

case WM_DESTROY:

/*
* Call the application-defined function to free
* bitmap resources.
*/

DeleteIconBitmaps();
break;

default:
return FALSE;
}
return TRUE;
}

/********************************************************

FUNCTION: InitGroupList

PURPOSE: Initializes the "food groups" drop-down
list box.

COMMENTS: The ID of the food group associated with
each list item is saved as item data.

*********************************************************/

BOOL PASCAL InitGroupList(HWND hwndDlg)
{
HWND hwndCombo = GetDlgItem(hwndDlg, IDCOMBO);
DWORD dwIndex;

/* Add an item for each food group. */

LoadString(hinst, ID_BREAD, achTemp, sizeof(achTemp));
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,
(LPARAM) (LPCSTR) achTemp);
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_BREAD);

LoadString(hinst, ID_DAIRY, achTemp, sizeof(achTemp));
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,
(LPARAM) (LPCSTR) achTemp);
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_DAIRY);

LoadString(hinst, ID_FRUIT, achTemp, sizeof(achTemp));
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,
(LPARAM) (LPCSTR) achTemp);
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_FRUIT);

LoadString(hinst, ID_MEAT, achTemp, sizeof(achTemp));
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,
(LPARAM) (LPCSTR) achTemp);
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_MEAT);

return TRUE;
}

/********************************************************

FUNCTION: InitFoodList

PURPOSE: Clears the contents of the food list, and
adds the names of foods for the current
food group.

*********************************************************/

void PASCAL InitFoodList(HWND hwndDlg)
{
HWND hwndCombo = GetDlgItem(hwndDlg, IDCOMBO);
HWND hwndList = GetDlgItem(hwndDlg, IDLIST);
UINT idFoodGroup;
LPSTR lpsz;
LPSTR lpszEnd;

/* Determine the current food group. */

idFoodGroup = SendMessage(
hwndCombo,
CB_GETITEMDATA,
SendMessage(hwndCombo, CB_GETCURSEL, 0, 0),
0
);

/* Clear the list contents. */

SendMessage(hwndList, LB_RESETCONTENT, 0, 0);

.
. /* Add food names for the current food group. */
.

}

+$#A Subclassing a Combo Box
This section demonstrates how to subclass combo boxes so your application can use them outside a dialog box. The example in Creating a Combo-box Toolbar_win32_Creating_a_Combo_box_Toolbar shows the window procedure for a toolbar window containing two combo boxes. By subclassing the edit controls within the combo boxes, the toolbar window intercepts TAB, ENTER, and ESC keys that would otherwise be ignored.
The following illustration shows the toolbar window.
{ewc msdncd, EWGraphic, group10612 1 /a "SDK_02.bmp"}
+$#A Processing the WM_CREATE Message
The example in Creating a Combo-box Toolbar_win32_Creating_a_Combo_box_Toolbar processes the WM_CREATE_win32_WM_CREATE message to create two combo box controls as child windows. It then subclasses the edit controls (selection fields) in each combo box because they receive the character input for simple and drop-down combo box. The application gets the handle of each edit control by using the ChildWindowFromPoint_win32_ChildWindowFromPoint function.
To subclass the edit controls, the application calls the SetWindowLong_win32_SetWindowLong function, replacing the address of the class window procedure with the address of the application-defined SubClassProc function. The address of the original window procedure is saved in the global variable lpfnEditWndProc.
SubClassProc intercepts TAB, ESC, and ENTER keys and notifies the toolbar window by sending application-defined messages (WM_TAB, WM_ESC, and WM_ENTER). SubClassProc uses the CallWindowProc_win32_CallWindowProc function to pass most messages to the original window procedure, lpfnEditWndProc.
+$#A Processing the WM_SETFOCUS Message
When the toolbar window receives the input focus, it immediately sets the focus to the first combo box in the toolbar. To do so, the example in Creating a Combo-box Toolbar_win32_Creating_a_Combo_box_Toolbar calls the SetFocus_win32_SetFocus function in response to the WM_SETFOCUS_win32_WM_SETFOCUS message.
+$#A Processing the Application-Defined Messages
In the example in Creating a Combo-box Toolbar_win32_Creating_a_Combo_box_Toolbar, SubClassProc sends application-defined messages to the toolbar window when the user presses the TAB, ESC, or ENTER key in a combo box. The WM_TAB message is sent for the TAB key, the WM_ESC message for the ESC key, and the WM_ENTER message for the ENTER key.
The example processes the WM_TAB message by setting the focus to the next combo box in the toolbar. It processes the WM_ESC message by setting the focus to the main application window.
In response to the WM_ENTER message, the example ensures that the current selection for the combo box is valid and then sets the focus to the main application window. If the combo box contains no current selection, the example uses the CB_FINDSTRINGEXACT_win32_CB_FINDSTRINGEXACT message to search for a list item that matches the contents of the selection field. If there is a match, the example sets the current selection; otherwise, it adds a new list item.
+$#A Creating a Combo-box Toolbar
Following are the window procedure for the toolbar and the subclass procedure for the two combo boxes.
#define WM_TAB (WM_USER)
#define WM_ESC (WM_USER + 1)
#define WM_ENTER (WM_USER + 2)

HWND hwndMain;
HWND hwndEdit;
WNDPROC lpfnEditWndProc; /* original window procedure for */
/* the combo box edit windows */

int cyToolbar; /* toolbar window height */

/********************************************************

FUNCTION: ToolbarWindowProc

PURPOSE: Window procedure for the toolbar window

*********************************************************/

LRESULT CALLBACK ToolbarWindowProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndEdit1;
static HWND hwndEdit2;
static HWND hwndCombo1;
static HWND hwndCombo2;
.
.
.
POINT pt;
DWORD dwBaseUnits;
HWND hwndCombo;
DWORD dwIndex;

switch (msg) {
case WM_CREATE:

/* Create two combo box child windows. */

dwBaseUnits = GetDialogBaseUnits();

hwndCombo1 = CreateWindow("COMBOBOX", "",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(6 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, hinst, NULL);

hwndCombo2 = CreateWindow("COMBOBOX", "",
CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,
(112 * LOWORD(dwBaseUnits)) / 4,
(2 * HIWORD(dwBaseUnits)) / 8,
(100 * LOWORD(dwBaseUnits)) / 4,
(50 * HIWORD(dwBaseUnits)) / 8,
hwnd, NULL, hinst, NULL);

/* Get the edit window handle for each combo box. */

pt.x = 1;
pt.y = 1;
hwndEdit1 = ChildWindowFromPoint(hwndCombo1, pt);
hwndEdit2 = ChildWindowFromPoint(hwndCombo2, pt);

/*
* Change the window procedure for both edit windows
* to the subclass procedure.
*/

lpfnEditWndProc = (WNDPROC) SetWindowLong(hwndEdit1,
GWL_WNDPROC, (DWORD) SubClassProc);

SetWindowLong(hwndEdit2, GWL_WNDPROC,
(DWORD) SubClassProc);

break;

case WM_SETFOCUS:
SetFocus(hwndCombo1);
break;

case WM_TAB:
if (GetFocus() == hwndEdit1)
SetFocus(hwndCombo2);
else
SetFocus(hwndCombo1);
break;

case WM_ESC:

hwndCombo = GetFocus() == hwndEdit1 ?
hwndCombo1 : hwndCombo2;

/* Clear the current selection. */

SendMessage(hwndCombo, CB_SETCURSEL,
(WPARAM) (-1), 0);

/* Set the focus to the main window. */

SetFocus(hwndMain);
break;

case WM_ENTER:
hwndCombo = GetFocus() == hwndEdit1 ?
hwndCombo1 : hwndCombo2;
SetFocus(hwndMain);

/* If there is no current selection, set one. */

if (SendMessage(hwndCombo, CB_GETCURSEL, 0, 0)
== CB_ERR) {
if (SendMessage(hwndCombo, WM_GETTEXT,
sizeof(achTemp), (LPARAM) achTemp) == 0)
break; /* empty selection field */
dwIndex = SendMessage(hwndCombo,
CB_FINDSTRINGEXACT, (WPARAM) (-1),
(LPARAM) achTemp);

/* Add the string, if necessary, and select it. */

if (dwIndex == CB_ERR)
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING,
0, (LPARAM) achTemp);
if (dwIndex != CB_ERR)
SendMessage(hwndCombo, CB_SETCURSEL,
dwIndex, 0);
}
break;

.
. /* Process additional messages. */
.

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

/********************************************************

FUNCTION: SubClassProc

PURPOSE: Process TAB and ESCAPE keys, and pass all
other messages to the class window
procedure.

*********************************************************/

LRESULT CALLBACK SubClassProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
switch (msg) {
case WM_KEYDOWN:
switch (wParam) {
case VK_TAB:
SendMessage(hwndToolbar, WM_TAB, 0, 0);
return 0;
case VK_ESCAPE:
SendMessage(hwndToolbar, WM_ESC, 0, 0);
return 0;
case VK_RETURN:
SendMessage(hwndToolbar, WM_ENTER, 0, 0);
return 0;
}
break;

case WM_KEYUP:
case WM_CHAR:
switch (wParam) {
case VK_TAB:
case VK_ESCAPE:
case VK_RETURN:
return 0;
}
}

/*
* Call the original window procedure for default
* processing.
*/

return CallWindowProc(lpfnEditWndProc, hwnd,
msg, wParam, lParam);
}

+$#AK Combo Box Reference
The following functions and messages are associated with combo boxes.
+$#A Combo Box Functions
Following are the functions used with combo boxes.
DlgDirListComboBox_win32_DlgDirListComboBoxDlgDirSelect_win32_DlgDirSelectDlgDirSelectComboBox_win32_DlgDirSelectComboBoxDlgDirSelectComboBoxEx_win32_DlgDirSelectComboBoxEx
+$#A Combo Box Messages
Following are the messages used with combo boxes.
CB_ADDSTRING_win32_CB_ADDSTRINGCB_DELETESTRING_win32_CB_DELETESTRINGCB_DIR_win32_CB_DIRCB_FINDSTRING_win32_CB_FINDSTRINGCB_FINDSTRINGEXACT_win32_CB_FINDSTRINGEXACTCB_GETCOUNT_win32_CB_GETCOUNTCB_GETCURSEL_win32_CB_GETCURSELCB_GETDROPPEDCONTROLRECT_win32_CB_GETDROPPEDCONTROLRECTCB_GETEDITSEL_win32_CB_GETEDITSELCB_GETEXTENDEDUI_win32_CB_GETEXTENDEDUICB_GETITEMDATA_win32_CB_GETITEMDATACB_GETITEMHEIGHT_win32_CB_GETITEMHEIGHTCB_GETLBTEXT_win32_CB_GETLBTEXTCB_GETLBTEXTLEN_win32_CB_GETLBTEXTLENCB_GETLOCALE_win32_CB_GETLOCALECB_INSERTSTRING_win32_CB_INSERTSTRINGCB_LIMITTEXT_win32_CB_LIMITTEXTCB_RESETCONTENT_win32_CB_RESETCONTENTCB_SELECTSTRING_win32_CB_SELECTSTRINGCB_SETCURSEL_win32_CB_SETCURSELCB_SETEDITSEL_win32_CB_SETEDITSELCB_SETEXTENDEDUI_win32_CB_SETEXTENDEDUICB_SETITEMDATA_win32_CB_SETITEMDATACB_SETITEMHEIGHT_win32_CB_SETITEMHEIGHTCB_SETLOCALE_win32_CB_SETLOCALECB_SHOWDROPDOWN_win32_CB_SHOWDROPDOWNCBN_CLOSEUP_win32_CBN_CLOSEUPCBN_DBLCLK_win32_CBN_DBLCLKCBN_DROPDOWN_win32_CBN_DROPDOWNCBN_EDITCHANGE_win32_CBN_EDITCHANGECBN_EDITUPDATE_win32_CBN_EDITUPDATECBN_ERRSPACE_win32_CBN_ERRSPACECBN_KILLFOCUS_win32_CBN_KILLFOCUSCBN_SELCHANGE_win32_CBN_SELCHANGECBN_SELENDCANCEL_win32_CBN_SELENDCANCELCBN_SELENDOK_win32_CBN_SELENDOKCBN_SETFOCUS_win32_CBN_SETFOCUSWM_COMPAREITEM_win32_WM_COMPAREITEMWM_DRAWITEM_win32_WM_DRAWITEMWM_MEASUREITEM_win32_WM_MEASUREITEM

+$#AK Controls
In Microsoft® Windows®, a control is a child window an application uses in conjunction with another window to perform simple input and output (I/O) tasks. Controls are most often used within dialog boxes, but they can also be used in other windows. Controls within dialog boxes provide the user with the means to type text, choose options, and direct a dialog box to complete its action. Controls in other windows provide a variety of services, such as letting the user choose commands, view status, and view and edit text.
+$#A About Controls
Controls, like other windows, each belong to a window class, either predefined or application defined. The window class and the corresponding window procedure define the properties of the control, its appearance, behavior, and purpose. An application can create controls individually by specifying the name of the window class when calling the CreateWindowEx_win32_CreateWindowEx function. An application can also direct Windows to create controls for a dialog box by specifying the controls in the dialog box template.
Controls are child windows. When Windows creates controls for a dialog box, each control is the child of the dialog box. When an application creates a control, the control is the child of a window identified by the application. This relationship is important because a control sends messages, called notification messages, to its parent window when events, such as input from the user, occur in the control. The application relies on these notification messages to determine what action the user wants the application to take.
Because controls are windows, an application can manipulate them by using the window-management functions, such as the ShowWindow_win32_ShowWindow and EnableWindow_win32_EnableWindow functions. If the window class for a control supports control messages, an application can also manipulate a control of that class by using the SendMessage_win32_SendMessage function to send these messages to the control. The purpose and function of each control message is specific to the window class and is defined by the control's window procedure.
+$#A Predefined Controls
Windows provides several predefined window classes for controls. Controls belonging to these window classes are called predefined controls. An application creates a predefined control of a particular type by specifying the appropriate window class name in either the CreateWindowEx_win32_CreateWindowEx function or the dialog box template. Following are the predefined window classes.
Name Description
BUTTON Creates button controls. These controls typically notify the parent window when the user chooses the control.
COMBOBOX Creates combo boxes. These controls are a combination of list boxes and edit controls, letting the user choose and edit items.
EDIT Creates edit controls. These controls let the user view and edit text.
LISTBOX Creates list boxes. These controls display a list from which the user can select one or more items.
SCROLLBAR Creates scroll bar controls. These controls let the user choose the direction and distance to scroll information in a related window.
STATIC Creates static controls. These controls often act as labels for other controls.

Each predefined window class has a corresponding set of control styles that enable an application to vary the appearance and behavior of the controls it creates. For example, the BUTTON class supports styles to create push buttons, radio buttons, check boxes, and group boxes. An application specifies the style when creating the control.
Each predefined window class has a corresponding set of notification and control messages. Applications rely on the notification messages to determine when the user has provided input to the controls. For example, a push button sends a BN_CLICKED_win32_BN_CLICKED message to the parent window when the user clicks the button. Applications use the control messages to retrieve information from the controls and to manipulate the appearance and behavior of the controls. For example, an application can send a BM_GETCHECK_win32_BM_GETCHECK message to a check box to determine whether it currently contains a check mark.
Most applications make extensive use of predefined controls in dialog boxes and other windows. Because predefined controls offer many capabilities, a full discussion of each is beyond the scope of this topic.
+$#A Custom Controls
Applications can create custom controls to perform tasks not supported by predefined controls. Windows provides the following ways to create custom controls:
· Use owner-drawn buttons, list boxes, and combo boxes.
· Subclass an existing control-window class.
· Register and implement from scratch an application-defined window class.
Buttons, list boxes, and combo boxes have owner-drawn styles available that direct the control to send a message to the parent window whenever the control must be drawn. This feature permits an application to alter the appearance of a control. For buttons, the owner-drawn style affects how the system draws the entire control. For list boxes and combo boxes, the parent window draws the items within the control, and the control draws its own outline. For example, an application can customize a list box so that it displays a small bitmap next to each item in the list.
An application can designate list boxes, combo boxes, and buttons as owner-drawn controls by creating them with the appropriate style. When a control has the owner-drawn style, Windows handles the user's interaction with the control as usual, performing such tasks as detecting when a user has chosen a button and notifying the button's owner of the event. However, because the control is owner drawn, the parent window of the control is responsible for the visual appearance of the control. For more information about owner-drawn controls, see the individual topics for buttons, list boxes, and combo boxes.
Subclassing an existing control is another way to create a custom control. The subclass procedure can alter selected behaviors of the control by processing those messages that affect the selected behaviors. All other messages pass to the original window procedure for the control. For example, an application can display a small bitmap next to the text in a read-only, single-line edit control by subclassing the control and processing the WM_PAINT_win32_WM_PAINT message. For more information about subclassing, see Window Classes_win32_Window_Classes.
Although an application may subclass a predefined control, it relies on the window procedure of the control to provide all other aspects of the control's behavior. For more information about a control's behavior, see the individual topics for the predefined controls.
An application can create custom controls by registering an application-defined window class and specifying the name of the window class in the CreateWindowEx_win32_CreateWindowEx function or in the dialog box template. The process for registering an application-defined window class for a custom control is the same as for registering a class for an ordinary window. Each class must have a unique name, a corresponding window procedure, and other information.
At a minimum, the window procedure draws the control. If an application uses the control to let the user type information, the window procedure also processes input messages from the keyboard and mouse and sends notification messages to the parent window. In addition, if the control supports control messages, the window procedure processes messages sent to it by the parent window or other windows. For example, controls often process the WM_GETDLGCODE_win32_WM_GETDLGCODE message sent by dialog boxes to direct a dialog box to process keyboard input in a given way.
+$#A Control Notification Messages
A control should send a notification message to its parent window to notify the parent about user input or changes to the control. The notification message is a WM_COMMAND_win32_WM_COMMAND message that includes a control identifier and a notification code identifying the nature of the event. A control identifier is a unique number the application uses to identify the control sending the message. The application sets the identifier for a control when it creates the control. The application specifies the identifier either in the hMenu parameter of the CreateWindowEx_win32_CreateWindowEx function or in the id member of the dialog box template (the DLGITEMTEMPATE structure).
Because the control does not set the control identifier, the control must retrieve the identifier before it can send notification messages. A control must use the GetDlgCtrlID_win32_GetDlgCtrlID function to retrieve its own control identifier. Although the control identifier is specified as the menu handle when the control is created, the GetMenu_win32_GetMenu function cannot be used to retrieve the identifier. Alternatively, a control can retrieve the identifier from the hMenu member in the CREATESTRUCT_win32_CREATESTRUCT_str structure while processing the WM_CREATE_win32_WM_CREATE message.
+$#A Control Messages
A parent window or other windows send control messages to direct a control to perform specific tasks. The window procedure processes these messages and carries out the requested action.
Control messages can be predefined or application defined. Windows has several predefined messages, such as WM_GETTEXT_win32_WM_GETTEXT and WM_GETDLGCODE_win32_WM_GETDLGCODE, that it sends to controls. These messages typically correspond to window-management functions that carry out actions on windows. In general, the window procedure for an application-defined control should process any predefined control message in the following table if the message affects the operation of the control.
Message Recommendation
WM_GETDLGCODE_win32_WM_GETDLGCODE Process if the control uses the ENTER, ESC, TAB, or arrow keys. The IsDialogMessage_win32_IsDialogMessage function sends this message to controls in a dialog box to determine whether to process the keys or pass them to the control.
WM_GETFONT_win32_WM_GETFONT Process if the WM_SETFONT_win32_WM_SETFONT message is also processed.
WM_GETTEXT_win32_WM_GETTEXT Process if the control text is not the same as the title specified by the CreateWindowEx_win32_CreateWindowEx function.
WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH Process if the control text is not the same as the title specified by the CreateWindowEx function.
WM_KILLFOCUS_win32_WM_KILLFOCUS Process if the control displays a caret, a focus rectangle, or another item to indicate that it has the input focus.
WM_SETFOCUS_win32_WM_SETFOCUS Process if the control displays a caret, a focus rectangle, or another item to indicate that it has the input focus.
WM_SETTEXT_win32_WM_SETTEXT Process if the control text is not the same as the title specified by the CreateWindowEx_win32_CreateWindowEx function.
WM_SETFONT_win32_WM_SETFONT Process if the control displays text. Windows sends this message when creating a dialog box that has the DS_SETFONT style.

Application-defined control messages are specific to the given control and must be explicitly sent to the control by using a SendMessage_win32_SendMessage or SendDlgItemMessage_win32_SendDlgItemMessage function. The numeric value for each message must be unique and must not conflict with the values of other window messages. To ensure that application-defined message values do not conflict, an application should create each value by adding a unique number to the WM_USER_win32_WM_USER value.
+$#A Control Messages
Following are the messages used with controls.
WM_GETFONT_win32_WM_GETFONTWM_SETFONT_win32_WM_SETFONTWM_SETTEXT_win32_WM_SETTEXT

+$#AK Dialog Boxes
In Microsoft® Windows®, a dialog box is a temporary window an application creates to retrieve user input. An application typically uses dialog boxes to prompt the user for additional information for commands. A dialog box usually contains one or more controls (child windows) with which the user enters text, chooses options, or directs the action of the command.
+$#A About Dialog Boxes
Windows provides many functions, messages, and predefined controls to help create and manage dialog boxes, thus making it easier to develop the user interface for an application. This overview describes the dialog box functions and messages and explains how to use them to create and use dialog boxes.
Windows also provides many predefined, or "common," dialog boxes that support commands, such as File Open and File Print. Applications that use these commands should use the common dialog boxes to prompt for the same user input, regardless of the type of application carrying out the commands. For more information about using common dialog boxes in your applications, see Common Dialog Box Library_win32_Common_Dialog_Box_Library.
+$#A When to Use a Dialog Box
Most applications use dialog boxes to prompt for additional information for commands that require user input. Using a dialog box is the only recommended way for an application to retrieve the input. For example, the File Open command requires the name of a file to open, so an application should use a dialog box to prompt the user for the name. In such cases, the application creates the dialog box when the user chooses the command and destroys the dialog box immediately after the user supplies the information.
Many applications also use dialog boxes to display information or options while the user works in another window. For example, word processing applications often use a dialog box with a text-search command. While the application searches for the text, the dialog box remains on the screen. The user can then return to the dialog box and search for the same word again; or the user can change the entry in the dialog box and search for a new word. Applications that use dialog boxes in this way typically create one when the user chooses a command and continue to display it for as long as the application runs or until the user explicitly closes the dialog box.
To support the different ways applications use dialog boxes, Windows provides two types of dialog box: modal and modeless. A modal dialog box requires the user to supply information or cancel the dialog box before allowing the application to continue. Applications use modal dialog boxes in conjunction with commands that require additional information before they can proceed. A modeless dialog box allows the user to supply information and return to the previous task without closing the dialog box. Modal dialog boxes are simpler to manage than modeless dialog boxes because they are created, perform their task, and are destroyed by calling a single function.
To create either a modal or modeless dialog box, an application must supply a dialog box template to describe the dialog box style and content; the application must also supply a dialog box procedure to carry out tasks. The dialog box template is a binary description of the dialog box and the controls it contains. The developer can create this template as a resource to be loaded from the application's executable file, or created in memory while the application runs. The dialog box procedure is an application-defined callback function that Windows calls when it has input for the dialog box or tasks for the dialog box to carry out. Although a dialog box procedure is similar to a window procedure, it does not have the same responsibilities.
An application typically creates a dialog box by using either the DialogBox_win32_DialogBox or CreateDialog_win32_CreateDialog function. DialogBox creates a modal dialog box; CreateDialog creates a modeless dialog box. These two functions load a dialog box template from the application's executable file and create a pop-up window that matches the template's specifications. There are other functions that create a dialog box by using templates in memory and pass additional information to the dialog box procedure as the dialog box is created.
Dialog boxes usually belong to a predefined, exclusive window class. Windows uses this window class and its corresponding window procedure for both modal and modeless dialog boxes. When the function is called, it creates the window for the dialog box, as well as the windows for the controls in the dialog box, then sends selected messages to the dialog box procedure. While the dialog box is visible, the predefined window procedure manages all messages, processing some messages and passing others to the dialog box procedure so that the procedure can carry out tasks. Applications do not have direct access to the predefined window class or window procedure, but they can use the dialog box template and dialog box procedure to modify the style and behavior of a dialog box.
+$#A Owner Window
Most dialog boxes have an owner window (or more simply, an owner). When creating the dialog box, the application sets the owner by specifying the owner's window handle. Windows uses the owner to determine the position of the dialog box in the Z order so that the dialog box is always positioned above its owner. Also, Windows can send messages to the window procedure of the owner, notifying it of events in the dialog box.
Windows automatically hides or destroys the dialog box whenever its owner is hidden or destroyed. This means the dialog box procedure requires no special processing to detect changes to the state of the owner window.
Because the typical dialog box is used in conjunction with a command in a menu, the owner window is usually the window containing the menu. Although it is possible to create a dialog box that has no owner, it is not recommended. For example, when a modal dialog box has no owner, Windows does not disable any of the application's other windows and allows the user to continue to carry out work in the other windows, defeating the purpose of the modal dialog box.
When a modeless dialog box has no owner, Windows neither hides nor destroys the dialog box when other windows in the application are hidden or destroyed. Although this does not defeat the purpose of the modeless dialog box, it requires that the application carry out special processing to ensure the dialog box is hidden and destroyed at appropriate times.
+$#A Message Boxes
A message box is a special dialog box that an application can use to display messages and prompt for simple input. A message box typically contains a text message and one or more buttons. An application creates the message box by using the MessageBox_win32_MessageBox or MessageBoxEx_win32_MessageBoxEx function, specifying the text and the number and types of buttons to display. The MessageBoxEx function also allows you to specify the language to use for the text of any predefined push buttons in the message box.
Although the message box is a dialog box, Windows takes complete control of the creation and management of the message box. This means the application does not provide a dialog box template and dialog box procedure. Windows creates its own template based on the text and buttons specified for the message box and supplies its own dialog box procedure.
A message box is a modal dialog box and Windows creates it by using the same internal functions that DialogBox_win32_DialogBox uses. If the application specifies an owner window when calling MessageBox or MessageBoxEx, Windows disables the owner. An application can also direct Windows to disable all top-level windows belonging to the current task by specifying the MB_TASKMODAL value when creating the dialog box.
Windows can send messages to the owner, such as WM_CANCELMODE_win32_WM_CANCELMODE and WM_ENABLE_win32_WM_ENABLE, just as it does when creating a modal dialog box. The owner window should carry out any actions requested by these messages.
+$#A Modal Dialog Boxes
A modal dialog box should be a pop-up window having a System menu, a title bar, and a thick border; that is, the dialog box template should specify the WS_POPUP, WS_SYSMENU, WS_CAPTION, and DS_MODALFRAME styles. Although an application can designate the WS_VISIBLE style, Windows always displays a modal dialog box regardless of whether the dialog box template specifies the WS_VISIBLE style. An application must not create a modal dialog box having the WS_CHILD style. A modal dialog box with this style disables itself, preventing any subsequent input from reaching the application.
An application creates a modal dialog box by using either the DialogBox_win32_DialogBox or DialogBoxIndirect_win32_DialogBoxIndirect function. DialogBox requires the name or identifier of a resource containing a dialog box template; DialogBoxIndirect requires the handle of a memory object containing a dialog box template. The DialogBoxParam_win32_DialogBoxParam and DialogBoxIndirectParam_win32_DialogBoxIndirectParam functions also create modal dialog boxes; they are identical to the previously mentioned functions but pass a specified parameter to the dialog box procedure when the dialog box is created.
When creating the modal dialog box, Windows makes it the active window. The dialog box remains active until the dialog box procedure calls the EndDialog_win32_EndDialog function or Windows activates a window in another application. Neither the user nor the application can make the owner window active until the modal dialog box is destroyed.
When the owner window is not already disabled, Windows automatically disables the window and any child windows belonging to it when it creates the modal dialog box. The owner window remains disabled until the dialog box is destroyed. Although a dialog box procedure could potentially enable the owner window at any time, enabling the owner defeats the purpose of the modal dialog box and is not recommended. When the dialog box procedure is destroyed, Windows enables the owner window again, but only if the modal dialog box caused the owner to be disabled.
As Windows creates the modal dialog box, it sends the WM_CANCELMODE_win32_WM_CANCELMODE message to the window (if any) currently capturing mouse input. An application that receives this message should release the mouse capture so that the user can move the mouse in the modal dialog box. Because Windows disables the owner window, all mouse input is lost if the owner fails to release the mouse upon receiving this message.
To process messages for the modal dialog box, Windows starts its own message loop, taking temporary control of the message queue for the entire application. When Windows retrieves a message that is not explicitly for the dialog box, it dispatches the message to the appropriate window. If it retrieves a WM_QUIT_win32_WM_QUIT message, it posts the message back to the application message queue so that the application's main message loop can eventually retrieve the message.
Windows sends the WM_ENTERIDLE_win32_WM_ENTERIDLE message to the owner window whenever the application message queue is empty. The application can use this message to carry out a background task while the dialog box remains on the screen. When an application uses the message in this way, the application must frequently yield control (for example, by using the PeekMessage_win32_PeekMessage function) so that the modal dialog box can receive any user input. To prevent the modal dialog box from sending the WM_ENTERIDLE messages, the application can specify the DS_NOIDLEMSG style when creating the dialog box.
An application destroys a modal dialog box by using the EndDialog_win32_EndDialog function. In most cases, the dialog box procedure calls EndDialog when the user chooses the Close command from the dialog box's System menu or chooses the OK or Cancel button in the dialog box. The dialog box can return a value through the DialogBox_win32_DialogBox function (or other creation functions) by specifying a value when calling the EndDialog function. Windows returns this value after destroying the dialog box. Most applications use this return value to determine whether the dialog box completed its task successfully or was canceled by the user. Windows does not return control from the function that creates the dialog box until the dialog box procedure has called the EndDialog function.
+$#A Modeless Dialog Boxes
A modeless dialog box should be a pop-up window having a System menu, a title bar, and a thin border; that is, the dialog box template should specify the WS_POPUP, WS_CAPTION, WS_BORDER, and WS_SYSMENU styles. Windows does not automatically display the dialog box unless the template specifies the WS_VISIBLE style.
An application creates a modeless dialog box by using the CreateDialog_win32_CreateDialog or CreateDialogIndirect_win32_CreateDialogIndirect function. CreateDialog requires the name or identifier of a resource containing a dialog box template; CreateDialogIndirect requires the handle of a memory object containing a dialog box template. Two other functions, CreateDialogParam_win32_CreateDialogParam and CreateDialogIndirectParam_win32_CreateDialogIndirectParam, also create modeless dialog boxes; they pass a specified parameter to the dialog box procedure when the dialog box is created.
CreateDialog and other creation functions return a window handle for the dialog box. The application and the dialog box procedure can use this handle to manage the dialog box. For example, if WS_VISIBLE is not specified in the dialog box template, the application can display the dialog box by passing the window handle to the ShowWindow_win32_ShowWindow function.
A modeless dialog box neither disables the owner window nor sends messages to it. When creating the dialog box, Windows makes it the active window, but the user or the application can change the active window at any time. If the dialog box does become inactive, it remains above the owner window in the Z order, even if the owner window is active.
The application is responsible for retrieving and dispatching input messages to the dialog box. Most applications use the main message loop for this. To permit the user to move to and select controls by using the keyboard, however, the application must call the IsDialogMessage_win32_IsDialogMessage function. For more information about this function, see Dialog Box Keyboard Interface_win32_Dialog_Box_Keyboard_Interface.
A modeless dialog box cannot return a value to the application as a modal dialog box does, but the dialog box procedure can send information to the owner window by using the SendMessage_win32_SendMessage function.
An application must destroy all modeless dialog boxes before terminating. It can destroy a modeless dialog box by using the DestroyWindow_win32_DestroyWindow function. In most cases, the dialog box procedure calls DestroyWindow in response to user input, such as choosing the Cancel button. If the user never closes the dialog box in this way, the application must call DestroyWindow.
DestroyWindow invalidates the window handle for the dialog box, so any subsequent calls to functions that use the handle return error values. To prevent errors, the dialog box procedure should notify the owner that the dialog box has been destroyed. Many applications maintain a global variable containing the handle for the dialog box. When the dialog box procedure destroys the dialog box, it also sets the global variable to NULL, indicating that the dialog box is no longer valid.
The dialog box procedure must not call the EndDialog_win32_EndDialog function to destroy a modeless dialog box.
+$#A Dialog Box Template
A dialog box template is binary data that describes the dialog box, defining its height, width, style, and the controls it contains. To create a dialog box, Windows either loads a dialog box template from the resources in the application's executable file or uses the template passed to it in global memory by the application. In either case, the application must supply a template when creating a dialog box.
A developer creates template resources by using a resource compiler or a dialog box editor. A resource compiler converts a text description into a binary resource, and a dialog box editor saves an interactively constructed dialog box as a binary resource.

Note An explanation of how to create template resources and add them to the application's executable file is beyond the scope of this book. For more information about creating template resources and adding them to an executable file, see the documentation provided with your application development tools.

Applications that create dialog boxes without using template resources must create templates in memory and pass the templates to either the DialogBoxIndirect_win32_DialogBoxIndirect or CreateDialogIndirect_win32_CreateDialogIndirect function. An application creates a memory template by allocating a global memory object and filling it with a DLGTEMPLATE_win32_DLGTEMPLATE_str structure and one or more DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_str structures. A memory template is identical in form and content to a template resource. Many applications that use memory templates first load a template resource into memory by using the LoadResource_win32_LoadResource function, then modify the loaded resource to create a new memory template.
The following sections describe the styles, measurements, and other values used in a dialog box template.
+$#A Dialog Box Template Styles
Every dialog box template specifies a combination of style values that define the appearance and features of the dialog box. The style values can be window styles, such as WS_POPUP and WS_SYSMENU, and dialog box styles, such as DS_MODALFRAME. The number and type of styles for a template depends on the type and purpose of the dialog box.
Windows passes all window styles given in the template to the CreateWindowEx_win32_CreateWindowEx function when creating the dialog box. Windows may pass one or more extended styles depending on the given dialog box styles. For example, when the template specifies DS_MODALFRAME, Windows uses WS_EX_DLGMODALFRAME when creating the dialog box. When the template specifies DS_SYSMODAL, Windows uses WS_EX_TOPMOST. All other dialog box styles affect how Windows manages the dialog box.
Most dialog boxes are pop-up windows that have a System menu and a title bar. Therefore, the typical template specifies the WS_POPUP, WS_SYSMENU, and WS_CAPTION styles. The template also specifies a border style: WS_BORDER for modeless dialog boxes, and DS_MODALFRAME for modal dialog boxes. A template may specify a window type other than pop-up (such as WS_OVERLAPPED) if it creates a customized window instead of a dialog box.
Windows always displays a modal dialog box regardless of whether the WS_VISIBLE style is given. When the template for a modeless dialog box specifies the WS_VISIBLE style, Windows automatically displays the dialog box when it is created. Otherwise, the application is responsible for displaying the dialog box by using the ShowWindow_win32_ShowWindow function.
The template can specify the DS_SETFOREGROUND style to force Windows to bring the dialog box to the foreground. This is especially useful for modal dialog boxes that require immediate attention from the user regardless of whether the owner window is the foreground window.
Using the DS_ABSALIGN style, Windows interprets dialog box measurements as screen coordinates; using the DS_SETFONT style, Windows uses a given font, instead of the system font, to draw text in the dialog box client area and in the controls in the dialog box; the DS_NOIDLEMSG style prevents a modal dialog box from sending WM_ENTERIDLE_win32_WM_ENTERIDLE messages to the owner window. These styles are described in more detail in later topics.
The DS_LOCALEDIT style does not apply to Win32-based applications.
Dialog boxes with the DS_SYSMODAL style receive the WS_EX_TOPMOST window style but no other special properties or styles; this means the user still has access to other windows on the desktop even though a DS_SYSMODAL dialog box may be displayed.
+$#A Dialog Box Measurements
Every dialog box template contains measurements that specify the position, width, and height of the dialog box and the controls it contains. These measurements are device independent, so an application can use a single template to create the same dialog box for all types of display devices. This ensures that a dialog box will have the same proportions and appearance on all screens despite differing resolutions and aspect ratios between screens.
Dialog box measurements are given in dialog base units. One horizontal unit is equal to one-fourth of the average character width for the system font. One vertical unit is equal to one-eighth of the average character height for the system font. An application can retrieve the number of pixels per base unit for the current display by using the GetDialogBaseUnits_win32_GetDialogBaseUnits function. An application can convert measurements from dialog base units to pixels by using the MapDialogRect_win32_MapDialogRect function.
The template must specify the initial coordinates of the upper left corner of the dialog box. Usually the coordinates are relative to the upper left corner of the owner window's client area. When the template specifies the DS_ABSALIGN style or the dialog box has no owner, the position is relative to the upper left corner of the screen. Windows sets this initial position when creating the dialog box, but permits an application to adjust the position before displaying the dialog box. For example, an application can retrieve the dimensions of the owner window, calculate a new position that centers the dialog box in the owner window, and then set the position by using the SetWindowPos_win32_SetWindowPos function.
The template should specify a dialog box width and height that does not exceed the width and height of the screen and ensures that all controls and are within the client area of the dialog box. Although Windows permits a dialog box to be any size, creating one that is too small or too large can prevent the user from providing input, defeating the purpose of the dialog box. Many applications use more than one dialog box when there are a large number of controls. In such cases, the initial dialog box usually contains one or more buttons that the user can choose to display the next dialog box.
+$#A Dialog Box Controls
The template specifies the position, width, height, style, identifier, and window class for each control in the dialog box. Windows creates each control by passing this data to the CreateWindowEx_win32_CreateWindowEx function. Controls are created in the order they are given in the template. The template should specify the appropriate number, type, and order of controls to ensure that the user can enter the input needed to complete the command associated with the dialog box.
For each control, the template specifies style values that define the appearance and operation of the control. Every control is a child window and therefore must have the WS_CHILD style. To ensure that the control is visible when the dialog box is displayed, each control must also have the WS_VISIBLE style. Other commonly used window styles are WS_BORDER for controls that have optional borders, WS_DISABLED for controls that should be disabled when the dialog box is initially created, and WS_TABSTOP and WS_GROUP for controls that can be accessed using the keyboard. The WS_TABSTOP and WS_GROUP styles are used in conjunction with the dialog keyboard interface described later in this topic.
The template may also specify control styles specific to the control's window class. For example, a template that specifies a button control must give a button control style such as BS_PUSHBUTTON or BS_CHECKBOX. Windows passes the control styles to the control window procedure through the WM_CREATE_win32_WM_CREATE message, allowing the procedure to adapt the appearance and operation of the control.
Windows converts the position coordinates and the width and height measurements from dialog base units to pixels, before passing these to CreateWindowEx. When Windows creates a control, it specifies the dialog box as the parent window. This means Windows always interprets the position coordinates of the control as client coordinates, relative to the upper left corner of the dialog box's client area.
The template specifies the window class for each control. A typical dialog box contains controls belonging to the predefined control window classes, such as the button and edit control window classes. In this case, the template specifies window classes by supplying the corresponding predefined atom values for the classes. When a dialog box contains a control belonging a custom control window class, the template gives the name of that registered window class or the atom value currently associated with the name.
Each control in a dialog box must have a unique identifier to distinguish it from other controls. Controls send information to the dialog box procedure through WM_COMMAND_win32_WM_COMMAND messages, so the control identifiers are essential for the procedure to determine which control sent a given message. The only exception to this rule are control identifiers for static controls. Static controls do not require unique identifiers because they send no WM_COMMAND messages.
To permit the user to close the dialog box, the template should specify at least one push button and give it the control identifier IDCANCEL. To permit the user to choose between completing or canceling the command associated with the dialog box, the template should specify two push buttons, labeled OK and Cancel, with control identifiers of IDOK and IDCANCEL, respectively.
A template also specifies optional text and creation data for a control. The text typically provides labels for button controls or specifies the initial content of a static text control. The creation data is one or more bytes of data that Windows passes to the control window procedure when creating the control. Creation data is useful for controls that require more information about their initial content or style than is given by other data. For example, an application can use creation data to set the initial setting and range for a scroll bar control.
+$#A System Menu
Windows gives a dialog box a System menu when the template specifies the WS_SYSMENU style. To prevent inappropriate command input, Windows automatically disables all commands in the menu except the Move and Close commands. The user can use the Move command to move the dialog box. When the user chooses the Close command, Windows sends a WM_COMMAND_win32_WM_COMMAND message to the dialog box procedure with the wParam parameter set to IDCANCEL. This is identical to the message sent by the Cancel button when the user chooses it. The recommended action for this message is to close the dialog box and cancel the requested command or task.
Although other menus in dialog boxes are not recommended, a dialog box template can specify a menu by supplying the identifier or the name of a menu resource. In this case, Windows loads the resource and creates the menu for the dialog box. Applications typically use menu identifiers or names in templates when using the templates to create custom windows rather than dialog boxes.
+$#A Dialog Box Fonts
Windows draws all text in dialog box using the system font by default. An application can direct Windows to use another font by setting the DS_SETFONT style for the dialog box and specifying a point size and typeface name. Although a dialog box template can specify a font, Windows always uses the system font for the title and menus of the dialog box; the DS_SETFONT style does not change this.
When the DS_SETFONT style is specified, the system sends a WM_SETFONT_win32_WM_SETFONT message to the dialog box procedure and to each control as it creates the control. The dialog box procedure is responsible for saving the font handle passed with the WM_SETFONT message and selecting the handle into the display device context whenever it writes text to the window. Predefined controls do this by default.
When the DS_SETFONT style is given, Windows uses the average character width of the font to calculate the position and dimensions of the dialog box. Otherwise, it uses the average character width of the system font.
+$#A Templates in Memory
A template in memory consists of a DLGTEMPLATE_win32_DLGTEMPLATE_str structure and related data followed by one or more DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_str structures and related data. There is one DLGITEMTEMPLATE structure for each control in the dialog box.
The DLGTEMPLATE structure specifies the position, width, height, style, and number of controls in the dialog box. Additional data following the structure specifies the optional menu, class, title, and font for the dialog box. The DLGTEMPLATE structure has the following form:
typedef struct { // dltt
DWORD style;
DWORD dwExtendedStyle;
WORD cdit;
WORD x;
WORD y;
WORD cx;
WORD cy;
} DLGTEMPLATE;

The style member specifies the window and dialog box styles for the dialog box. Windows does not use the dwExtendedStyle member when creating a dialog box. An application that uses templates to create other types of windows can use this member to specify extended window styles for the other windows. The x and y members specify the dialog box position; cx and cy specify the width and height. The cdit member specifies the number of controls in the dialog box and is the same as the number of DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_str structures in the template.
The DLGTEMPLATE_win32_DLGTEMPLATE_str structure is immediately followed by a 16-bit value that specifies whether the dialog box has a menu. When the value is 0x0000, the dialog box has no menu. When the value is 0xFFFF, the subsequent 16-bit value specifies the identifier of the menu resource that describes the dialog box's menu. When the initial value is neither 0x0000 nor 0xFFFF, Windows assumes it is the start of a null-terminated string specifying the name of the menu resource that describes the dialog box's menu.
The first 16-bit value following the menu identifier or name specifies the window class to which the dialog box belongs. When the value is 0x0000, Windows uses the predefined dialog box class for the dialog box. When the value is 0xFFFF, the subsequent 16-bit value is an atom value identifying a registered window class. When the initial value is neither 0x0000 nor 0xFFFF, Windows assumes it is the start of a null-terminated string identifying a registered window class.
The dialog box title immediately follows the class atom or name. The title is a null-terminated string. When the string is empty, Windows leaves the title bar for the dialog box blank. When the dialog box does not have the WS_CAPTION style, Windows sets the title to the given string but does not display it.
When the dialog box has the DS_SETFONT style, the dialog box title is immediately followed by a 16-bit value specifying the point size and by a null-terminated string specifying the typeface for the font. Windows uses this data to create a font for use when writing text in the dialog box client area and in each control. When the dialog box does not have the DS_SETFONT style, the 16-bit value and string are not part of the template.
Each DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_str structure in the template contains data specifying the position, width, height, style, and identifier of the control. Additional information follows each structure, specifying the window class to which the control belongs, the initial text for the control, and any other creation data needed to create the control. A DLGITEMTEMPLATE structure has the following form:
typedef struct { // dlit
DWORD style;
DWORD dwExtendedStyle;
WORD x;
WORD y;
WORD cx;
WORD cy;
WORD id;
} DLGITEMTEMPLATE;

The style member specifies the control style. This member is a combination of window-style flags, such as BS_PUSHBUTTON and WS_VISIBLE. Windows does not use the dwExtendedStyle member to create controls for dialog boxes. Applications that use templates to create other types of windows can use this member to specify extended window styles for those controls. The x and y members specify the coordinates of the upper left corner of the control; cx and cy specify the width and height of the control. The id member is the control identifier.
The control's window class is specified by an atom or name immediately following the DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_str structure. When the first 16-bit value after the structure is 0xFFFF, the subsequent 16-bit value is an atom value identifying a registered window class. Otherwise, the first value is the start of a null-terminated string specifying the name of a registered window class.
The control text starts on the first 16-bit boundary after the class atom or name. This text is either two 16-bit values specifying a resource identifier or a null- terminated string specifying the initial text for the control. If this member is a resource identifier, the first 16-bit value is 0xFFFF and the second is the identifier. Resource identifiers are usually given for controls, such as static icon controls, that load and display icons or other resources rather than displaying text.
Any additional creation data required by the control follows the control text. Windows passes this data to the control as the creation parameters when it calls the CreateWindowEx_win32_CreateWindowEx function.
+$#A Dialog Box Procedure
A dialog box procedure is similar to a window procedure in that Windows sends messages to the procedure when it has information to give or tasks to carry out. Unlike a window procedure, a dialog box procedure never calls the DefWindowProc_win32_DefWindowProc function. Instead, it returns the Boolean value TRUE if it processes a message or FALSE if it does not.
Every dialog box procedure has the following form:
BOOL APIENTRY DlgProc(hwndDlg, message, wParam, lParam)
HWND hwndDlg;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
switch (message) {

// Place message cases here.

default:
return FALSE;
}
}

The procedure parameters serve the same purpose as in a window procedure, with the hwndDlg parameter receiving the window handle of the dialog box.
Most dialog box procedures process the WM_INITDIALOG_win32_WM_INITDIALOG message and the WM_COMMAND_win32_WM_COMMAND messages sent by the controls, but process few if any other messages. If a dialog box procedure does not process a message, it must return FALSE to direct Windows to process the messages internally. The only exception to this rule is the WM_INITDIALOG message. The dialog box procedure must return TRUE to direct Windows to further process the WM_INITDIALOG message. In any case, the procedure must not call DefWindowProc_win32_DefWindowProc.
+$#A The WM_INITDIALOG Message
Windows does not send a WM_CREATE_win32_WM_CREATE message to the dialog box procedure. Instead, it sends a WM_INITDIALOG_win32_WM_INITDIALOG message when it creates the dialog box and all its controls but before it displays the dialog box. The procedure should carry out any initialization required to ensure that the dialog box displays the current settings associated with the command or task. For example, when a dialog box contains a control to show the current drive and directory, the procedure must determine the current drive and directory and set the control to that value.
The procedure can initialize controls by using functions such as SetDlgItemText_win32_SetDlgItemText and CheckDlgButton_win32_CheckDlgButton. Because controls are windows, the procedure can also manipulate them by using window-management functions such as EnableWindow_win32_EnableWindow and SetFocus_win32_SetFocus. The procedure can retrieve the window handle for a control by using the GetDlgItem_win32_GetDlgItem function.
The dialog box procedure can change the contents, state, and position of any control as needed. For example, in a dialog box that contains a list box of filenames and an Open button, the procedure can disable the Open button until the user selects a file from the list. In this example, the dialog box template specifies the WS_DISABLED style for the Open button and Windows automatically disables the button when creating it. When the dialog box procedure receives a notification message from the list box indicating that the user has selected a file, the procedure calls the EnableWindow function to enable the Open button.
If the application creates the dialog box by using one of the functions DialogBoxParam_win32_DialogBoxParam, DialogBoxIndirectParam_win32_DialogBoxIndirectParam, CreateDialogParam_win32_CreateDialogParam, or CreateDialogIndirectParam_win32_CreateDialogIndirectParam, the lParam parameter for the WM_INITDIALOG message contains the extra parameter passed to the function. Applications typically use this extra parameter to pass the address of additional initialization information to the dialog box procedure, but the dialog box procedure must determine the meaning of the parameter. If the application uses another function to create the dialog box, Windows sets the lParam parameter to NULL.
Before returning from the WM_INITDIALOG_win32_WM_INITDIALOG message, the procedure should determine whether it should set the input focus to a given control. If the dialog box procedure returns TRUE, Windows automatically sets the input focus to the control whose window handle is in the wParam parameter. If the control receiving the default focus is not appropriate, it can set the focus to the appropriate control by using the SetFocus function. If the procedure sets the input focus, it must return FALSE to prevent Windows from setting the default focus. The control receiving the default input focus is always the first control given in the template that is visible, not disabled, and has the WS_TABSTOP style. If no such control exists, Windows sets the default input focus to the first control in the template.
+$#A The WM_COMMAND Message
A control can send a WM_COMMAND_win32_WM_COMMAND message to the dialog box procedure when the user carries out an action in the control. These messages, called notification messages, inform the procedure of user input and permit it to carry out appropriate responses.
All predefined controls, except static controls, send notification messages for selected user actions. For example, a push button sends the BN_CLICKED_win32_BN_CLICKED notification message whenever the user chooses the button. In all cases, the low-order word of the wParam parameter contains the control identifier, the high-order word of wParam contains the notification code, and the lParam parameter contains the control window handle.
The dialog box procedure should monitor and process notification messages. In particular, the procedure must process messages having the IDOK or IDCANCEL identifiers; these messages represent a request by the user to close the dialog box. The procedure should close the dialog box by using the EndDialog_win32_EndDialog function for modal dialog boxes and the DestroyWindow_win32_DestroyWindow function for modeless dialog boxes.
Windows also sends WM_COMMAND messages to the dialog box procedure if the dialog box has a menu, such as the System menu, and the user chooses a command. In particular, Windows sends a WM_COMMAND message with the wParam parameter set to IDCANCEL whenever the user chooses the Close command in the dialog box's System menu. The message is nearly identical to the notification message sent by the Cancel button and should be processed in exactly the same way.
+$#A The WM_PARENTNOTIFY Message
A control sends a WM_PARENTNOTIFY_win32_WM_PARENTNOTIFY message whenever the user presses a mouse button while pointing to the control. Some applications interpret this message as a signal to carry out an action related to the control, such as displaying a line of text describing the purpose of the control.
Windows also sends WM_PARENTNOTIFY messages when it creates and destroys a window, but not for controls created from a dialog box template. Windows prevents these messages by specifying the WS_EX_NOPARENTNOTIFY style when creating the controls. An application cannot override this default behavior unless it creates its own controls for the dialog box.
+$#A Control-Color Messages
Controls and Windows can send control-color messages when they want the dialog box procedure to paint the background of a control or other window by using a specific brush and colors. This can be useful when applications override the default colors used in dialog boxes and their controls. Following are the control-color messages, which have replaced the WM_CTLCOLOR message.
WM_CTLCOLORBTN_win32_WM_CTLCOLORBTNWM_CTLCOLORDLG_win32_WM_CTLCOLORDLGWM_CTLCOLOREDIT_win32_WM_CTLCOLOREDITWM_CTLCOLORLISTBOX_win32_WM_CTLCOLORLISTBOXWM_CTLCOLORSCROLLBAR_win32_WM_CTLCOLORSCROLLBARWM_CTLCOLORSTATIC_win32_WM_CTLCOLORSTATIC
A control sends a control-color message to the dialog box procedure just before it paints its own background. The message allows the procedure to specify which brush to use and to set the background and foreground colors. The procedure specifies a brush by returning the brush handle. To set the background and foreground colors, the procedure uses the SetBkColor_win32_SetBkColor and SetTextColor_win32_SetTextColor functions with the control's display device context. The control-color message passes a handle of the display device context to the procedure in the message's wParam parameter.
Windows sends a WM_CTLCOLORDLG message to the dialog box procedure if the procedure does not process the WM_ERASEBKGND_win32_WM_ERASEBKGND message. The predefined dialog box class does not have a class background brush, so this message lets the procedure define its own background without having to include the code to carry out the work.
In any case, when a dialog box procedure does not process a control-color message, Windows uses a brush with the default window color to paint the background for all controls and windows except scroll bars. An application can retrieve the default window color by passing the COLOR_WINDOW value to the GetSysColor_win32_GetSysColor function. While the background is painted, the foreground color for the display device context is set to the default text color (COLOR_WINDOWTEXT). For scroll bars, Windows uses a brush having the default scroll bar color (COLOR_SCROLLBAR). In this case, the background and foreground colors for the display device context are set to white and black, respectively.
+$#A Dialog Box Default Message Processing
The window procedure for the predefined dialog box class carries out default processing for all messages that the dialog box procedure does not process. When the dialog box procedure returns FALSE for any message, the predefined window procedure checks the messages and carries out the following default actions:
Message Default action
DM_GETDEFID_win32_DM_GETDEFID Returns the control identifier of the default push button if it exists. Otherwise, returns zero.
DM_SETDEFID_win32_DM_SETDEFID Sets the control specified by the control identifier in the wParam parameter to the default push button.
WM_ACTIVATE_win32_WM_ACTIVATE Restores the input focus to the control identified by the previously saved handle if the dialog box is activated. Otherwise, the procedure saves the handle of the control having the input focus.
WM_CHARTOITEM_win32_WM_CHARTOITEM Returns zero.
WM_CLOSE_win32_WM_CLOSE Posts the BN_CLICKED_win32_BN_CLICKED notification message to the dialog box, specifying IDCANCEL as the control identifier. If the dialog box has an IDCANCEL control identifier and the control is currently disabled, the procedure sounds a warning and does not post the message.
WM_COMPAREITEM_win32_WM_COMPAREITEM Returns zero.
WM_ERASEBKGND_win32_WM_ERASEBKGND Fills the dialog box client area by using either the brush returned from the WM_CTLCOLORDLG_win32_WM_CTLCOLORDLG message or with the default window color.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the application-defined dialog box font.
WM_INITDIALOG_win32_WM_INITDIALOG Returns zero.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Sends a CB_SHOWDROPDOWN_win32_CB_SHOWDROPDOWN message to the combo box having the input focus, directing the control to hide its drop-down list box. The procedure calls DefWindowProc_win32_DefWindowProc to complete the default action.
WM_NCDESTROY_win32_WM_NCDESTROY Releases global memory allocated for edit controls in the dialog box (applies to dialog boxes in Windows-based applications that specify the DS_LOCALEDIT style) and frees any application-defined font (applies to dialog boxes that specify the DS_SETFONT style). The procedure calls the DefWindowProc function to complete the default action.
WM_NCLBUTTONDOWN_win32_WM_NCLBUTTONDOWN Sends a CB_SHOWDROPDOWN_win32_CB_SHOWDROPDOWN message to the combo box having the input focus, directing the control to hide its drop-down list box. The procedure calls DefWindowProc_win32_DefWindowProc to complete the default action.
WM_NEXTDLGCTL_win32_WM_NEXTDLGCTL Sets the input focus to the next or previous control in the dialog box, to the control identified by the handle in the wParam parameter, or to the first control in the dialog box that is visible, not disabled, and has the WS_TABSTOP style. The procedure ignores this message if the current window with the input focus is not a control.
WM_SETFOCUS_win32_WM_SETFOCUS Sets the input focus to the control identified by a previously saved control window handle. If no such handle exists, the procedure sets the input focus to the first control in the dialog box template that is visible, not disabled, and has the WS_TABSTOP style. If no such control exists, the procedure sets the input focus to the first control in the template.
WM_SHOWWINDOW_win32_WM_SHOWWINDOW Saves the handle of the control having the input focus if the dialog box is being hidden, then calls DefWindowProc_win32_DefWindowProc to complete the default action.
WM_SYSCOMMAND_win32_WM_SYSCOMMAND Saves the handle of the control having the input focus if the dialog box is being minimized, then calls DefWindowProc to complete the default action.
WM_VKEYTOITEM_win32_WM_VKEYTOITEM Returns zero.

The predefined window procedure passes all other messages to DefWindowProc_win32_DefWindowProc for default processing.
+$#A Dialog Box Keyboard Interface
Windows provides a special keyboard interface for dialog boxes that carries out special processing for several keys. The interface generates messages that correspond to certain buttons in the dialog box or changes the input focus from one control to another. Following are the keys used in this interface and their respective actions.
Key Action
ALT+mnemonic Moves the input focus to the first control (having the WS_TABSTOP style) after the static control containing the given mnemonic.
DOWN Moves the input focus to the next control in the group.
ENTER Sends a WM_COMMAND_win32_WM_COMMAND message to the dialog box procedure. The wParam parameter is set to IDOK or control identifier of the default push button.
ESC Sends a WM_COMMAND message to the dialog box procedure. The wParam parameter is set to IDCANCEL.
LEFT Moves the input focus to the previous control in the group.
mnemonic Moves the input focus to the first control (having the WS_TABSTOP style) after the static control containing the given mnemonic.
RIGHT Moves the input focus to the next control in the group.
SHIFT+TAB Moves the input focus to the previous control that has the WS_TABSTOP style.
TAB Moves the input focus to the next control that has the WS_TABSTOP style.
UP Moves the input focus to the previous control in the group.

The Win32 API automatically provides the keyboard interface for all modal dialog boxes. It does not provide the interface for modeless dialog boxes unless the application calls the IsDialogMessage_win32_IsDialogMessage function to filter messages in its main message loop. This means that the application must pass the message to IsDialogMessage immediately after retrieving the message from the message queue. The function processes the messages if it is for the dialog box and returns a nonzero value to indicate that the message has been processed and must not be passed to the TranslateMessage_win32_TranslateMessage or DispatchMessage_win32_DispatchMessage function.
Because the dialog box keyboard interface uses direction keys to move between controls in a dialog box, an application cannot use these keys to scroll the contents of any modal dialog box or any modeless dialog box for which IsDialogMessage is called. When a dialog box has scroll bars, the application must provide an alternate keyboard interface for the scroll bars. Note that the mouse interface for scrolling is available when the system includes a mouse.
+$#A The WS_TABSTOP Style
The TAB key and SHIFT+TAB keys have no effect when the controls in the dialog box do not have the WS_TABSTOP style. Windows looks for this style as it searches for the next control in the dialog box to receive the input focus.
When the user presses TAB or SHIFT+TAB, Windows first determines whether the control having the input focus processes these keys. It sends the control a WM_GETDLGCODE_win32_WM_GETDLGCODE message, and if the control returns DLGC_WANTTAB, Windows passes the keys to the control. Otherwise, Windows uses the GetNextDlgTabItem_win32_GetNextDlgTabItem function to locate the next control that is visible, not disabled, and that has the WS_TABSTOP style. The search starts with the control currently having the input focus and proceeds in the order in which the controls were created ¾ that is, the order in which they are defined in the dialog box template. When the system locates a control having the required characteristics, Windows moves the input focus to it.
An application can also use GetNextDlgTabItem to locate controls having the WS_TABSTOP style. The function retrieves the window handle of the next or previous control having the WS_TABSTOP style without moving the input focus.
+$#A The WS_GROUP Style
By default, Windows moves the input focus to the next or previous control whenever the user presses a direction key. As long as the current control with the input focus does not process these keys and the next or previous control is not a static control, Windows continues to move the input focus through all controls in the dialog box as the user continues to press the direction keys.
An application can use the WS_GROUP style to modify this default behavior. The style marks the beginning of a group of controls. If a control in the group has the input focus when the user begins pressing direction keys, the focus remains in the group. In general, the first control in a group must have the WS_GROUP style and all other controls in the group must not have this style. All controls in the group must be contiguous ¾ that is, they must have been created consecutively with no intervening controls.
When the user presses a direction key, Windows first determines whether the current control having the input focus processes the direction keys. Windows sends a WM_GETDLGCODE_win32_WM_GETDLGCODE message to the control and if the control returns the DLGC_WANTARROWS value, passes the key to the control. Otherwise, Windows uses the GetNextDlgGroupItem_win32_GetNextDlgGroupItem function to determine the next control in the group.
GetNextDlgGroupItem searches controls in the order (or reverse order) they were created. If the user presses the RIGHT or DOWN key, GetNextDlgGroupItem returns the next control if that control does not have WS_GROUP style. Otherwise, the function reverses the order of the search and returns the first control that has the WS_GROUP style. If the user presses the LEFT or UP key, the function returns the previous control unless the current control already has the WS_GROUP style. If the current control has this style, the function reverses the order of the search, locates the first control having the WS_GROUP style, and returns the control that immediately precedes the located control.
Once Windows has the next or previous control, it sends a WM_GETDLGCODE message to the control to determine the control type. Windows then moves the input focus to control if it is not a static control. If the control is an automatic radio button, Windows sends a BM_CLICK_win32_BM_CLICK message to it. An application can also use GetNextDlgGroupItem to locate controls in a group.
Generally, the first control in the group combines the WS_GROUP and WS_TABSTOP styles so that the user can move from group to group by using the TAB key. If the group contains radio buttons, the application should apply the WS_TABSTOP style only to the first control in the group. Windows automatically moves the style when the user moves between controls in the group. This ensures that the input focus will always be on the most recently selected control when the user moves to the group using the TAB key.
+$#A Mnemonics
A mnemonic is a selected letter or digit in the label of a button or in the text of a static control. Windows moves the input focus to the control associated with the mnemonic whenever the user either presses the key that corresponds to the mnemonic or presses this key and the ALT key in combination. Mnemonics provide a quick way for the user to move to a given control by using the keyboard.
An application creates a mnemonic for a control by inserting the ampersand (&) immediately before the selected letter or digit in the label or text for the control. In most cases, the null-terminated string provided with the control in the dialog box template contains the ampersand. However, an application can create a mnemonic at any time by replacing a control's existing label or text by using the SetDlgItemText_win32_SetDlgItemText function. Only one mnemonic can be given for each control. Although it is recommended, mnemonics in a dialog box need not be unique.
When the user presses a letter or digit key, Windows first determines whether the current control having the input focus processes the key. Windows sends a WM_GETDLGCODE_win32_WM_GETDLGCODE message to the control, and if the control returns the DLGC_WANTALLKEYS or DLG_WANTMESSAGE value, Windows passes the key to the control. Otherwise, it searches for a control whose mnemonic matches the given letter or digit. It continues to search until it locates a control or has examined all controls. During the search, it skips any static controls that have the SS_NOPREFIX style.
If Windows locates a static control and the control is not disabled, Windows moves the input focus to the first control after the static control that is visible, not disabled, and that has the WS_TABSTOP style. If Windows locates some other control that has a matching mnemonic, it moves the input focus to that control. If the control is a default push button, Windows sends a BN_CLICKED_win32_BN_CLICKED notification message to the dialog box procedure. If the control is another style of button and there is no other control in the dialog box having the same mnemonic, Windows sends the BM_CLICK_win32_BM_CLICK message to the control.
+$#A Dialog Box Settings
The dialog box settings are the current selections and values for the controls in the dialog box. The dialog box procedure is responsible for initializing the controls to these settings when creating the dialog box. It is also responsible for retrieving the current settings from the controls before destroying the dialog box. The methods used to initialize and retrieve settings depend on the type of control.
+$#A Radio Buttons and Check Boxes
Dialog boxes use radio buttons and check boxes to let the user choose from a list of options. Radio buttons let the user choose from mutually exclusive options; check boxes let the user pick a combination of options.
The dialog box procedure can set the initial state of a check box by using the CheckDlgButton_win32_CheckDlgButton function, which sets or clears the check box. For radio buttons in a group of mutually exclusive radio buttons, the dialog box procedure can use the CheckRadioButton_win32_CheckRadioButton function to set the appropriate radio button and automatically clear any other radio button.
Before a dialog box terminates, the dialog box procedure can check the state of each radio button and check box by using the IsDlgButtonChecked_win32_IsDlgButtonChecked function, which returns the current state of the button. A dialog box typically saves this information to initialize the buttons the next time it creates the dialog box.
+$#A Dialog Box Edit Controls
Many dialog boxes have edit controls that let the user supply text as input. Most dialog box procedures initialize an edit control when the dialog box first starts. For example, the dialog box procedure may place a proposed filename in the control that the user can then select, modify, or replace. The dialog box procedure can set the text in an edit control by using the SetDlgItemText_win32_SetDlgItemText function, which copies text from a given buffer to the edit control. When the edit control receives the input focus, it automatically selects the complete text for editing.
Because edit controls do not automatically return their text to the dialog box, the dialog box procedure must retrieve the text before it terminates. It can retrieve the text by using the GetDlgItemText_win32_GetDlgItemText function, which copies the edit control text to a buffer. The dialog box procedure typically saves this text to initialize the edit control later or passes it on to the parent window for processing.
Some dialog boxes use edit controls that let the user enter numbers. The dialog box procedure can retrieve a number from an edit control by using the GetDlgItemInt_win32_GetDlgItemInt function, which retrieves the text from the edit control and converts the text to a decimal value. The user types the number in decimal digits. It can be either signed or unsigned. The dialog box procedure can display an integer by using the SetDlgItemInt_win32_SetDlgItemInt function. SetDlgItemInt converts a signed or unsigned integer to a string of decimal digits.
+$#A List Boxes, Combo Boxes, and Directory Listings
Some dialog boxes display lists of names from which the user can select one or more names. To display a list of filenames, for example, a dialog box typically uses a list box and the DlgDirList_win32_DlgDirList and DlgDirSelect_win32_DlgDirSelect functions. The DlgDirList function automatically fills a list box with the filenames in the current directory. The DlgDirSelect function retrieves the selected filename from the list box. Together, these two functions provide a convenient way for a dialog box to display a directory listing so the user can select a file without having to type its name and location.
A dialog box can also use a combo box to display a list of filenames. The DlgDirListComboBox_win32_DlgDirListComboBox function automatically fills a list box portion of the combo box with the filenames in the current directory. The DlgDirSelectComboBox_win32_DlgDirSelectComboBox function retrieves a selected filename from the list box portion.
+$#A Dialog Box Control Messages
Many controls recognize predefined messages that, when received by controls, cause them to carry out some action. For example, the BM_SETCHECK_win32_BM_SETCHECK message sets the check in a check box and the EM_GETSEL_win32_EM_GETSEL message retrieves the portion of the control's text that is currently selected. The control messages give a dialog procedure greater and more flexible access to the controls than the standard functions, so they are often used when the dialog box requires complex interactions with the user.
A dialog box procedure can send a message to a control by supplying the control identifier and using the SendDlgItemMessage_win32_SendDlgItemMessage function, which is identical to the SendMessage_win32_SendMessage function, except that it uses a control identifier instead of a window handle to identify the control that is to receive the message. A given message may require that the dialog procedure send parameters with the message, and the message may have corresponding return values. The operation and requirements of each control message depends on the purpose of the message and the control that processes it.
For more information about the control messages, see Controls_win32_Controls.
+$#A Custom Dialog Boxes
An application can create custom dialog boxes by using an application-defined window class for the dialog boxes instead of using the predefined dialog box class. Applications typically use this method when a dialog box is their main window, but it is also useful for creating modal and modeless dialog boxes for applications that have standard overlapping windows.
The application-defined window class allows the application to define a window procedure for the dialog box and process messages before sending them to the dialog box procedure. It also lets the application define a class icon, a class background brush, and a class menu for the dialog box. The application must register the window class before attempting to create a dialog box and must provide the dialog box template with the atom value or name of the window class.
Many applications create a new dialog box class by first retrieving the class information for the predefined dialog box class, and passing it to the GetClassInfo_win32_GetClassInfo function, which fills a WNDCLASS_win32_WNDCLASS_str structure with the information. The application modifies individual members of the structure, such as the class name, brush, and icon, then registers the new class by using the RegisterClass_win32_RegisterClass function. If an application fills the WNDCLASS structure on its own, it must set the cbWndExtra member to the DLGWINDOWEXTRA, which is the number of extra bytes Windows requires for each dialog box. If an application also uses extra bytes for each dialog box, they must be beyond the extra bytes required by Windows.
The window procedure for the custom dialog box has the same parameters and requirements as any other window procedure. Unlike other window procedures, however, the window procedure for this dialog box should call the DefDlgProc_win32_DefDlgProc function instead of the DefWindowProc_win32_DefWindowProc function for any messages it does not process. DefDlgProc carries out the same default message processing as the window procedure for the predefined dialog box, which includes calling the dialog box procedure.
An application can also create custom dialog boxes by subclassing the window procedure of the predefined dialog box. The SetWindowLong_win32_SetWindowLong function lets an application set the address of the window procedure for a given window. The application may also attempt to subclass by using the SetClassLong_win32_SetClassLong function, but doing so affects all dialog boxes in the system, not just those belonging to the application.
Applications that create custom dialog boxes sometimes provide an alternate keyboard interface for the dialog boxes. For modeless dialog boxes, this may mean the application does not call the IsDialogMessage_win32_IsDialogMessage function and instead processes all keyboard input in the custom window procedure. In such cases, the application can use the WM_NEXTDLGCTL_win32_WM_NEXTDLGCTL message to minimize the code needed to move the input focus from one control to another. This message, when passed to DefDlgProc, moves the input focus to a specified control and updates the appearance of the controls, such as moving the default push button border or setting an automatic radio button.
+$#A Using Dialog Boxes
You use dialog boxes to display information and prompt for input from the user. Your application loads and initializes the dialog box, processes user input, and destroys the dialog box when the user finishes the task. The process for handling dialog boxes varies, depending on whether the dialog box is modal or modeless. A modal dialog box requires the user to close the dialog box before activating another window in the application. However, the user can activate windows in different applications. A modeless dialog box does not require an immediate response from the user. It is similar to a main window containing controls. The following sections discuss how to use both types of dialog boxes.
· Displaying a message box_win32_Displaying_a_Message_Box
· Creating a modal dialog box_win32_Creating_a_Modal_Dialog_Box
· Creating a modeless dialog box_win32_Creating_a_Modeless_Dialog_Box
· Initializing a dialog box_win32_Initializing_a_Dialog_Box
· Creating a template in memory_win32_Creating_a_Template_in_Memory

+$#A Displaying a Message Box
The simplest form of modal dialog box is the message box. Most applications use message boxes to warn the user of errors and to prompt for directions on how to proceed after an error has occurred. You create a message box by using the MessageBox_win32_MessageBox or MessageBoxEx_win32_MessageBoxEx function, specifying the message and the number and type of buttons to display. Windows creates a modal dialog box, providing its own dialog box template and procedure. After the user closes the message box, MessageBox or MessageBoxEx returns a value identifying the button chosen by the user to close the message box.
In the following example, the application displays a message box if the fError variable is TRUE. The message box displays the message describing the error. The MB_OKCANCEL style directs MessageBox to provide two buttons with which the user can choose how to proceed:
if (fError) {
if (MessageBox(hwndDlg, SZNOTFOUND, SZDELETEITEM,
MB_OKCANCEL)==IDOK)

.
. // Prompt for a new item name and repeat the command.
.

else

.
. // Cancel the command.
.
}

In this example, SZNOTFOUND and SZDELETEITEM are application-defined, null-terminated strings that represent the message text and the title for the message box.
+$#A Creating a Modal Dialog Box
You create a modal dialog box by using the DialogBox_win32_DialogBox function. You must specify the identifier or name of a dialog box template resource and the address of the dialog box procedure. The DialogBox function loads the template, displays the dialog box, and processes all user input until the user closes the dialog box.
In the following example, the application displays a modal dialog box when the user chooses a Delete Item command from an application menu. The dialog box contains an edit control (in which the user enters the name of an item) and OK and Cancel buttons. The control identifiers for these controls are ID_ITEMNAME, IDOK, and IDCANCEL, respectively.
The first part of the example consists of the statements that create the modal dialog box. These statements, in the window procedure for the application's main window, create the dialog box when the system receives a WM_COMMAND_win32_WM_COMMAND message having the IDM_DELETEITEM command identifier. The second part of the example is the dialog box procedure, which retrieves the contents of the edit control and closes the dialog box upon receiving a WM_COMMAND message.
The following statements create the modal dialog box. The dialog box template is a resource in the application's executable file and has the resource identifier DLG_DELETEITEM:
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_DELETEITEM:
if (DialogBox(hinst,
MAKEINTRESOURCE(DLG_DELETEITEM),
hwnd, (DLGPROC)DeleteItemProc)==IDOK)

.
. // Complete the command; szItemName
. // contains the name of the item to
. // delete.
.

else
.
. // Cancel the command.
.

break;
}
return 0L;

In this example, the application identifies its main window as the owner window for the dialog box. When Windows initially displays the dialog box, its position is relative to the upper left corner of the owner window's client area. The application uses the return value from DialogBox_win32_DialogBox to determine whether to proceed with the command or cancel it. The following statements define the dialog box procedure.
char szItemName[80]; // receives name of item to delete.

BOOL CALLBACK DeleteItemProc(hwndDlg, message, wParam, lParam)
HWND hwndDlg;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
switch (message) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
if (!GetDlgItemText(hwndDlg, ID_ITEMNAME,
szItemName, 80))
*szItemName=0;

// Fall through.

case IDCANCEL:
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
return FALSE;
}

In this example, the procedure uses GetDlgItemText_win32_GetDlgItemText to retrieve the current text from the edit control identified by ID_ITEMNAME. The procedure then calls the EndDialog_win32_EndDialog function to set the dialog box's return value to either IDOK or IDCANCEL, depending on the message received, and to begin the process of closing the dialog box. The IDOK and IDCANCEL identifiers correspond to the OK and Cancel buttons. After the procedure calls EndDialog, Windows sends additional messages to the procedure to destroy the dialog box and returns the dialog box's return value back to the function that created the dialog box.
+$#A Creating a Modeless Dialog Box
You create a modeless dialog box by using the CreateDialog_win32_CreateDialog function, specifying the identifier or name of a dialog box template resource and the address of the dialog box procedure. CreateDialog loads the template, creates the dialog box, and optionally displays it. Your application is responsible for retrieving and dispatching user input messages to the dialog box procedure.
In the following example, the application displays a modeless dialog box ¾ if it is not already displayed ¾ when the user chooses a Go To command from an application menu. The dialog box contains an edit control, a check box, and OK and Cancel buttons. The dialog box template is a resource in the application's executable file and has the resource identifier DLG_GOTO. The user enters a line number in the edit control and checks the check box to specify that the line number is relative to the current line. The control identifiers are ID_LINE, ID_ABSREL, IDOK, and IDCANCEL.
The statements in the first part of the example create the modeless dialog box. These statements, in the window procedure for the application's main window, create the dialog box when the window procedure receives a WM_COMMAND_win32_WM_COMMAND message having the IDM_GOTO command identifier, but only if the global variable hwndGoto does not already contain a valid handle. The second part of the example is the application's main message loop. The loop includes the IsDialogMessage_win32_IsDialogMessage function to ensure that the user can use the dialog box keyboard interface in this modeless dialog box. The third part of the example is the dialog box procedure. The procedure retrieves the contents of the edit control and check box when the user chooses the OK button. The procedure destroys the dialog box when the user chooses the Cancel button.
HWND hwndGoto = NULL; // window handle of dialog box

.
.
.

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDM_GOTO:
if (!IsWindow(hwndGoto)) {
hwndGoto = CreateDialog(hinst,
MAKEINTRESOURCE(DLG_GOTO),
hwnd, (DLGPROC) GoToProc);
ShowWindow(hwndGoto, SW_SHOW);
}
break;
}
return 0L;

In the preceding statements, CreateDialog_win32_CreateDialog is called only if hwndGoto does not contain a valid window handle. This ensures that the application does not display two dialog boxes at the same time. To support this method of checking, the dialog procedure must set hwndGoto to NULL when it destroys the dialog box.
The message loop for an application consists of the following statements:
while (GetMessage(&msg, NULL, NULL, NULL)) {
if (!IsWindow(hwndGoto) || !IsDialogMessage(hwndGoto, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

The loop checks the validity of the window handle for the dialog box and only calls the IsDialogMessage_win32_IsDialogMessage function if the handle is valid. IsDialogMessage only processes the message if it belongs to the dialog box. Otherwise, it returns FALSE and the loop dispatches the message to the appropriate window.
The following statements define the dialog box procedure:
int iLine; // receives line number
BOOL fRelative; // receives check box status

.
.
.

BOOL CALLBACK GoToProc(hwndDlg, message, wParam, lParam)
HWND hwndDlg;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
BOOL fError;

switch (message) {
case WM_INITDIALOG:
CheckDlgButton(hwndDlg, ID_ABSREL, fRelative);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
fRelative = IsDlgButtonChecked(hwndDlg,
ID_ABSREL);
iLine = GetDlgItemInt(hwndDlg, ID_LINE,
&fError, fRelative);
if (fError) {
MessageBox(hwndDlg, SZINVALIDNUMBER,
SZGOTOERR, MB_OK);
SendDlgItemMessage(hwndDlg, ID_LINE,
EM_SETSEL, 0, -1L);
} else

.
. // Notify the owner window to carry
. // out the command.
.

return TRUE;

case IDCANCEL:
DestroyWindow(hwndDlg);
hwndGoto = NULL;
return TRUE;
}
}
return FALSE;
}

In the preceding statements, the procedure processes the WM_INITDIALOG_win32_WM_INITDIALOG and WM_COMMAND_win32_WM_COMMAND messages. During WM_INITDIALOG processing, the procedure initializes the check box by passing the current value of the global variable fRelative to CheckDlgButton_win32_CheckDlgButton. The procedure then returns TRUE to direct Windows to set the default input focus.
During WM_COMMAND processing, the procedure closes the dialog box only if the user chooses the Cancel button ¾ that is, the button having the IDCANCEL identifier. The procedure must call DestroyWindow_win32_DestroyWindow to close a modeless dialog box. Notice that the procedure also sets the hwndGoto variable to NULL to ensure that other statements that depend on this variable operate correctly.
If the user chooses the OK button, the procedure retrieves the current state of the check box and assigns it to the fRelative variable. It then uses the variable to retrieve the line number from the edit control. GetDlgItemInt_win32_GetDlgItemInt translates the text in the edit control into an integer. The value of fRelative determines whether the function interprets the number as a signed or unsigned value. If the edit control text is not a valid number, GetDlgItemInt sets the value of the fError variable to nonzero. The procedure checks this value to determine whether to display an error message or carry out the command. In the event of an error, the dialog box procedure sends a message to the edit control, directing it to select the text in the control so that the user can easily replace it. If GetDlgItemInt does not return an error, the procedure can either carry out the requested command itself or send a message to the owner window, directing it to carry out the command.
+$#A Initializing a Dialog Box
You initialize the dialog box and its contents while processing the WM_INITDIALOG_win32_WM_INITDIALOG message. The most common task is to initialize the controls to reflect the current dialog box settings. Another common task is to center a dialog box on the screen or within its owner window. A useful task for some dialog boxes is to set the input focus to a given control rather than accept the default input focus.
In the following example, the dialog box procedure centers the dialog box and sets the input focus while processing the WM_INITDIALOG message. To center the dialog box, the procedure retrieves the window rectangles for the dialog box and the owner window and calculates a new position for the dialog box. To set the input focus, the procedure checks the wParam parameter to determine the identifier of the default input focus:
HWND hwndOwner;
RECT rc, rcDlg, rcOwner;

case WM_INITDIALOG:

// Get the owner window and dialog box rectangles.

if ((hwndOwner = GetParent(hwndDlg)) == NULL)
hwndOwner = GetDesktopWindow();
GetWindowRect(hwndOwner, &rcOwner);
GetWindowRect(hwndDlg, &rcDlg);
CopyRect(&rc, &rcOwner);

// Offset the owner and dialog box rectangles so that
// right and bottom values represent the width and
// height, and then offset the owner again to discard
// space taken up by the dialog box.

OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
OffsetRect(&rc, -rc.left, -rc.top);
OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);

// The new position is the sum of half the remaining
// space and the owner's original position.

SetWindowPos(hwndDlg,
HWND_TOP,
rcOwner.left + (rc.right / 2),
rcOwner.top + (rc.bottom / 2),
0, 0, // ignores size arguments
SWP_NOSIZE);

if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME) {
SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME));
return FALSE;
}
return TRUE;

In the preceding statements, the procedure uses the GetParent_win32_GetParent function to retrieve the owner window handle for a dialog box. The function returns the owner window handle for dialog boxes, and the parent window handle for child windows. Because an application can create a dialog box that has no owner, the procedure checks the returned handle and uses the GetDesktopWindow_win32_GetDesktopWindow function to retrieve the desktop window handle, if necessary. After calculating the new position, the procedure uses the SetWindowPos_win32_SetWindowPos function to move the dialog box, specifying the HWND_TOP value to ensure that the dialog box remains on top of the owner window.
Before setting the input focus, the procedure checks the control identifier of the default input focus. Windows passes the window handle of the default input focus in the wParam parameter. The GetDlgCtrlID_win32_GetDlgCtrlID function returns the identifier for the control identified by the window handle. If the identifier does not match the correct identifier, the procedure uses the SetFocus_win32_SetFocus function to set the input focus. The GetDlgItem_win32_GetDlgItem function is required to retrieve the window handle of the desired control.
+$#A Creating a Template in Memory
Applications sometimes adapt or modify the content of dialog boxes depending on the current state of the data being processed. In such cases, it is not practical to provide all possible dialog box templates as resources in the application's executable file. But creating templates in memory gives the application more flexibility to adapt to any circumstances.
In the following example, the application creates a template in memory for a modal dialog box that contains a message and OK and Help buttons:
LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner,
LPSTR lpszMessage)
{
HGLOBAL hgbl;
LPDLGTEMPLATE lpdt;
LPDLGITEMTEMPLATE lpdit;
LPWORD lpw;
LPWSTR lpwsz;
LRESULT ret;

hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hgbl)
return -1;

lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);

// Define a dialog box.

lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
| DS_MODALFRAME | WS_CAPTION;
lpdt->cdit = 3; // number of controls
lpdt->cdit = 1;
lpdt->x = 10; lpdt->y = 10;
lpdt->cx = 100; lpdt->cy = 100;

lpw = (LPWORD) (lpdt + 1);
*lpw++ = 0; // no menu
*lpw++ = 0; // predefined dialog box class (by default)

lpwsz = (LPWSTR) lpw;
lstrcpyW(lpwsz, L"My Message"); // dialog title (Unicode)
lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);

// Define an OK button.

lpdit = (LPDLGITEMTEMPLATE) lpw;
lpdit->x = 10; lpdit->y = 10;
lpdit->cx = 40; lpdit->cy = 20;
lpdit->id = IDOK; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

lpw = (LPWORD) (lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // button class

lpwsz = (LPWSTR) lpw;
lstrcpyW(lpwsz, L"OK"); // button label (Unicode)
lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);
*lpw++ = 0; // no creation data

// Define a Help button.

lpdit = (LPDLGITEMTEMPLATE) lpw;
lpdit->x = 55; lpdit->y = 10;
lpdit->cx = 40; lpdit->cy = 20;
lpdit->id = ID_HELP; // Help button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;

lpw = (LPWORD) (lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // button class atom
lpwsz = (LPWSTR) lpw;
lstrcpyW(lpwsz, L"Help"); // button label (Unicode)
lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);
*lpw++ = 0; // no creation data

// Define a static text control.

lpdit = (LPDLGITEMTEMPLATE) lpw;
lpdit->x = 55; lpdit->y = 10;
lpdit->cx = 40; lpdit->cy = 20;
lpdit->id = ID_TEXT; // text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;

lpw = (LPWORD) (lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0082; // static class
for (lpwsz = (LPWSTR) lpw;
*lpwsz++ = (WCHAR) *lpszMessage++; // message
);
lpw = (LPWORD) lpwsz;
*lpw++ = 0; // no creation data
GlobalUnlock(hgbl);
ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
hwndOwner, (DLGPROC) DialogProc);
GlobalFree(hgbl);
return ret;
}

+$#AK Dialog Box Reference
The following functions, structures, and messages are associated with dialog boxes.
+$#A Dialog Box Functions
Following are the functions used to create and manage dialog boxes and controls within dialog boxes.
CreateDialog_win32_CreateDialogCreateDialogIndirect_win32_CreateDialogIndirectCreateDialogIndirectParam_win32_CreateDialogIndirectParamCreateDialogParam_win32_CreateDialogParamDefDlgProc_win32_DefDlgProcDialogBox_win32_DialogBoxDialogBoxIndirect_win32_DialogBoxIndirectDialogBoxIndirectParam_win32_DialogBoxIndirectParamDialogBoxParam_win32_DialogBoxParamDialogProc_win32_DialogProcEndDialog_win32_EndDialogGetDialogBaseUnits_win32_GetDialogBaseUnitsGetDlgCtrlID_win32_GetDlgCtrlIDGetDlgItem_win32_GetDlgItemGetDlgItemInt_win32_GetDlgItemIntGetDlgItemText_win32_GetDlgItemTextGetNextDlgGroupItem_win32_GetNextDlgGroupItemGetNextDlgTabItem_win32_GetNextDlgTabItemIsDialogMessage_win32_IsDialogMessageMapDialogRect_win32_MapDialogRectMessageBox_win32_MessageBoxMessageBoxEx_win32_MessageBoxExSendDlgItemMessage_win32_SendDlgItemMessageSetDlgItemInt_win32_SetDlgItemIntSetDlgItemText_win32_SetDlgItemTextMessageBoxIndirect_win32_MessageBoxIndirect
+$#A Dialog Box Structures
Following are the structures used to create and manage dialog boxes and controls within dialog boxes.
DLGITEMTEMPLATE_win32_DLGITEMTEMPLATE_strDLGTEMPLATE_win32_DLGTEMPLATE_strMSGBOXPARAMS_win32_MSGBOXPARAMS_str
+$#A Dialog Box Messages
Following are the messages used to create and manage dialog boxes and controls within dialog boxes.
WM_CTLCOLORMSGBOX_win32_WM_CTLCOLORMSGBOXWM_ENTERIDLE_win32_WM_ENTERIDLEWM_GETDLGCODE_win32_WM_GETDLGCODEWM_INITDIALOG_win32_WM_INITDIALOGWM_NEXTDLGCTL_win32_WM_NEXTDLGCTL

+$#AK Edit Controls
Microsoft® Windows® provides dialog boxes and controls to support communication between the application and the user. An edit control is a rectangular control window typically used in a dialog box to permit the user to enter and edit text from the keyboard.
+$#A About Edit Controls
An edit control is selected and receives the input focus when a user clicks the mouse inside it or presses the TAB key. After it is selected, the edit control displays its text (if any) and a flashing caret that indicates the insertion point. The user can then enter text, move the insertion point, or select text to be moved or deleted by using the keyboard or the mouse. An edit control can send notification messages to its parent window in the form of WM_COMMAND_win32_WM_COMMAND messages. For more information about messages from an edit control, see Edit Control Notification Messages_win32_Edit_Control_Notification_Messages. A parent window can send messages to an edit control in a dialog box by calling the SendDlgItemMessage_win32_SendDlgItemMessage function. Each of the messages sent to edit controls are discussed in this overview.
Windows provides both single line edit controls (sometime called SLEs) and multiline edit controls (sometimes called MLEs). Edit controls belong to the EDIT window class.
A combo box is a control that combines much of the functionality of an edit control and a list box. In a combo box, the edit control displays the current selection and the list box presents options a user can select. For more information about combo boxes, see Combo Boxes_win32_Combo_Boxes.
Many developers use the dialog boxes provided in the common dialog box library (COMDLG32.DLL) to perform tasks that otherwise might require customized edit controls. For information about common dialog boxes, see Common Dialog Box Library_win32_Common_Dialog_Box_Library.
+$#A Edit Control Styles
Windows provides several edit control styles. An individual edit control can have several styles at the same time. Most developers use standalone tools to develop dialog boxes and so may not need to specify edit control styles explicitly. If an application creates an edit control using the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function, however, it must specify these edit control styles.
Every edit control specifies a combination of style values that define the appearance and features of the edit control. The style values can establish the appearance of a single-line or multiline edit control, align the text in the control, and determine how, and even if, text appears in the edit control. The number and type of styles the application uses depend on the type and purpose of the edit control.
There are two line styles for edit controls. The default is a single-line edit control that doesn't require an associated style. An application can create a multiline edit control by using the ES_MULTILINE style.
There are three styles that cause Windows to align the text in an edit control. The ES_LEFT, ES_CENTER, and ES_RIGHT styles determine whether text is aligned on the left, center, or right, respectively. These styles apply only to multiline edit controls.
An application can use a style to determine how Windows displays text that a user enters into an edit control. The ES_LOWERCASE style converts the text into lowercase characters; the ES_UPPERCASE style converts the text into uppercase characters. Some applications may need to convert the text in a Windows string (such as a filename) into a specific character set. The ES_OEMCONVERT style ensures the proper conversion of characters in these instances. For more information about character sets, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support.
When the amount of text to be displayed exceeds the size of the edit control, an application can use two styles to scroll the text through the edit control. The ES_AUTOHSCROLL style automatically scrolls text horizontally in single-line and multiline edit controls. When the application has a multiline edit control, it can also use the ES_AUTOVSCROLL style to automatically scroll text vertically, if necessary.
Other available styles define different aspects of an edit control. The ES_NUMBER style (available in version 4.0 or later) restricts input to the edit control to digits only. The ES_NOHIDESEL style specifies that the selected text is not hidden when the edit control loses the keyboard focus. The ES_READONLY style makes the edit control read-only. The ES_PASSWORD style displays all characters in the edit control as asterisks. (An application can define a different character to display by using the EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR message, as described later in this topic.) In multiline edit controls, an application can specify the ES_WANTRETURN style to request that Windows insert a carriage return when the user presses the ENTER key in the edit control.
By default, an edit control has no border. To give it one, an application can use the WS_BORDER window style.
+$#A The Text Buffer
Windows stores edit control text in a buffer and copies it to the control, as necessary. This section discusses the tools Windows provides to allocate and initialize the buffer and to change its characteristics.
+$#A Allocating a Text Buffer
When Windows creates an edit control, it automatically creates a text buffer, sets its initial size, and increases the size as necessary. The size can be up to a predefined limit of approximately 32 kilobytes for single-line edit controls. Because this limit can change, it is called a soft limit. An application can set a hard limit to the buffer size by sending an EM_SETLIMITTEXT_win32_EM_SETLIMITTEXT message to the edit control. If the buffer exceeds either limit, Windows sends the application an EN_ERRSPACE_win32_EN_ERRSPACE message. An application can retrieve the current text limit by sending an EM_GETLIMITTEXT_win32_EM_GETLIMITTEXT message.
Windows typically creates an edit control buffer in a dialog box, using memory outside the application's data segment. An application can suppress this default allocation behavior and create the buffer from its local heap by using the DS_LOCALEDIT style when creating the edit control. An application that uses the DS_LOCALEDIT style is responsible for all buffer allocations. To make the initial allocation, an application can call the LocalAlloc_win32_LocalAlloc function and pass the returned buffer handle to the edit control by sending it an EM_SETHANDLE_win32_EM_SETHANDLE message. To make subsequent allocations (in response to an EN_ERRSPACE message, for example), an application should save the current buffer content (if necessary) and obtain a new buffer as follows:
1. Retrieve the handle of the memory currently allocated for the text in a multiline edit control by sending the control an EM_GETHANDLE_win32_EM_GETHANDLE message.
2. Free the buffer by calling the LocalFree_win32_LocalFree function.
3. Obtain a new buffer (and buffer handle) by calling LocalAlloc.
4. Give the buffer handle to Windows by sending the control an EM_SETHANDLE_win32_EM_SETHANDLE message.

The EM_SETHANDLE and EM_GETHANDLE_win32_EM_GETHANDLE messages apply only to multiline edit controls.
An application that uses the default allocation behavior (that is, does not use the DS_LOCALEDIT style) must not send EM_SETHANDLE and EM_GETHANDLE messages to the edit control.
Sending an EM_SETHANDLE_win32_EM_SETHANDLE message has several side effects: it clears the undo flag (making the EM_CANUNDO_win32_EM_CANUNDO message return zero), it clears the modify flag (making the EM_GETMODIFY_win32_EM_GETMODIFY message return zero), and it redraws the edit control window.
+$#A Initializing a Text Buffer
An application can initialize or reinitialize an edit control's text buffer by calling the SetDlgItemText_win32_SetDlgItemText function. An application can retrieve the content of a text buffer by calling the GetDlgItemText_win32_GetDlgItemText function.
+$#A Making a Text Buffer Read-Only
For each edit control, Windows maintains a read-only flag that indicates whether the control's text is read-write (the default) or read-only. An application can set the read-write or read-only flag for the text by sending the control an EM_SETREADONLY_win32_EM_SETREADONLY message. To determine whether an edit control is read-only, an application can call the GetWindowLong_win32_GetWindowLong function using the GWL_STYLE constant. The EM_SETREADONLY message applies to both single-line and multiline edit controls.
+$#A Changing the Formatting Rectangle
The visibility of an edit control's text is governed by the dimensions of its window rectangle and its formatting rectangle. The window rectangle is the client area of the window containing the edit control. The formatting rectangle is a construct maintained by Windows for formatting the text displayed in the window rectangle. When an edit control is first displayed, the two rectangles are identical on the screen. An application can make the formatting rectangle larger than the window rectangle (thereby limiting the visibility of the edit control's text) or smaller than the window rectangle (thereby creating extra white space around the text).
An application can set the coordinates of an edit control's formatting rectangle by sending it an EM_SETRECT_win32_EM_SETRECT message. The EM_SETRECT message also automatically redraws the edit control's text. To establish the coordinates of the formatting rectangle without redrawing the control's text, an application can send the control an EM_SETRECTNP_win32_EM_SETRECTNP message. To retrieve the coordinates of the formatting rectangle, an application can send the control an EM_GETRECT_win32_EM_GETRECT message. These messages apply to multiline edit controls only.
+$#A Text Operations
Windows automatically processes all user-initiated text operations and notifies the application when the operations are completed. This section discusses user-initiated text operations and the application's response.
+$#A Character Sets
Windows (except version 4.0) supports two character sets: Unicode™, a two-byte character set, and ANSI, a one-byte character set that is a strict subset of Unicode. In ANSI, n characters equals n bytes, but in Unicode, n characters equals 2n bytes. For more information about Unicode and character sets, see Unicode and Character Sets_win32_Unicode_and_Character_Sets.
+$#A Fonts
An application can change the font that an edit control uses by sending the WM_SETFONT_win32_WM_SETFONT message. Most applications do this while processing the WM_INITDIALOG_win32_WM_INITDIALOG message. Changing the font does not change the size of the edit control; applications that send the WM_SETFONT message may have to retrieve the font metrics for the text and recalculate the size of the edit control. For more information about fonts and font metrics, see Fonts and Text_win32_Fonts_and_Text.
+$#A Selecting an Edit Control
The user can select an edit control by clicking it with the mouse or by tabbing to it. The "tabbing" method is part of a predefined keyboard interface that Windows provides. For a complete description of this interface, see Dialog Boxes_win32_Dialog_Boxes.) When the user selects an edit control, Windows gives the control the keyboard focus and highlights its text by using reverse video.
+$#A Selecting Text
After selecting an edit control, the user can select text in the control by using the mouse or the keyboard. An application can retrieve the starting and ending character positions of the current selection in an edit control by sending the control an EM_GETSEL_win32_EM_GETSEL message. The return value for the ending position is one greater than the last character in the selection (that is, the position of the first character following the last selected character).
An application can also select text in an edit control by sending the control an EM_SETSEL_win32_EM_SETSEL message with the starting and ending character indices for the selection. For example, the application can use EM_SETSEL with EM_REPLACESEL_win32_EM_REPLACESEL to delete text from an edit control.
These three messages apply to both single-line and multiline edit controls.
+$#A Replacing Text
An application can replace selected text in an edit control by sending the control an EM_REPLACESEL_win32_EM_REPLACESEL message with a pointer to the replacement text. If there is no current selection, EM_REPLACESEL inserts the replacement text at the insertion point. The application may receive an EN_ERRSPACE_win32_EN_ERRSPACE notification message if the replacement text exceeds the available memory. This message applies to both single-line and multiline edit controls.
An application can use EM_REPLACESEL to replace part of an edit control's text or the SetDlgItemText_win32_SetDlgItemText function to replace all of it.
+$#A Cut, Copy, Paste, and Clear Operations
Windows provides four messages for moving text between an edit control and the clipboard. The WM_COPY_win32_WM_COPY message copies the current selection (if any) from an edit control to the clipboard without deleting it from the edit control. The WM_CUT_win32_WM_CUT message deletes the current selection, if any, in the edit control and copies the deleted text to the clipboard. The WM_CLEAR_win32_WM_CLEAR message deletes the current selection (if any) from an edit control, but does not copy it to the clipboard (unless the user pressed the SHIFT key). The WM_PASTE_win32_WM_PASTE message copies text from the clipboard into an edit control at the insertion point. These four messages apply to both single-line and multiline edit controls.
With version 4.0 or later, an edit control includes a built in context menu that makes it easy for the user to move text between the edit control and the clipboard. The context menu appears when the user clicks the control using the right mouse button. The menu items in the context menu include Undo, Cut, Copy, Paste, Delete, and Select All.
+$#A Modifying Text
The user can select, delete, or move text in an edit control. Windows maintains an internal flag for each edit control indicating whether the content of the control has been modified. Windows clears this flag when it creates the control and sets the flag whenever the text in the control is modified. An application can retrieve the modification flag by sending the control an EM_GETMODIFY_win32_EM_GETMODIFY message and set or clear the modification flag by sending the control an EM_SETMODIFY_win32_EM_SETMODIFY message. These messages apply to both single-line and multiline edit controls.
+$#A Limiting User-Entered Text
The default limit to the amount of text a user can enter in an edit control is 32 kilobytes. An application can change the amount of text the user can enter by sending the control an EM_SETLIMITTEXT_win32_EM_SETLIMITTEXT message. This message sets a hard limit to the number of bytes the user can enter into an edit control, but affects neither text that is already in the control when the message was sent nor text copied to the control by the SetDlgItemText_win32_SetDlgItemText function or the WM_SETTEXT_win32_WM_SETTEXT message. For example, suppose that the application uses the SetDlgItemText function to place 500 bytes in an edit control, and the user also enters 500 bytes (1000 bytes total). If the application then sends an EM_SETLIMITTEXT message limiting user-entered text to 300 bytes, the 1000 bytes already in the edit control remain there, and the user cannot add any more text. On the other hand, if the application sends an EM_SETLIMITTEXT message limiting user-entered text to 1300 bytes, the 1000 bytes remain, but the user can add 300 more bytes.
When the user reaches the character limit of an edit control, Windows sends the application a WM_COMMAND_win32_WM_COMMAND message containing an EN_MAXTEXT_win32_EN_MAXTEXT notification message. This notification message does not mean that memory has been exhausted, but that the limit for user-entered text has been reached; the user cannot enter any more text. To change this limit, an application must send the control a new EM_SETLIMITTEXT message with a higher limit.
As an example of the use of EM_SETLIMITTEXT and EN_MAXTEXT, suppose that the application must limit the user to no more than four characters in an edit control. The application would use EM_SETLIMITTEXT to specify a four-character limit. If the user tried to enter a fifth character, Windows would send an EN_MAXTEXT notification message to the application.
+$#A Character and Line Operations
The Microsoft® Win32® application programming interface (API) provides several messages that return information about the characters and lines in an edit control. Most of the messages return an index, usually a zero-based number, to refer to a character or line. Given this, a single-line edit control containing n characters, the line index is zero and the characters are indexed from zero to n - 1. In a multiline edit control containing m lines and n characters, the lines are indexed from zero to m - 1, and the characters are indexed from zero to n - 1. Note that character indexing ignores linebreaks.
An application can determine the number of characters in an edit control by sending the WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH message to the edit control. This message returns the length, in characters (not including the terminating null character), of the text in a single-line or multiline edit control. The EM_LINELENGTH_win32_EM_LINELENGTH message returns the length, in characters, of a line specified by the character index of a character in the line. The returned length does not include any selected characters. An application can use these messages in a single-line or multiline edit control.
The EM_GETFIRSTVISIBLELINE_win32_EM_GETFIRSTVISIBLELINE message returns the zero-based index of the uppermost visible line in a multiline edit control, or the zero-based index of the first visible character in a single-line edit control. An application can copy a line from an edit control to a buffer by sending the EM_GETLINE_win32_EM_GETLINE message to the edit control. The line is specified by its line index and the first word of the receiving buffer contains the maximum number of bytes to be copied to the buffer. The return value is the number of bytes copied. This message can also be used in a single-line or multiline edit control.
There are unique messages available to return the information about a line in a multiline edit control. The EM_GETLINECOUNT_win32_EM_GETLINECOUNT message returns the number of lines in an edit control. An application can determine the index of a character in a specific line by using the EM_LINEFROMCHAR_win32_EM_LINEFROMCHAR and EM_LINEINDEX_win32_EM_LINEINDEX messages. The EM_LINEFROMCHAR message returns the index of the line containing a specified character index. This message is the reverse of the EM_LINEINDEX message, which returns the index of the first character in a specified line.
+$#A Scrolling Text in an Edit Control
An application can implement scrolling in an edit control by using the automatic scrolling styles discussed in Edit Control Styles_win32_Edit_Control_Styles or by explicitly adding scroll bars to the control. To add a horizontal scroll bar, use the style WS_HSCROLL; to add a vertical scroll bar, use the style WS_VSCROLL. An edit control with scroll bars processes its own scroll bar messages. For detailed information about adding scroll bars to edit controls, see Scroll Bars_win32_Scroll_Bars.
Windows provides three messages that an application can send to an edit control with scroll bars. The EM_LINESCROLL_win32_EM_LINESCROLL message can scroll a multiline edit control both vertically and horizontally. The lParam parameter specifies the number of lines to scroll vertically starting from the current line and the wParam parameter specifies the number of characters to scroll horizontally, starting from the current character. The edit control doesn't acknowledge horizontal scrolling messages if it has the ES_CENTER or ES_RIGHT style. This message applies to multiline edit controls only.
The EM_SCROLL_win32_EM_SCROLL message scrolls a multiline edit control vertically, which is the same effect as sending a WM_VSCROLL_win32_WM_VSCROLL message. The wParam parameter specifies the scrolling action. The EM_SCROLL message applies to multiline edit controls only.
The EM_SCROLLCARET_win32_EM_SCROLLCARET message scrolls the caret into view in an edit control. This was done in previous versions of Windows by specifying wParam = FALSE in an EM_SETSEL_win32_EM_SETSEL message. A Win32-based application should use the EM_SCROLLCARET message for the task.
+$#A Tab Stops and Margins
An application can set tab stops in a multiline edit control by using the EM_SETTABSTOPS_win32_EM_SETTABSTOPS message. (The default for a tab stop is eight characters.) When an application adds text to the edit control, tab characters in the text automatically generate space up to the next tab stop. The EM_SETTABSTOPS message does not automatically cause Windows to redraw the text. To do that, an application can call the InvalidateRect_win32_InvalidateRect function. The EM_SETTABSTOPS message applies to multiline edit controls only.
An application can set width of the left and right margins for an edit control by using the EM_SETMARGINS_win32_EM_SETMARGINS message. After sending this message, Windows redraws the edit control to reflect the new margin settings. An application can retrieve the width of the left or right margin by sending the EM_GETMARGINS_win32_EM_GETMARGINS message. By default, the edit control margins are set to be just wide enough to accommodate the largest character horizontal overhang (negative ABC widths) for the font currently in use in the edit control.
+$#A Password Characters
An application can use a password character in an edit control to conceal user input. When a password character is set, it is displayed in place of each character the user types. When a password character is removed, the control displays the characters the user types. If the application creates an edit control using the style ES_PASSWORD, the default password character is an asterisk (*). An application can use the EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR message to remove or define a different password character and the EM_GETPASSWORDCHAR_win32_EM_GETPASSWORDCHAR message to retrieve the current password character. These messages apply to single-line edit controls only.
+$#A Using Integers
Windows provides two integer-conversion functions for edit controls designed to contain numbers only. The SetDlgItemInt_win32_SetDlgItemInt function creates the string representation of a specified integer (signed or unsigned) and sends the string to an edit control. SetDlgItemInt returns no value. The GetDlgItemInt_win32_GetDlgItemInt function creates an integer (signed or unsigned) from its string representation in an edit control. GetDlgItemInt returns the integer (or an error value).
+$#A Undoing Text Operations
Every edit control maintains an undo flag that indicates whether an application can reverse (undo) the most recent operation on the edit control (to undo a text deletion, for example). The edit control sets the undo flag to indicate that the operation can be undone and resets it to indicate that the operation cannot be undone. An application can determine the setting of the undo flag by sending the control an EM_CANUNDO_win32_EM_CANUNDO message.
An application can undo the most recent operation by sending the control an EM_UNDO_win32_EM_UNDO message. An operation can be undone provided no other edit control operation occurs first. For example, the user can delete text, replace the text (undo the deletion), and then delete the text again (undo the replacement). The EM_UNDO message applies to both single-line and multiline edit controls and always works for single-line edit controls.
An application can reset an edit control's undo flag by sending the control an EM_EMPTYUNDOBUFFER_win32_EM_EMPTYUNDOBUFFER message. Windows automatically resets the undo flag whenever an edit control receives an EM_SETHANDLE_win32_EM_SETHANDLE or WM_SETTEXT_win32_WM_SETTEXT message. The SetDlgItemText_win32_SetDlgItemText function sends a WM_SETTEXT message.
+$#A Wordwrap Functions
An application can use wordwrap functions with multiline edit controls to locate the word or word fragment that should be wrapped to the next line. Using the default wordwrap function provided by Windows, lines always end at the spaces between words. An application can specify its own wordwrap function by supplying a EditWordBreakProc_win32_EditWordBreakProc wordwrap function and sending an edit control an EM_SETWORDBREAKPROC_win32_EM_SETWORDBREAKPROC message. An application can retrieve the address of the current wordwrap function by sending the control an EM_GETWORDBREAKPROC_win32_EM_GETWORDBREAKPROC message.
An application may direct a multiline edit control to add or remove a soft linebreak character (two carriage returns and a linefeed) automatically at the end of wrapped text lines. An application can turn this feature on or off by sending the edit control an EM_FMTLINES_win32_EM_FMTLINES message. This message applies only to multiline edit controls and does not affect a line that ends with a hard linebreak (one carriage return and a linefeed entered by the user).
+$#A Retrieving Points and Characters
An application can determine which character is closest to the specified point in an edit control by sending the EM_CHARFROMPOS_win32_EM_CHARFROMPOS message. The message returns the character index and line index of the character nearest the point. Similarly, an application can determine the client coordinates of the specified character in an edit control by sending the EM_POSFROMCHAR_win32_EM_POSFROMCHAR message. The application specifies the index of a character and the message returns the x and y coordinates of the upper left corner of the character.
+$#A Edit Control Notification Messages
The user makes editing requests by using the keyboard and mouse. Windows sends each request to the edit control's parent window in the form of a WM_COMMAND_win32_WM_COMMAND message. The message includes the edit control identifier in the low-order word of the wParam parameter, the handle of the edit control in the lParam parameter, and an edit control notification message corresponding to the user's action in the high-order word of the wParam parameter.

Portability issue In Microsoft® Windows NT™, the placement of the notification code in a notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x applications that process notification messages must modify this code when porting to Windows NT.

An application should examine each notification message and respond appropriately. The following table lists each edit control notification message and the action that generates it.
Notification message User action
EN_CHANGE_win32_EN_CHANGE The user has modified text in an edit control. Windows updates the display before sending this message (unlike EN_UPDATE_win32_EN_UPDATE).
EN_ERRSPACE_win32_EN_ERRSPACE The edit control cannot allocate enough memory to meet a specific request.
EN_HSCROLL_win32_EN_HSCROLL The user has clicked the edit control's horizontal scroll bar. Windows sends this message before updating the screen.
EN_KILLFOCUS_win32_EN_KILLFOCUS The user has selected another control.
EN_MAXTEXT_win32_EN_MAXTEXT While inserting text, the user has exceeded the specified number of characters for the edit control. Insertion has been truncated. This message is also sent either when an edit control does not have the ES_AUTOHSCROLL style and the number of characters to be inserted exceeds the width of the edit control or when an edit control does not have the ES_AUTOVSCROLL style and the total number of lines to be inserted exceeds the height of the edit control.
EN_SETFOCUS_win32_EN_SETFOCUS The user has selected this edit control.
EN_UPDATE_win32_EN_UPDATE The user has altered the text in the edit control and Windows is about to display the new text. Windows sends this message after formatting the text, but before displaying it, so that the application can resize the edit control window.
EN_VSCROLL_win32_EN_VSCROLL The user has clicked the edit control's vertical scroll bar. Windows sends this message before updating the screen.

In addition, the system sends a WM_CTLCOLOREDIT_win32_WM_CTLCOLOREDIT message to an edit control's parent window before the edit control is drawn. This message contains a handle of the edit control's display context (DC) and a handle of the child window. The parent window can use these handles to change the edit control's text and background colors.
+$#A Edit Control Default Message Processing
The window procedure for the predefined edit control window class carries out default processing for all messages that the edit control procedure does not process. When the edit control procedure returns FALSE for any message, the predefined window procedure checks the messages and carries out the following default actions.
Message Default action
EM_CANUNDO_win32_EM_CANUNDO Returns TRUE if the edit control operation can be undone.
EM_CHARFROMPOS_win32_EM_CHARFROMPOS Returns the character index and line index of the character nearest the specified point.
EM_EMPTYUNDOBUFFER_win32_EM_EMPTYUNDOBUFFER Empties the undo buffer and sets the undo flag retrieved by the EM_CANUNDO_win32_EM_CANUNDO message to FALSE. Windows automatically clears the undo flag whenever the edit control receives a WM_SETTEXT_win32_WM_SETTEXT or EM_SETHANDLE_win32_EM_SETHANDLE message.
EM_FMTLINES_win32_EM_FMTLINES Adds or removes soft linebreak characters (two carriage returns and a linefeed) to the ends of wrapped lines in a multiline edit control. It is not processed by single-line edit controls.
EM_GETFIRSTVISIBLELINE_win32_EM_GETFIRSTVISIBLELINE Returns the zero-based index of the first visible character in a single-line edit control or the zero-based index of the uppermost visible line in a multiline edit control.
EM_GETHANDLE_win32_EM_GETHANDLE Returns a handle identifying the buffer containing the multiline edit control's text. It is not processed by single-line edit controls.
EM_GETLIMITTEXT_win32_EM_GETLIMITTEXT Returns the current text limit, in characters.
EM_GETLINE_win32_EM_GETLINE Copies characters in a single-line edit control to a buffer and returns the number of characters copied. In a multiline edit control, retrieves a line of text from the control and returns the number of characters copied.
EM_GETLINECOUNT_win32_EM_GETLINECOUNT Returns the number of lines in the edit control.
EM_GETMARGINS_win32_EM_GETMARGINS Returns the widths of the left and right margins.
EM_GETMODIFY_win32_EM_GETMODIFY Returns a flag indicating whether the content of an edit control has been modified.
EM_GETPASSWORDCHAR_win32_EM_GETPASSWORDCHAR Returns the character that edit controls use in conjunction with the ES_PASSWORD style.
EM_GETRECT_win32_EM_GETRECT Returns the coordinates of the formatting rectangle in an edit control.
EM_GETSEL_win32_EM_GETSEL Returns the starting and ending character positions of the current selection in the edit control.
EM_GETTHUMB_win32_EM_GETTHUMB Returns the position of the scroll box.
EM_GETWORDBREAKPROC_win32_EM_GETWORDBREAKPROC Returns the address of the current wordwrap function in an edit control.
EM_LINEFROMCHAR_win32_EM_LINEFROMCHAR Returns the zero-based number of the line in a multiline edit control that contains a specified character index This message is the reverse of the EM_LINEINDEX_win32_EM_LINEINDEX message. It is not processed by single-line edit controls.
EM_LINEINDEX Returns the character of a line in a multiline edit control. This message is the reverse of the EM_LINEFROMCHAR_win32_EM_LINEFROMCHAR message. It is not processed by single-line edit controls.
EM_LINELENGTH_win32_EM_LINELENGTH Returns the length, in characters, of a single-line edit control. In a multiline edit control, returns the length, in characters, of a specified line.
EM_LINESCROLL_win32_EM_LINESCROLL Scrolls the text vertically in a single-line edit control or horizontally in a multiline edit control (when the control has the ES_LEFT style). The lParam parameter specifies the number of lines to scroll vertically, starting from the current line. The wParam parameter specifies the number of characters to scroll horizontally, starting from the current character.
EM_POSFROMCHAR_win32_EM_POSFROMCHAR Returns the client coordinates of the specified character.
EM_REPLACESEL_win32_EM_REPLACESEL Replaces the current selection with the text in an application-supplied buffer, sends the parent window EN_UPDATE_win32_EN_UPDATE and EN_CHANGE_win32_EN_CHANGE messages, and updates the undo buffer.
EM_SCROLL_win32_EM_SCROLL Scrolls the text vertically in a multiline edit control. This message is equivalent to sending a WM_VSCROLL_win32_WM_VSCROLL message to the edit control. It is not processed by single-line edit controls.
EM_SCROLLCARET_win32_EM_SCROLLCARET Scrolls the caret into view in an edit control. In earlier versions of Windows this was done via EM_SETSEL_win32_EM_SETSEL using particular parameters. A Win32-based application should use EM_SCROLLCARET.
EM_SETFONT Unsupported.
EM_SETHANDLE_win32_EM_SETHANDLE Sets a handle to the memory used as a text buffer, empties the undo buffer, resets the scroll positions to zero, and redraws the window.
EM_SETLIMITTEXT_win32_EM_SETLIMITTEXT Sets the maximum number of characters the user may enter in the edit control. For single-line edit controls, this value is either 0x7FFFFFFE (0x7FFE for version 4.0) or the value of the wParam parameter, whichever is smaller. For multiline edit controls, this value is either 0xFFFFFFFF (0xFFFF for version 4.0) or the value of the wParam parameter, whichever is smaller.
EM_SETMARGINS_win32_EM_SETMARGINS Sets the widths of the left and right margins, and redraws the edit control to reflect the new margins.
EM_SETMODIFY_win32_EM_SETMODIFY Sets or clears the modification flag to indicate whether the edit control has been modified.
EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR Defines the character that edit controls use in conjunction with the ES_PASSWORD style.
EM_SETREADONLY_win32_EM_SETREADONLY Sets or removes the read-only style (ES_READONLY) in an edit control.
EM_SETRECT_win32_EM_SETRECT Sets the formatting rectangle for the multiline edit control and redraws the window. It is not processed by single-line edit controls.
EM_SETRECTNP_win32_EM_SETRECTNP Sets the formatting rectangle for the multiline edit control but does not redraw the window. It is not processed by single-line edit controls.
EM_SETSEL_win32_EM_SETSEL Selects a range of characters in the edit control by setting the starting and ending positions to be selected.
EM_SETTABSTOPS_win32_EM_SETTABSTOPS Sets tab-stop positions in the multiline edit control. It is not processed by single-line edit controls.
EM_SETWORDBREAKPROC_win32_EM_SETWORDBREAKPROC Replaces the default wordwrap function with an application-defined wordwrap function.
EM_UNDO_win32_EM_UNDO Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text. If necessary, sends the EN_UPDATE_win32_EN_UPDATE and EN_CHANGE_win32_EN_CHANGE notification messages to the parent window.
WM_CHAR_win32_WM_CHAR Writes a character to the single-line edit control and sends the EN_UPDATE and EN_CHANGE notification messages to the parent window. Writes a character to the multiline edit control. Handles the accelerator keys for standard functions, such as CTRL+C for copying and CTRL+V for pasting. In multiline edit controls, also processes TAB, and CTRL+TAB keystrokes to move among the controls in a dialog box and to insert tabs into multiline edit controls. Uses the MessageBeep_win32_MessageBeep function for illegal characters.
WM_CLEAR_win32_WM_CLEAR Clears the current selection, if any, in an edit control. If there is no current selection, deletes the character to the right of the caret. If the user presses the SHIFT key, this cuts the selection to the clipboard, or deletes the character to the left of the caret when there is no selection. If the user presses the CTRL key, this deletes the selection, or deletes to the end of the line when there is no selection.
WM_COPY_win32_WM_COPY Copies text to the clipboard unless the style is ES_PASSWORD, in which case the message returns zero.
WM_CREATE_win32_WM_CREATE Creates the edit control and notifies the parent window with TRUE for success or -1 for failure.
WM_CUT_win32_WM_CUT Cuts the selection to the clipboard, or deletes the character to the left of the cursor if there is no selection.
WM_ENABLE_win32_WM_ENABLE Causes the rectangle to be redrawn in gray for single-line edit controls. Returns the enabled state for single-line and multiline edit controls.
WM_ERASEBKGND_win32_WM_ERASEBKGND Fills the multiline edit control window with the current color of the edit control.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns the following values: DLGC_WANTCHARS, DLGC_HASSETSEL, and DLGC_WANTARROWS. In multiline edit controls, it also returns DLGC_WANTALLKEYS. If the user presses ALT+BACKSPACE, it also returns DLGC_WANTMESSAGE.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the font being used by the control, or NULL if the control uses the system font.
WM_GETTEXT_win32_WM_GETTEXT Copies the specified number of characters to a buffer and returns the number of characters copied.
WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH Returns the length, in characters, of the text in an edit control. The length does not include the null- terminating character.
WM_HSCROLL_win32_WM_HSCROLL Scrolls the text in a multiline edit control horizontally and handles scroll box movement.
WM_KEYDOWN_win32_WM_KEYDOWN Performs standard processing of the virtual-key codes.
WM_KILLFOCUS_win32_WM_KILLFOCUS Removes the keyboard focus of an edit control window, destroys the caret, hides the current selection, and notifies the parent window that the edit control has lost the focus.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Clears the current selection and selects the word under the cursor. If the SHIFT key is down, extends the selection to the word under the cursor.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Changes the current insertion point. If the SHIFT key is down, it extends the selection to the position of the cursor. In multiline edit controls, also sets the timer to automatically scroll when the user holds down the mouse button outside the multiline edit control window.
WM_LBUTTONUP_win32_WM_LBUTTONUP Releases the mouse capture and sets the text insertion point in the single-line edit control. In a multiline edit control, it also kills the timer set in the WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Changes the current selection in the single-line edit control, if the mouse button is down. In a multiline edit controls, also sets the timer to automatically scroll if the user holds down the mouse button outside the multiline edit control window.
WM_NCCREATE_win32_WM_NCCREATE Points to the CREATESTRUCT_win32_CREATESTRUCT_str structure for the window. This message is sent to the WM_CREATE_win32_WM_CREATE message when a window is first created.
WM_NCDESTROY_win32_WM_NCDESTROY Frees all memory associated with the edit control window, including the text buffer, undo buffer, tab-stop buffer, and highlight brush.
WM_PAINT_win32_WM_PAINT Erases the background, fills the window with the current color of the edit control window, draws the border (if any), sets the font and draws any text, and shows the text-insertion caret.
WM_PASTE_win32_WM_PASTE Pastes text from the clipboard into the edit control window at the caret position.
WM_SETFOCUS_win32_WM_SETFOCUS Sets the keyboard focus of an edit control window (shows the current selection, if it was hidden, and creates the caret).
WM_SETFONT_win32_WM_SETFONT Sets the font and optionally redraws the edit control.
WM_SETTEXT_win32_WM_SETTEXT Copies text to the single-line edit control, notifies the parent window when there is insufficient memory, empties the undo buffer, and sends the EN_UPDATE_win32_EN_UPDATE and EN_CHANGE_win32_EN_CHANGE notification messages to the parent window. In multiline edit controls, also rewraps the lines (if necessary) and sets the scroll positions.
WM_SIZE_win32_WM_SIZE Changes the size of the edit control window and ensures that the minimum size accommodates the height and width of a character.
WM_SYSCHAR_win32_WM_SYSCHAR Returns TRUE if the user presses ALT+BACKSPACE; otherwise, it takes no action.
WM_SYSKEYDOWN_win32_WM_SYSKEYDOWN Undoes the last action if the user presses ALT+BACKSPACE; otherwise, it takes no action.
WM_TIMER_win32_WM_TIMER Scrolls the text in the edit control window if the user holds down the mouse button outside the multiline edit control window.
WM_UNDO_win32_WM_UNDO Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text. If necessary, sends the EN_UPDATE_win32_EN_UPDATE and EN_CHANGE_win32_EN_CHANGE notification messages to the parent window.
WM_VSCROLL_win32_WM_VSCROLL Scrolls a multiline edit control vertically and handles scroll box movement. It is not processed by single-line edit controls.

The predefined edit control window procedure passes all other messages to the DefWindowProc_win32_DefWindowProc function for default processing.
+$#A Using Edit Controls
Edit controls are typically used in dialog boxes, but you can use them in the client area of a standard window as well. Single-line edit controls are useful for retrieving a single string from the user. Multiline edit controls make it easy for your application to implement most of the features of a simple word processor.
· Simple word processing with an edit control_win32_Simple_Word_Processing_with_an_Edit_Control
· Using single-line edit controls_win32_Using_Single_Line_Edit_Controls

+$#A Simple Word Processing with an Edit Control
The following example implements much of the functionality of a simple word processor by filling the client area of a window with a multiline edit control. The system automatically performs wordwrap operations for this edit control and also handles the processing for the vertical scroll bar (created by specifying ES_AUTOVSCROLL in the call to the CreateWindow_win32_CreateWindow function). The WM_COMMAND_win32_WM_COMMAND message processes menu items; they allow the user to undo the previous action, cut or copy selections to the clipboard, paste text from the clipboard, and delete the current selection.
LONG APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{
static HWND hwndEdit;

CHAR lpszTrouble[] = "When in the Course of human Events "
"it becomes necessary for one People "
"to dissolve the Political Bands which "
"have connected them with another, and "
"to assume among the Powers of the "
"Earth, the separate and equal Station "
"to which the Laws of Nature and of "
"Nature's God entitle them, a decent "
"Respect to the Opinions of Mankind "
"requires that they should declare the "
"causes which impel them to the "
"Separation. ";

switch (message) {
case WM_CREATE:
hwndEdit = CreateWindow(
"EDIT", /* predefined class */
NULL, /* no window title */
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 0, 0, /* set size in WM_SIZE message */
hwnd, /* parent window */
(HMENU) ID_EDITCHILD, /* edit control ID */
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
NULL); /* pointer not needed */

/* Add text to the window. */

SendMessage(hwndEdit, WM_SETTEXT, 0,
(LPARAM) lpszTrouble);

return 0;

case WM_COMMAND:
switch (wParam) {
case IDM_EDUNDO:

/*
* Send WM_UNDO only if there is something
* to be undone.
*/

if (SendMessage(hwndEdit, EM_CANUNDO, 0, 0))
SendMessage(hwndEdit, WM_UNDO, 0, 0);
else
MessageBox(hwndEdit,
"Nothing to undo.",
"Undo notification", MB_OK);
break;

case IDM_EDCUT:
SendMessage(hwndEdit, WM_CUT, 0, 0);
break;

case IDM_EDCOPY:
SendMessage(hwndEdit, WM_COPY, 0, 0);
break;

case IDM_EDPASTE:
SendMessage(hwndEdit, WM_PASTE, 0, 0);
break;

case IDM_EDDEL:
SendMessage(hwndEdit, WM_CLEAR, 0, 0);
break;

case IDM_PASSWORD:
DialogBox(hinst, /* current instance */
"PassBox", /* resource to use */
hwnd, /* parent handle */
(DLGPROC) PassProc);
break;

case IDM_WRAP:
SendMessage(hwndEdit,
EM_SETWORDBREAKPROC,
(WPARAM) 0,
(LPARAM) (EDITWORDBREAKPROC) WordBreakProc);
SendMessage(hwndEdit,
EM_FMTLINES,
(WPARAM) TRUE,
(LPARAM) 0);
SendMessage(hwndEdit,
EM_SETSEL,
0, -1); /* select all text */
SendMessage(hwndEdit, WM_CUT, 0, 0);
SendMessage(hwndEdit, WM_PASTE, 0, 0);
break;

case IDM_ABOUT:
DialogBox(hinst, /* current instance */
"AboutBox", /* resource to use */
hwnd, /* parent handle */
(DLGPROC) About);
break;

default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
break;

case WM_SETFOCUS:
SetFocus(hwndEdit);
return 0;

case WM_SIZE:

/*
* Make the edit control the size of the window's
* client area.
*/

MoveWindow(hwndEdit,
0, 0, /* starting x- and y-coordinates */
LOWORD(lParam), /* width of client area */
HIWORD(lParam), /* height of client area */
TRUE); /* repaint window */
return 0;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return NULL;
}

+$#A Using Single-Line Edit Controls
The example in this section demonstrates how to use a window procedure to produce a dialog box that prompts the user to enter a password.
The single-line edit control in the Password dialog box has the ES_PASSWORD style. By default, edit controls with this style display an asterisk for each character the user types. This example, however, uses the EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR message to change the default character from an asterisk to a plus sign (+).
This window procedure changes the default push button from Cancel to OK as soon as the user enters text in the edit control. If the user presses the OK button, the window procedure uses the EM_LINELENGTH_win32_EM_LINELENGTH and EM_GETLINE_win32_EM_GETLINE messages to retrieve the text.
LRESULT CALLBACK PassProc(hDlg, message, wParam, lParam)
HWND hDlg; /* window handle of the dialog box */
UINT message; /* type of message */
UINT wParam; /* message-specific information */
LONG lParam;
{
CHAR lpszPassword[16];
WORD cchPassword;

switch (message) {
case WM_INITDIALOG:

/* Set password character to a plus sign (+) */

SendDlgItemMessage(hDlg,
IDE_PASSWORDEDIT,
EM_SETPASSWORDCHAR,
(WPARAM) '+',
(LPARAM) 0);

/* Set the default push button to "Cancel." */

SendMessage(hDlg,
DM_SETDEFID,
(WPARAM) IDCANCEL,
(LPARAM) 0);

return TRUE;

case WM_COMMAND:

/*
* Set the default push button to "OK" when the user
* enters text.
*/

if(HIWORD (wParam) == EN_CHANGE &&
LOWORD(wParam) == IDE_PASSWORDEDIT)
SendMessage(hDlg,
DM_SETDEFID,
(WPARAM) IDOK,
(LPARAM) 0);

switch(wParam) {
case IDOK:

/* Get number of characters. */

cchPassword = (WORD) SendDlgItemMessage(hDlg,
IDE_PASSWORDEDIT,
EM_LINELENGTH,
(WPARAM) 0,
(LPARAM) 0);

if (cchPassword >= 16) {
MessageBox(hDlg,
"Too many characters.",
"Error",
MB_OK);
EndDialog(hDlg, TRUE);
return FALSE;
}
else if (cchPassword == 0) {
MessageBox(hDlg,
"No characters entered.",
"Error",
MB_OK);
EndDialog(hDlg, TRUE);
return FALSE;
}

/*
* Put the number of characters into first word
* of buffer.
*/

*((LPWORD)lpszPassword) = cchPassword;

/* Get the characters. */

SendDlgItemMessage(hDlg,
IDE_PASSWORDEDIT,
EM_GETLINE,
(WPARAM) 0, /* line 0 */
(LPARAM) lpszPassword);

/* Null-terminate the string. */

lpszPassword[cchPassword] = 0;

MessageBox(hDlg,
lpszPassword,
"Did it work?",
MB_OK);

/* Call a local password-parsing function. */

ParsePassword(lpszPassword);

EndDialog(hDlg, TRUE);
return TRUE;

case IDCANCEL:
EndDialog(hDlg, TRUE);
return TRUE;
}
return 0;
}
return FALSE;
UNREFERENCED_PARAMETER(lParam);
}

+$#AK Edit Control Reference
The following functions and messages are associated with edit controls.
+$#A Edit Control Functions
Following are the functions used with edit controls.
EditWordBreakProc_win32_EditWordBreakProc
+$#A Edit Control Messages
Following are the messages used with edit controls.
EM_CANUNDO_win32_EM_CANUNDOEM_CHARFROMPOS_win32_EM_CHARFROMPOSEM_EMPTYUNDOBUFFER_win32_EM_EMPTYUNDOBUFFEREM_FMTLINES_win32_EM_FMTLINESEM_GETFIRSTVISIBLELINE_win32_EM_GETFIRSTVISIBLELINEEM_GETHANDLE_win32_EM_GETHANDLEEM_GETLIMITTEXT_win32_EM_GETLIMITTEXTEM_GETLINE_win32_EM_GETLINEEM_GETLINECOUNT_win32_EM_GETLINECOUNTEM_GETMARGINS_win32_EM_GETMARGINSEM_GETMODIFY_win32_EM_GETMODIFYEM_GETPASSWORDCHAR_win32_EM_GETPASSWORDCHAREM_GETRECT_win32_EM_GETRECTEM_GETSEL_win32_EM_GETSELEM_GETWORDBREAKPROC_win32_EM_GETWORDBREAKPROCEM_LINEFROMCHAR_win32_EM_LINEFROMCHAREM_LINEINDEX_win32_EM_LINEINDEXEM_LINELENGTH_win32_EM_LINELENGTHEM_LINESCROLL_win32_EM_LINESCROLLEM_POSFROMCHAR_win32_EM_POSFROMCHAREM_REPLACESEL_win32_EM_REPLACESELEM_SCROLL_win32_EM_SCROLLEM_SCROLLCARET_win32_EM_SCROLLCARETEM_SETHANDLE_win32_EM_SETHANDLEEM_SETLIMITTEXT_win32_EM_SETLIMITTEXTEM_SETMARGINS_win32_EM_SETMARGINSEM_SETMODIFY_win32_EM_SETMODIFYEM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAREM_SETREADONLY_win32_EM_SETREADONLYEM_SETRECT_win32_EM_SETRECTEM_SETRECTNP_win32_EM_SETRECTNPEM_SETSEL_win32_EM_SETSELEM_SETTABSTOPS_win32_EM_SETTABSTOPSEM_SETWORDBREAKPROC_win32_EM_SETWORDBREAKPROCEM_UNDO_win32_EM_UNDOEN_CHANGE_win32_EN_CHANGEEN_ERRSPACE_win32_EN_ERRSPACEEN_HSCROLL_win32_EN_HSCROLLEN_KILLFOCUS_win32_EN_KILLFOCUSEN_MAXTEXT_win32_EN_MAXTEXTEN_SETFOCUS_win32_EN_SETFOCUSEN_UPDATE_win32_EN_UPDATEEN_VSCROLL_win32_EN_VSCROLLWM_COMMAND_win32_WM_COMMANDWM_COPY_win32_WM_COPYWM_CTLCOLOREDIT_win32_WM_CTLCOLOREDITWM_CUT_win32_WM_CUTWM_PASTE_win32_WM_PASTEWM_UNDO_win32_WM_UNDO

+$#AK List Boxes
The Microsoft® Win32® application programming interface (API) provides dialog boxes and controls to support communication between an application and the user. A list box is a control window that contains a list of items from which the user can choose.
+$#A About List Boxes
List box items can be represented by text strings, bitmaps, or both. If the list box is not large enough to display all the list box items at once, the list box can provide a scroll bar. The user maneuvers through the list box items, scrolling the list when necessary, and selects or removes the selection from items. Selecting a list box item changes its visual appearance, usually by changing the text and background colors to the colors specified by the operating system metrics for selected items. When the user selects an item or removes the selection from an item, Windows sends a notification message to the parent window of the list box.
A dialog box procedure is responsible for initializing and monitoring its child windows, including any list boxes. The dialog box procedure communicates with the list box by sending messages to it and by processing the notification messages sent by the list box. For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes.
For general information about controls, see Controls_win32_Controls.
+$#A List Box Styles
The Win32 API provides two general styles of list box: single-selection (the default style) and multiple-selection. In a single-selection list box, the user can select only one item at a time. In a multiple-selection list box, the user can select more than one item at a time; an application must specify either the LBS_MULTIPLESEL or the LBS_EXTENDEDSEL style for a multiple-selection list box.
The Win32 API provides many other list box and window styles that control the appearance and operation of a list box. These styles indicate whether list box items are sorted, arranged in multiple columns, drawn by the application, and so on. The dimensions and styles of a list box are typically defined in a dialog box template included in an application's resources. For information about how to create a dialog box template and include it in an application, see the documentation provided with your development tools.
To create a list box by using the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function, use the LISTBOX class, appropriate window style constants, and the following style constants to define the list box.
List box style Description
LBS_DISABLENOSCROLL Shows a disabled vertical scroll bar for the list box when the box does not contain enough items to scroll. If you do not specify this style, the scroll bar is hidden when the list box does not contain enough items.
LBS_EXTENDEDSEL Allows multiple items to be selected by using the SHIFT key and the mouse or special key combinations.
LBS_HASSTRINGS Specifies that a list box contains items consisting of strings. The list box maintains the memory and addresses for the strings so that the application can use the LB_GETTEXT_win32_LB_GETTEXT message to retrieve the text for a particular item. By default, all list boxes except owner-drawn list boxes have this style. You can create an owner-drawn list box either with or without this style.
LBS_MULTICOLUMN Specifies a multicolumn list box that is scrolled horizontally. The LB_SETCOLUMNWIDTH_win32_LB_SETCOLUMNWIDTH message sets the width of the columns.
LBS_MULTIPLESEL Turns string selection on or off each time the user clicks or double-clicks a string in the list box. The user can select any number of strings.
LBS_NODATA Obsolete.
LBS_NOINTEGRALHEIGHT Specifies that the size of the list box is exactly the size specified by the application when it created the list box. Normally, Windows sizes a list box so that the list box does not display partial items.
LBS_NOREDRAW Specifies that the list box's appearance is not updated when changes are made. You can change this style by sending a WM_SETREDRAW_win32_WM_SETREDRAW message at any time.
LBS_NOSEL Specifies that the list box contains items that can be viewed but not selected.
LBS_NOTIFY Notifies the parent window with an input message whenever the user clicks or double-clicks a string in the list box.
LBS_OWNERDRAWFIXED Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are the same height. The owner window receives a WM_MEASUREITEM_win32_WM_MEASUREITEM message when the list box is created and a WM_DRAWITEM_win32_WM_DRAWITEM message when a visual aspect of the list box has changed.
LBS_OWNERDRAWVARIABLE Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are variable in height. The owner window receives a WM_MEASUREITEM message for each item in the combo box when the combo box is created and a WM_DRAWITEM message when a visual aspect of the combo box has changed.
LBS_SORT Sorts strings in the list box alphabetically.
LBS_STANDARD Sorts strings in the list box alphabetically. The parent window receives an input message whenever the user clicks or double-clicks a string. The list box has borders on all sides.
LBS_USETABSTOPS Enables a list box to recognize and expand tab characters when drawing its strings. The default tab positions are 32 dialog box units. A dialog box unit is a horizontal or vertical distance. One horizontal dialog box unit is equal to 0.25 of the current dialog box base-width unit. Windows calculates these units based on the height and width of the current system font. The GetDialogBaseUnits_win32_GetDialogBaseUnits function returns the current dialog box base units in pixels.
LBS_WANTKEYBOARDINPUT Specifies that the owner of the list box receives WM_VKEYTOITEM_win32_WM_VKEYTOITEM messages whenever the user presses a key and the list box has the input focus. This enables an application to perform special processing on the keyboard input.

+$#A List Box Functions Overview
The Win32 API provides two functions specific to list boxes. The DlgDirList_win32_DlgDirList function fills a list box with the files and directories in a specified path. The DlgDirSelectEx_win32_DlgDirSelectEx function retrieves the current selection in a list box initialized by DlgDirList. These functions make it possible for the user to select a file from a list box without typing the location and name of the file.
+$#A Notification Messages from List Boxes
When an event occurs in a list box, the list box sends a notification message to the dialog box procedure of the owner window. List box notification messages are sent when a user selects, double-clicks, or cancels a list box item; when the list box receives or loses the keyboard focus; and when the system cannot allocate enough memory for a list box request. A notification message is sent as a WM_COMMAND_win32_WM_COMMAND message in which the low-order word of the wParam parameter contains the list box identifier, the high-order word of wParam contains the notification message, and the lParam parameter contains the control window handle.

Portability Issue The placement of notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x-based applications that process notification messages must be modified in this respect when they are ported to Win32-based applications.

A dialog box procedure is not required to process these messages; the default window procedure processes them.
An application should monitor and process the following list box notification messages.
Notification message Description
LBN_DBLCLK_win32_LBN_DBLCLK The user double-clicks an item in the list box.
LBN_ERRSPACE_win32_LBN_ERRSPACE The list box cannot allocate enough memory to fulfill a request.
LBN_KILLFOCUS_win32_LBN_KILLFOCUS The list box loses the keyboard focus.
LBN_SELCANCEL_win32_LBN_SELCANCEL The user cancels the selection of an item in the list box.
LBN_SELCHANGE_win32_LBN_SELCHANGE The selection in a list box is about to change.
LBN_SETFOCUS_win32_LBN_SETFOCUS The list box receives the keyboard focus.

+$#A Messages to List Boxes
A dialog box procedure can send messages to a list box to add, delete, examine, and change list box items. For example, a dialog box procedure could send an LB_ADDSTRING_win32_LB_ADDSTRING message to a list box to add an item, and an LB_GETSEL_win32_LB_GETSEL message to determine whether the item is selected. Other messages set and retrieve information about the size, appearance, and behavior of the list box. For example, the LB_SETHORIZONTALEXTENT_win32_LB_SETHORIZONTALEXTENT message sets the scrollable width of a list box. A dialog box procedure can send any message to a list box by using the SendMessage_win32_SendMessage or SendDlgItemMessage_win32_SendDlgItemMessage function.
A list box item is often referenced by its index, an integer that represents the item's position in the list box. The index of the first item in a list box is 0, the index of the second item is 1, and so on.
The following table describes how the predefined list box procedure responds to list box messages.
Message Response
LB_ADDFILE_win32_LB_ADDFILE Inserts a file into a directory list box filled by the DlgDirList_win32_DlgDirList function and retrieves the list box index of the inserted item.
LB_ADDSTRING_win32_LB_ADDSTRING Adds a string to a list box and returns its index.
LB_DELETESTRING_win32_LB_DELETESTRING Removes a string from a list box and returns the number of strings remaining in the list.
LB_DIR_win32_LB_DIR Adds a list of filenames to a list box and returns the index of the last filename added.
LB_FINDSTRING_win32_LB_FINDSTRING Returns the index of the first string in the list box that matches a given prefix.
LB_FINDSTRINGEXACT_win32_LB_FINDSTRINGEXACT Returns the index of the string that is equivalent to or prefixed by a given prefix.
LB_GETANCHORINDEX_win32_LB_GETANCHORINDEX Returns the index of the item that the mouse last selected.
LB_GETCARETINDEX_win32_LB_GETCARETINDEX Returns the index of the item that has the focus rectangle.
LB_GETCOUNT_win32_LB_GETCOUNT Returns the number of items in the list box.
LB_GETCURSEL_win32_LB_GETCURSEL Returns the index of the currently selected item.
LB_GETHORIZONTALEXTENT_win32_LB_GETHORIZONTALEXTENT Returns the scrollable width, in pixels, of a list box.
LB_GETITEMDATA_win32_LB_GETITEMDATA Returns the 32-bit value associated with the given item.
LB_GETITEMHEIGHT_win32_LB_GETITEMHEIGHT Returns the height, in pixels, of an item in a list box.
LB_GETITEMRECT_win32_LB_GETITEMRECT Retrieves the client coordinates of the given list box item.
LB_GETLOCALE_win32_LB_GETLOCALE Retrieves the locale of the list box. The high-order word contains the country code and the low-order word contains the language identifier.
LB_GETSEL_win32_LB_GETSEL Returns the selection state of a list box item.
LB_GETSELCOUNT_win32_LB_GETSELCOUNT Returns the number of selected items in a multiple-selection list box.
LB_GETSELITEMS_win32_LB_GETSELITEMS Creates an array of the indexes of all selected items in a multiple-selection list box and returns the total number of selected items.
LB_GETTEXT_win32_LB_GETTEXT Retrieves the string associated with a given item and the length of the string.
LB_GETTEXTLEN_win32_LB_GETTEXTLEN Returns the length, in characters, of the string associated with a given item.
LB_GETTOPINDEX_win32_LB_GETTOPINDEX Returns the index of the first visible item in a list box.
LB_INITSTORAGE_win32_LB_INITSTORAGE Allocates memory for the specified number of items and their associated strings.
LB_INSERTSTRING_win32_LB_INSERTSTRING Inserts a string at a given index in a list box.
LB_ITEMFROMPOINT_win32_LB_ITEMFROMPOINT Retrieves the zero-based index of the item nearest the specified point in a list box.
LB_RESETCONTENT_win32_LB_RESETCONTENT Removes all items from a list box.
LB_SELECTSTRING_win32_LB_SELECTSTRING Selects the first string it finds that matches a given prefix.
LB_SELITEMRANGE_win32_LB_SELITEMRANGE Selects a given range of items in a list box.
LB_SELITEMRANGEEX_win32_LB_SELITEMRANGEEX Selects a given range of items if the index of the first item in the range is less than the index of the last item in the range. Cancels the selection in the range if the index of the first item is greater than the last.
LB_SETANCHORINDEX_win32_LB_SETANCHORINDEX Sets the item that the mouse last selected to a given item.
LB_SETCARETINDEX_win32_LB_SETCARETINDEX Sets the focus rectangle to a given list box item.
LB_SETCOLUMNWIDTH_win32_LB_SETCOLUMNWIDTH Sets the width, in pixels, of all columns in a list box.
LB_SETCOUNT_win32_LB_SETCOUNT Sets the number of items in a list box.
LB_SETCURSEL_win32_LB_SETCURSEL Selects a given list box item.
LB_SETHORIZONTALEXTENT_win32_LB_SETHORIZONTALEXTENT Sets the scrollable width, in pixels, of a list box.
LB_SETITEMDATA_win32_LB_SETITEMDATA Associates a 32-bit value with a list box item.
LB_SETITEMHEIGHT_win32_LB_SETITEMHEIGHT Sets the height, in pixels, of an item or items in a list box.
LB_SETLOCALE_win32_LB_SETLOCALE Sets the locale of a list box and returns the previous locale identifier.
LB_SETSEL_win32_LB_SETSEL Selects an item in a multiple-selection list box.
LB_SETTABSTOPS_win32_LB_SETTABSTOPS Sets the tab stops to those specified in a given array.
LB_SETTOPINDEX_win32_LB_SETTOPINDEX Scrolls the list box so the specified item is at the top of the visible range.

+$#A Default Window-Message Processing
The window procedure for the predefined list box window class carries out default processing for all messages that the list box does not process. When the list box procedure returns FALSE for a message, the predefined window procedure checks the message and performs default actions, as shown in the following table.
Message Default action
WM_CHAR_win32_WM_CHAR Moves the selection to the first item that begins with the character the user typed. If the list box has the LBS_OWNERDRAW style, no action occurs. Multiple characters typed within a short interval are treated as a group, and the first item that begins with that series of characters is selected.
WM_CREATE_win32_WM_CREATE Creates an empty list box.
WM_DESTROY_win32_WM_DESTROY Destroys the list box and frees any resources it uses.
WM_DROPFILES_win32_WM_DROPFILES Passes the message to the dialog box procedure or parent window process.
WM_ENABLE_win32_WM_ENABLE If the control is visible, invalidates the rectangle so the strings can be painted gray.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erases the background of a list box. If the list box has the LBS_OWNERDRAW style, the background is not erased.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns DLGC_WANTARROWS | DLGC_WANTCHARS, indicating the default list box procedure processes the arrow keys and WM_CHAR_win32_WM_CHAR messages.
WM_GETFONT_win32_WM_GETFONT Returns a handle of the current font for the list box.
WM_HSCROLL_win32_WM_HSCROLL Scrolls the list box horizontally.
WM_KEYDOWN_win32_WM_KEYDOWN Processes virtual keys for scrolling. The virtual key is the index of the item to move the caret to. The selection is not changed.
WM_KILLFOCUS_win32_WM_KILLFOCUS Turns the caret off and destroys it. Sends an LBN_KILLFOCUS_win32_LBN_KILLFOCUS notification message to the owner of the list box.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.
WM_LBUTTONUP_win32_WM_LBUTTONUP Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.
WM_PAINT_win32_WM_PAINT Performs a subclassed paint operation by using the list box handle of the device context (DC).
WM_SETFOCUS_win32_WM_SETFOCUS Turns the caret on and sends an LBN_SETFOCUS_win32_LBN_SETFOCUS notification message to the owner of the list box.
WM_SETFONT_win32_WM_SETFONT Sets a new font for the list box.
WM_SETREDRAW_win32_WM_SETREDRAW Sets or clears the redraw flag based on the value of wParam.
WM_SIZE_win32_WM_SIZE Resizes the list box to an integral number of items.
WM_VSCROLL_win32_WM_VSCROLL Scrolls the list box vertically.

The predefined list box procedure passes all other messages to DefWindowProc_win32_DefWindowProc for default processing.
+$#A Using List Boxes
· Creating a simple list box_win32_Creating_a_Simple_List_Box
· Creating a directory listing in a single-selection list box_win32_Creating_a_Directory_Listing_in_a_Single_Selection_List_Box
· Creating a multiple-selection list box_win32_Creating_a_Multiple_Selection_List_Box
· Creating an owner-drawn list box_win32_Creating_an_Owner_Drawn_List_Box

+$#A Creating a Simple List Box
The following example demonstrates how a dialog box procedure creates a simple list box and fills it with the names of people on a softball team. When a name in the list is selected, additional information about the player is displayed in the dialog box. The following illustration shows the dialog box.
{ewc msdncd, EWGraphic, group10616 0 /a "SDK_01.bmp"}
The list box has the LBS_STANDARD style, a combination of LBS_SORT, LBS_NOTIFY, WS_VSCROLL, and WS_BORDER. The code initializes the dialog box while processing the WM_INITDIALOG_win32_WM_INITDIALOG message. For each name that appears in the list box, the code sends an LB_ADDSTRING_win32_LB_ADDSTRING message to the list box. By processing the LBN_SELCHANGE_win32_LBN_SELCHANGE notification message, the code also keeps track of when the selection changes.
#define BUFFER MAX_PATH

#define NAMELENGTH 15
#define POSITIONLENGTH 20
#define TEAMSIZE 15

typedef struct {
TCHAR tchName[NAMELENGTH];
TCHAR tchPosition[POSITIONLENGTH];
int nGamesPlayed;
int nInningsPlayed;
double xBattingAverage;
TCHAR tchFoodName[NAMELENGTH];
} Player;

Player Roster[] = {
{"Pete", "shortstop", 26, 90, .608, "Rutabaga"},
{"Suzanna", "catcher", 16, 53, .286, "Toast"},
{"Jack", "pitcher", 27, 110, .542, "Animal Crackers"},
{"Karen", "second base", 26, 140, .238, "Pez"},
{"Dave", "first base", 28, 138, .508, "Suds"},
{"Wendy", "third base", 25, 154, .493, "Ham"},
{"Matt", "shortstop", 24, 112, .579, "Oats"},
{"Jenny", "right field", 22, 101, .509, "Mashed Potatoes"},
{"Seth", "left-center field", 20, 76, .407, "Otter Pop"},
{"Kathie", "left field", 26, 127, .353, "Baba Ganouj"},
{"Colin", "pitcher", 26, 96, .456, "Lefse"},
{"Penny", "right field", 24, 112, .393, "Zotz"},
{"Art", "left-center field", 17, 56, .375, "Cannelloni"},
{"Cindy", "second base", 13, 58, .207, "Tequila"},
{"David", "center field", 18, 101, .612, "Bok Choy"}
};

/*
* FUNCTION: DlgTeamProc(HWND, unsigned, UINT, LONG)
*
* PURPOSE: Dialog box for "BFG Softball Statistics"
*/

BOOL APIENTRY DlgTeamProc(
HWND hDlg, /* window handle of dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam) /* message-specific information */
{
TCHAR tchBuffer[BUFFER];
int nItem;
int i;
HWND hwndList;

switch (message) {

case WM_INITDIALOG:
{
hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);

/* Initialize the list box (fill it with player names). */

for (i = 0; i < TEAMSIZE; i++) {
SendMessage(hwndList, LB_ADDSTRING, 0,
(LPARAM) Roster[i].tchName);
SendMessage(hwndList, LB_SETITEMDATA, i, (LPARAM) i);

}
SetFocus(hwndList);
return FALSE;
}

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDL_SOFTBALL:

switch (HIWORD(wParam)) {

case LBN_SELCHANGE:

/* Show the selected player's statistics. */

hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);
nItem = SendMessage(hwndList, LB_GETCURSEL,
0, 0);
i = SendMessage(hwndList, LB_GETITEMDATA,
nItem, 0);

SetDlgItemText(hDlg, IDS_POS,
Roster[i].tchPosition);
SetDlgItemText(hDlg, IDS_GAME,
_itoa(Roster[i].nGamesPlayed,
tchBuffer, 10));
SetDlgItemText(hDlg, IDS_INN,
_itoa(Roster[i].nInningsPlayed,
tchBuffer, 10));
SetDlgItemText(hDlg, IDS_BA,
_gcvt(Roster[i].xBattingAverage,
3, tchBuffer));
SetDlgItemText(hDlg, IDS_FOOD,
Roster[i].tchFoodName);

return TRUE;
}
break;

case IDOK:
case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);
return TRUE;

default:
return FALSE;
}

default:
return FALSE;
}
}

+$#A Creating a Directory Listing in a Single-Selection List Box
The following example demonstrates how to display the content of the current directory in a list box and enable the user to delete one file at a time.
In addition to the standard list box styles, this list box has the LBS_MULTICOLUMN and LBS_HSCROLL styles. The code initializes the list box by using the DlgDirList_win32_DlgDirList function to fill the list box with the names of all the files in the current directory. When the user chooses the Delete button, the DlgDirSelectEx_win32_DlgDirSelectEx function retrieves the name of the selected file. The code deletes the file by using the DeleteFile_win32_DeleteFile function and updates the directory list box by sending the LB_DELETESTRING_win32_LB_DELETESTRING message.
#define BUFFER MAX_PATH

/*
* FUNCTION: DlgDelFileProc(HWND, unsigned, UINT, LONG)
*
* PURPOSE: "Delete File" dialog box
*/

BOOL APIENTRY DlgDelFileProc(
HWND hDlg, /* window handle of dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{

DWORD cchCurDir;
LPTSTR lpszCurDir;
LPTSTR lpszFileToDelete;
int nItem;
int nTotal;
TCHAR tchBuffer[BUFFER];
BOOL fResult;

switch (message) {

case WM_INITDIALOG:

/*
* Initialize the list box by filling it with files from
* the current directory.
*/

lpszCurDir = tchBuffer;
GetCurrentDirectory(cchCurDir, lpszCurDir);
DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);
SetFocus(GetDlgItem(hDlg, IDL_FILES));
return FALSE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

/*
* When the user presses the DEL (IDOK) button,
* delete the selected file.
*/

lpszFileToDelete = tchBuffer;

DlgDirSelectEx(hDlg, lpszFileToDelete, MAX_PATH,
IDL_FILES);
fResult = DeleteFile(lpszFileToDelete);
if (!fResult) {
MessageBox(hDlg, "Could not delete file.",
NULL, MB_OK);
}
else {
nItem = SendMessage(GetDlgItem(hDlg, IDL_FILES),
LB_GETCURSEL, 0, 0);

nTotal = SendMessage(GetDlgItem(hDlg,
IDL_FILES), LB_DELETESTRING, nItem, 0);

/* Update the list box. */

if (nTotal > nItem) {
SendMessage(GetDlgItem(hDlg, IDL_FILES),
LB_SETCURSEL, nItem, 0);
}
else {
SendMessage(GetDlgItem(hDlg, IDL_FILES),
LB_SETCURSEL, nTotal, 0);
}
}
return TRUE;

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);
return TRUE;

default:
return FALSE;
}

default:
return FALSE;
}
}

+$#A Creating a Multiple-Selection List Box
The following example displays and initializes the dialog box used in the preceding example. However, this code uses the LBS_MULTIPLESEL style to enable the user to select more than one file at a time. When the user chooses the Delete button, the example sends the LB_GETSELCOUNT_win32_LB_GETSELCOUNT message (to retrieve the number of files selected) and the LB_GETSELITEMS_win32_LB_GETSELITEMS message (to retrieve an array of selected list box items). After deleting a file, the code removes the corresponding item from the list box by sending the LB_DELETESTRING_win32_LB_DELETESTRING message.
#define BUFFER MAX_PATH

#define BIGBUFF 8192

/*
* FUNCTION: DlgDelFilesProc(HWND, unsigned, UINT, LONG)
*
* PURPOSE: "Delete files" dialog box
*/

BOOL APIENTRY DlgDelFilesProc(
HWND hDlg, /* window handle of dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{
DWORD cchCurDir;
LPTSTR lpszCurDir;
LPTSTR lpszFileToDelete;
int nSelItems;
int nSelItemsInBuffer;
TCHAR tchBuffer[BUFFER];
TCHAR tchMsgBuff[BUFFER];
int nBuffer[BIGBUFF];
int i;
BOOL fResult;
HWND hListBox;

switch (message) {

case WM_INITDIALOG:

/*
* Initialize the list box by filling it with files from
* the current directory.
*/

lpszCurDir = tchBuffer;
GetCurrentDirectory(cchCurDir, lpszCurDir);
DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);

SetFocus(GetDlgItem(hDlg, IDL_FILES));

return FALSE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

/*
* When the user presses the Delete (IDOK)
* button, delete all the selected files.
*/

lpszFileToDelete = tchBuffer;

hListBox = GetDlgItem(hDlg, IDL_FILES);
nSelItems = SendMessage(hListBox,
LB_GETSELCOUNT, 0, 0);

nSelItemsInBuffer = SendMessage(hListBox,
LB_GETSELITEMS, 512, (LPARAM) nBuffer);

if (nSelItems > nSelItemsInBuffer) {
MessageBox(hDlg, "Too many items selected.",
NULL, MB_OK);
}
else {

/*
* Go through the list backwards because after
* deleting an item the indices change for every
* subsequent item. By going backward, the
* indice are never invalidated.
*/

for (i = nSelItemsInBuffer - 1; i >= 0; i--) {
SendMessage(hListBox, LB_GETTEXT,
nBuffer[i],
(LPARAM) lpszFileToDelete);

fResult = DeleteFile(lpszFileToDelete);
if (!fResult) {
sprintf(tchMsgBuff,
"Could not delete file: %s "
"GetLastError = %u",
(LPARAM) lpszFileToDelete);

/* Call app-defined error handler. */

ErrorHandler(tchMsgBuff);
}
else {
SendMessage(hListBox, LB_DELETESTRING,
nBuffer[i], 0);
}
}
SendMessage(hListBox, LB_SETCARETINDEX, 0, 0);
}
return TRUE;

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);
return TRUE;

default:
return FALSE;
}

default:
return FALSE;
}
}

+$#A Creating an Owner-Drawn List Box
The following example shows how to draw a list box that contains five owner-drawn items: four drawing implements and a fork. Each list item appears as a bitmap followed by the name of the object. A button prompts the user to select one item that is not like the others. Choosing the button with the fork selected displays a "You're right!" message and closes the dialog box. Choosing the button with any other list item selected displays a "Try again!" message.
The list box has the LBS_OWNERDRAW and LBS_HASSTRINGS styles, in addition to the standard list box styles. The code initializes the list box by sending the LB_ADDSTRING_win32_LB_ADDSTRING message to set the text, and then sends the LB_SETITEMDATA_win32_LB_SETITEMDATA message to associate a bitmap with each list box item. The code also sets the height of each list box item by processing the WM_MEASUREITEM_win32_WM_MEASUREITEM message and draws the text and bitmap for each item by processing the WM_DRAWITEM_win32_WM_DRAWITEM message.
#define XBITMAP 80
#define YBITMAP 20

#define BUFFER MAX_PATH

HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork;
HBITMAP hbmpPicture, hbmpOld;

void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp)
{
int nItem;

nItem = SendMessage(hwndList, LB_ADDSTRING, 0, lpstr);
SendMessage(hwndList, LB_SETITEMDATA, nItem, hbmp);
}

DWORD APIENTRY DlgDrawProc(
HWND hDlg, /* window handle of dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{
int nItem;
TCHAR tchBuffer[BUFFER];
HBITMAP hbmp;
HWND hListBox;
TEXTMETRIC tm;
int y;
HDC hdcMem;
LPMEASUREITEMSTRUCT lpmis;
LPDRAWITEMSTRUCT lpdis;
RECT rcBitmap;

switch (message) {

case WM_INITDIALOG:

/* Load bitmaps. */

hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700));
hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701));
hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702));
hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703));
hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704));

/* Retrieve list box handle. */

hListBox = GetDlgItem(hDlg, IDL_STUFF);

/*
* Initialize the list box text and associate a bitmap
* with each list box item.
*/

AddItem(hListBox, "pencil", hbmpPencil);
AddItem(hListBox, "crayon", hbmpCrayon);
AddItem(hListBox, "marker", hbmpMarker);
AddItem(hListBox, "pen", hbmpPen);
AddItem(hListBox, "fork", hbmpFork);

SetFocus(hListBox);
SendMessage(hListBox, LB_SETCURSEL, 0, 0);
return TRUE;

case WM_MEASUREITEM:

lpmis = (LPMEASUREITEMSTRUCT) lParam;

/* Set the height of the list box items. */

lpmis->itemHeight = 20;
return TRUE;

case WM_DRAWITEM:

lpdis = (LPDRAWITEMSTRUCT) lParam;

/* If there are no list box items, skip this message. */

if (lpdis->itemID == -1) {
break;
}

/*
* Draw the bitmap and text for the list box item. Draw a
* rectangle around the bitmap if it is selected.
*/

switch (lpdis->itemAction) {

case ODA_SELECT:
case ODA_DRAWENTIRE:

/* Display the bitmap associated with the item. */

hbmpPicture = (HBITMAP) SendMessage(lpdis->hwndItem,
LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0);

hdcMem = CreateCompatibleDC(lpdis->hDC);
hbmpOld = SelectObject(hdcMem, hbmpPicture);

BitBlt(lpdis->hDC,
lpdis->rcItem.left, lpdis->rcItem.top,
lpdis->rcItem.right - lpdis->rcItem.left,
lpdis->rcItem.bottom - lpdis->rcItem.top,
hdcMem, 0, 0, SRCCOPY);

/* Display the text associated with the item. */

SendMessage(lpdis->hwndItem, LB_GETTEXT,
lpdis->itemID, (LPARAM) tchBuffer);

GetTextMetrics(lpdis->hDC, &tm);

y = (lpdis->rcItem.bottom + lpdis->rcItem.top -
tm.tmHeight) / 2;

TextOut(lpdis->hDC,
XBITMAP + 6,
y,
tchBuffer,
strlen(tchBuffer));

SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);

/* Is the item selected? */

if (lpdis->itemState & ODS_SELECTED) {

/*
* Set RECT coordinates to surround only the
* bitmap.
*/

rcBitmap.left = lpdis->rcItem.left;
rcBitmap.top = lpdis->rcItem.top;
rcBitmap.right = lpdis->rcItem.left + XBITMAP;
rcBitmap.bottom = lpdis->rcItem.top + YBITMAP;

/*
* Draw a rectangle around bitmap to indicate
* the selection.
*/

DrawFocusRect(lpdis->hDC, &rcBitmap);
}
break;

case ODA_FOCUS:

/*
* Do not process focus changes. The focus caret
* (outline rectangle) indicates the selection.
* The Which one? (IDOK) button indicates the final
* selection.
*/

break;
}
return TRUE;

case WM_COMMAND:

switch (LOWORD(wParam)) {
case IDOK:

/* Get the selected item's text. */

nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETCURSEL, 0, (LPARAM) 0);
hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETITEMDATA, nItem, 0);

/*
* If the item is not the correct answer, tell the
* user to try again.
*
* If the item is the correct answer, congratulate
* the user and destroy the dialog box.
*/

if (hbmp != hbmpFork) {
MessageBox(hDlg, "Try again!", "Oops.", MB_OK);
return FALSE;
}
else {
MessageBox(hDlg, "You're right!",
"Congratulations.", MB_OK);

/* Fall through. */

}

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);
return TRUE;

default:

return FALSE;
}

case WM_DESTROY:

/* Free any resources used by the bitmaps. */

DeleteObject(hbmpPencil);
DeleteObject(hbmpCrayon);
DeleteObject(hbmpMarker);
DeleteObject(hbmpPen);
DeleteObject(hbmpFork);

return TRUE;

default:
return FALSE;

}
return FALSE;
}

+$#AK List Box Reference
The following functions and messages are associated with list boxes.
+$#A List Box Functions
Following are the functions used with list boxes.
DlgDirList_win32_DlgDirListDlgDirSelectEx_win32_DlgDirSelectEx
+$#A List Box Messages
Following are the messages used with list boxes.
LB_ADDFILE_win32_LB_ADDFILELB_ADDSTRING_win32_LB_ADDSTRINGLB_DELETESTRING_win32_LB_DELETESTRINGLB_DIR_win32_LB_DIRLB_FINDSTRING_win32_LB_FINDSTRINGLB_FINDSTRINGEXACT_win32_LB_FINDSTRINGEXACTLB_GETANCHORINDEX_win32_LB_GETANCHORINDEXLB_GETCARETINDEX_win32_LB_GETCARETINDEXLB_GETCOUNT_win32_LB_GETCOUNTLB_GETCURSEL_win32_LB_GETCURSELLB_GETHORIZONTALEXTENT_win32_LB_GETHORIZONTALEXTENTLB_GETITEMDATA_win32_LB_GETITEMDATALB_GETITEMHEIGHT_win32_LB_GETITEMHEIGHTLB_GETITEMRECT_win32_LB_GETITEMRECTLB_GETLOCALE_win32_LB_GETLOCALELB_GETSEL_win32_LB_GETSELLB_GETSELCOUNT_win32_LB_GETSELCOUNTLB_GETSELITEMS_win32_LB_GETSELITEMSLB_GETTEXT_win32_LB_GETTEXTLB_GETTEXTLEN_win32_LB_GETTEXTLENLB_GETTOPINDEX_win32_LB_GETTOPINDEXLB_INSERTSTRING_win32_LB_INSERTSTRINGLB_RESETCONTENT_win32_LB_RESETCONTENTLB_SELECTSTRING_win32_LB_SELECTSTRINGLB_SELITEMRANGE_win32_LB_SELITEMRANGELB_SELITEMRANGEEX_win32_LB_SELITEMRANGEEXLB_SETANCHORINDEX_win32_LB_SETANCHORINDEXLB_SETCARETINDEX_win32_LB_SETCARETINDEXLB_SETCOLUMNWIDTH_win32_LB_SETCOLUMNWIDTHLB_SETCOUNT_win32_LB_SETCOUNTLB_SETCURSEL_win32_LB_SETCURSELLB_SETHORIZONTALEXTENT_win32_LB_SETHORIZONTALEXTENTLB_SETITEMDATA_win32_LB_SETITEMDATALB_SETITEMHEIGHT_win32_LB_SETITEMHEIGHTLB_SETLOCALE_win32_LB_SETLOCALELB_SETSEL_win32_LB_SETSELLB_SETTABSTOPS_win32_LB_SETTABSTOPSLB_SETTOPINDEX_win32_LB_SETTOPINDEXLBN_DBLCLK_win32_LBN_DBLCLKLBN_ERRSPACE_win32_LBN_ERRSPACELBN_KILLFOCUS_win32_LBN_KILLFOCUSLBN_SELCANCEL_win32_LBN_SELCANCELLBN_SELCHANGE_win32_LBN_SELCHANGELBN_SETFOCUS_win32_LBN_SETFOCUSWM_CHARTOITEM_win32_WM_CHARTOITEMWM_DELETEITEM_win32_WM_DELETEITEMWM_VKEYTOITEM_win32_WM_VKEYTOITEM

+$#AK Scroll Bars
A window in an application written for Microsoft® Windows® can display a data object, such as a document or a bitmap, that is larger than the window's client area. When provided with a scroll bar, the user can scroll a data object in the client area to bring into view the portions of the object that extend beyond the borders of the window.
+$#A About Scroll Bars
Scroll bars should be included in any window for which the content of the client area extends beyond the window's borders. A scroll bar's orientation determines the direction in which scrolling occurs when the user operates the scroll bar. A horizontal scroll bar enables the user to scroll the content of a window to the left or right. A vertical scroll bar enables the user to scroll the content up or down.
+$#A Parts of a Scroll Bar
A scroll bar consists of a shaded shaft with an arrow button at each end and a scroll box (sometimes called a thumb) between the arrow buttons. A scroll bar represents the overall length or width of a data object in a window's client area; the scroll box represents the portion of the object that is visible in the client area. The position of the scroll box changes whenever the user scrolls a data object to display a different portion of it. In version 4.0 or later, Windows also adjusts the size of a scroll bar's scroll box so that it indicates what portion of the entire data object is currently visible in the window. If most of the object is visible, the scroll box occupies most of the scroll bar's shaft. Similarly, if only a small portion of the object is visible, the scroll box occupies a small part of the shaft.
The user scrolls the content of a window by clicking one of the arrow buttons, by clicking the area in the shaded shaft, or by dragging the scroll box. When the user clicks an arrow button, the application scrolls the content by one unit (typically a single line or column). When the user clicks the shaded areas, the application scrolls the content by one window. The amount of scrolling that occurs when the user drags the scroll box depends on the distance the user drags the scroll box and on the scrolling range of the scroll bar. For more information about the scrolling range, see Scroll Box Position and Scrolling Range_win32_Scroll_Box_Position_and_Scrolling_Range.
+$#A Standard Scroll Bars and Scroll Bar Controls
A scroll bar is included in a window either as a standard scroll bar or as a scroll bar control. A standard scroll bar is located in the nonclient area of a window. It is created with the window and displayed when the window is displayed. The sole purpose of a standard scroll bar is to enable the user to generate scrolling requests for viewing the entire content of the client area. You can include a standard scroll bar in a window by specifying WS_HSCROLL, WS_VSCROLL, or both styles when you create the window. The WS_HSCROLL style creates a horizontal scroll bar positioned at the bottom of the client area. The WS_VSCROLL style creates a vertical scroll bar positioned at the right of the client area. The SM_CXHSCROLL and SM_CYHSCROLL system metric values define the width and height of a standard horizontal scroll bar. The SM_CXVSCROLL and SM_CYVSCROLL values define the width and height of a standard vertical scroll bar.
A scroll bar control is a control window that belongs to the SCROLLBAR window class. A scroll bar control appears and functions like a standard scroll bar, but it is a separate window. As a separate window, a scroll bar control receives direct input focus, indicated by a flashing caret displayed in the scroll box. Unlike a standard scroll bar, a scroll bar control also has a built-in keyboard interface that enables the user to direct scrolling. You can use as many scroll bar controls as needed in a single window. When you create a scroll bar control, you must specify the scroll bar's size and position. However, if a scroll bar control's window can be resized, adjustments to the scroll bar's size must be made whenever the size of the window changes.
The advantage of using a standard scroll bar is that Windows creates the scroll bar and automatically sets its size and position. However, standard scroll bars are sometimes too restrictive. For example, suppose that you want to divide a client area into quadrants and use a separate set of scroll bars to control the content of each quadrant. You cannot use standard scroll bars because you can only create one set of scroll bars for a particular window. Use scroll bar controls instead, because you can add as many of them to a window as you want.
Applications can provide scroll bar controls for purposes other than scrolling the content of a window. For example, a screen saver application might provide a scroll bar for setting the speed at which graphics are moved about on the screen.
A scroll bar control can have a number of styles that serves to control the orientation and position of the scroll bar. You specify the styles that you want when you call the CreateWindowEx_win32_CreateWindowEx function to create a scroll bar control. Some of the styles create a scroll bar control that uses a default width or height. However, you must always specify the x- and y-coordinates and the other dimensions of the scroll bar. Following are the scroll bar control styles.
Style Meaning
SBS_BOTTOMALIGN
Aligns the bottom edge of the scroll bar with the bottom edge of the rectangle defined by the CreateWindowEx_win32_CreateWindowEx parameters x, y, nWidth, and nHeight. The scroll bar has the default height for system scroll bars. Use this style with the SBS_HORZ style.
SBS_HORZ
Designates a horizontal scroll bar. If neither the SBS_BOTTOMALIGN nor SBS_TOPALIGN style is specified, the scroll bar has the height, width, and position specified by the parameters of CreateWindowEx_win32_CreateWindowEx.
SBS_LEFTALIGN
Aligns the left edge of the scroll bar with the left edge of the rectangle defined by the parameters of CreateWindowEx_win32_CreateWindowEx. The scroll bar has the default width for system scroll bars. Use this style with the SBS_VERT style.
SBS_RIGHTALIGN
Aligns the right edge of the scroll bar with the right edge of the rectangle defined by the parameters of CreateWindowEx_win32_CreateWindowEx. The scroll bar has the default width for system scroll bars. Use this style with the SBS_VERT style.
SBS_SIZEBOX
Designates a size box. If you specify neither the SBS_SIZEBOXBOTTOMRIGHTALIGN nor the SBS_SIZEBOXTOPLEFTALIGN style, the size box has the height, width, and position specified by the parameters of CreateWindowEx_win32_CreateWindowEx.
SBS_SIZEBOXBOTTOMRIGHTALIGN
Aligns the lower right corner of the size box with the lower right corner of the rectangle specified by the parameters of CreateWindowEx_win32_CreateWindowEx. The size box has the default size for system size boxes. Use this style with the SBS_SIZEBOX style.
SBS_SIZEBOXTOPLEFTALIGN
Aligns the upper left corner of the size box with the upper left corner of the rectangle specified by the parameters of CreateWindowEx_win32_CreateWindowEx. The size box has the default size for system size boxes. Use this style with the SBS_SIZEBOX style.
SBS_SIZEGRIP
Same as SBS_SIZEBOX, but with a raised edge (version 4.0 or later).
SBS_TOPALIGN
Aligns the top edge of the scroll bar with the top edge of the rectangle defined by the parameters of CreateWindowEx_win32_CreateWindowEx. The scroll bar has the default height for system scroll bars. Use this style with the SBS_HORZ style.
SBS_VERT
Designates a vertical scroll bar. If you specify neither the SBS_RIGHTALIGN nor the SBS_LEFTALIGN style, the scroll bar has the height, width, and position specified by the parameters of CreateWindowEx_win32_CreateWindowEx.

+$#A Scroll Box Position and Scrolling Range
The position of the scroll box is represented as an integer; it is relative to the left or upper end of the scroll bar, depending on whether the scroll bar is horizontal or vertical. The position must be within the minimum and maximum values of the scrolling range. For example, in a scroll bar with a range of 0 through 100, position 50 is in the middle, with the remaining positions distributed equally along the scroll bar. The initial range depends on the scroll bar. Standard scroll bars have an initial range of 0 through 100; scroll bar controls have an empty range (both minimum and maximum values are zero), unless you supply an explicit range when the control is created. You can change the range at any time. With operating system version 4.0 or later, you can use the SetScrollInfo_win32_SetScrollInfo function to set the range values, and the GetScrollInfo_win32_GetScrollInfo function to retrieve the current range values. In versions earlier than 4.0, you can use the SetScrollRange_win32_SetScrollRange and GetScrollRange_win32_GetScrollRange functions.
An application typically adjusts the scroll range to convenient integers, making it easy to translate the scroll box position into a value corresponding to the data object to be scrolled. For example, if an application must display 260 lines of a text file in a window that can show only 16 lines at a time, the vertical scroll bar range can be set to 1 through 244. If the scroll box is at position 1, the first line will be at the top of the window. If the scroll box is at position 244, the last line (line 260) will be at the bottom of the window. If an application attempts to specify a position value that is less than the minimum or more than the maximum, the minimum or maximum scrolling range value is used instead.
In version 4.0 or later, an application can set a page size for a scroll bar. The page size represents the number of data units that can fit in the client area of the owner window given its current size. For example, if the client area can hold 16 lines of text, an application would set the page size to 16. Windows uses the page size, along with the scrolling range and length of the scroll shaft, to set the size of the scroll box. Whenever a window containing a scroll bar is resized, an application should call the SetScrollInfo function to set the page size. An application can retrieve the current page size by calling the sending GetScrollInfo function.
To establish a useful relationship between the scroll bar range and the data object, an application must adjust the range whenever the size of the data object changes.
As the user moves the scroll box in a scroll bar, the scroll bar reports the scroll box position as an integer in the scrolling range. If the position is the minimum value, the scroll box is at the top of a vertical scroll bar or at the left end of a horizontal scroll bar. If the position is the maximum value, the scroll box is at the bottom of a vertical scroll bar or at right end of a horizontal scroll bar.
In version 4.0 or later, the maximum value that a scroll bar can report (that is, the maximum scrolling position) depends on the page size. If the scroll bar has a page size greater than one, the maximum scrolling position is less than the maximum range value. You can use the following formula to calculate the maximum scrolling position:
MaxScrollPos = MaxRangeValue - (PageSize - 1)

An application must move the scroll box in a scroll bar. Although the user makes a request for scrolling in a scroll bar, the scroll bar does not automatically update the scroll box position. Instead, it passes the request to the parent window, which must scroll the data and update the scroll box position. In version 4.0 or later, an application uses the SetScrollInfo_win32_SetScrollInfo function to update the scroll box position; otherwise, it uses the SetScrollPos_win32_SetScrollPos function. Because it controls the scroll box movement, the application can move the scroll box in increments that work best for the data being scrolled.
+$#A Scroll Bar Visibility
The system hides and disables a standard scroll bar when equal minimum and maximum values are specified. With version 4.0 or later, the system also hides and disables a standard scroll bar if you specify a page size that includes the entire scroll range of the scroll bar. This is the way to temporarily hide a scroll bar when it is not needed for the content of the client area. There is no need to make scrolling requests through the scroll bar when it is hidden. The system enables the scroll bar and shows it again when you set the minimum and maximum values to unequal values or, in version 4.0 or later, when the page size that does not include the entire scroll range. The ShowScrollBar_win32_ShowScrollBar function can also be used to hide or show a scroll bar. It does not affect the scroll bar's range, page size, or scroll box position.
The EnableScrollBar_win32_EnableScrollBar function can be used to disable one or both arrows of a scroll bar. An application displays disabled arrows in gray and does not respond to user input.
+$#A Scroll Bar Requests
The user makes scrolling requests by clicking various parts of a scroll bar. Windows sends the request to the given window in the form of a WM_HSCROLL_win32_WM_HSCROLL or WM_VSCROLL_win32_WM_VSCROLL message. A horizontal scroll bar sends the WM_HSCROLL message; a vertical scroll bar sends the WM_VSCROLL message. Each message includes a notification code that corresponds to the user's action, to the handle of the scroll bar (scroll bar controls only), and, in some cases, to the position of the scroll box.
The following figure shows the notification messages that the user generates when clicking various parts of a scroll bar.
{ewc msdncd, EWGraphic, group10617 0 /a "SDK_02.bmp"}
The scroll bar notification codes specify the action the user takes. An application examines the codes that accompany the WM_HSCROLL and WM_VSCROLL messages and then performs the appropriate scrolling operation. In the following table of notification messages, the user's action is specified for each message, followed by the application's response. In each case, a unit is defined by the application as appropriate for the given data. For example, the typical unit for scrolling text vertically is a line of text.
Notification message Action Response
SB_LINEUP The user clicks the top scroll arrow. Decrements the scroll box position; scrolls toward the top of the data by one unit.
SB_LINEDOWN The user clicks the bottom scroll arrow. Increments the scroll box position; scrolls toward the bottom of the data by one unit.
SB_LINELEFT The user clicks the left scroll arrow. Decrements the scroll box position; scrolls toward the left end of the data by one unit.
SB_LINERIGHT The user clicks the right scroll arrow. Increments the scroll box position; scrolls toward the right end of the data by one unit.
SB_PAGEUP The user clicks the scroll bar shaft above the scroll box. Decrements the scroll box position by the number of data units in the window; scrolls toward the top of the data by the same number of units.
SB_PAGEDOWN The user clicks the scroll bar shaft below the scroll box. Increments the scroll box position by the number of data units in the window; scrolls toward the bottom of the data by the same number of units.
SB_PAGELEFT The user clicks the scroll bar shaft to the left of the scroll box. Decrements the scroll box position by the number of data units in the window; scrolls toward the left end of the data by the same number of units.
SB_PAGERIGHT The user clicks the scroll bar shaft to the right of the scroll box. Increments the scroll box position by the number of data units in the window; scrolls toward the right end of the data by the same number of units.
SB_THUMBPOSITION The user releases the scroll box after dragging it. Sets the scroll box to the position given in the message; scrolls the data by the same number of units the scroll box has moved.
SB_THUMBTRACK The user drags the scroll box. Sets the scroll box to the position given in the message and scrolls the data by the same number of units the scroll box has moved for applications that draw data quickly. Applications that cannot draw data quickly must wait for the SB_THUMBPOSITION message before moving the scroll box and scrolling the data.
SB_ENDSCROLL The user releases the mouse after holding it on an arrow or in the scroll bar shaft. No response is needed.

A scroll bar generates the SB_THUMBPOSITION and SB_THUMBTRACK notification messages when the user clicks and drags the scroll box. An application should be programmed to process either the SB_THUMBTRACK or the SB_THUMBPOSITION notification message.
The SB_THUMBPOSITION notification message occurs when the user releases the mouse button after clicking the scroll box. An application that processes this message performs the scrolling operation after the user has dragged the scroll box to the desired position and released the mouse button.
SB_THUMBTRACK notification messages occur as the user drags the scroll box. If an application processes SB_THUMBTRACK messages, it can scroll the content of a window as the user drags the scroll box. However, a scroll bar can generate many SB_THUMBTRACK notification messages in a short period, so an application should process these messages only if it can quickly repaint the content of the window.
+$#A Keyboard Interface for a Scroll Bar
A scroll bar control provides a built-in keyboard interface that enables the user to issue scrolling requests by using the keyboard; a standard scroll bar does not. When a scroll bar control has the keyboard focus, it sends WM_HSCROLL_win32_WM_HSCROLL and WM_VSCROLL_win32_WM_VSCROLL messages to its parent window when the user presses the arrow keys. The notification message is sent with each message corresponding to the arrow key the user has pressed. Following are the arrow keys and their corresponding notification messages.
Arrow key Notification message
DOWN SB_LINEDOWN or SB_LINERIGHT
END SB_BOTTOM
HOME SB_TOP
LEFT SB_LINEUP or SB_LINELEFT
PGDN SB_PAGEDOWN or SB_PAGERIGHT
PGUP SB_PAGEUP or SB_PAGELEFT
RIGHT SB_LINEDOWN or SB_LINERIGHT
UP SB_LINEUP or SB_LINELEFT

Note that the keyboard interface of a scroll bar control sends the SB_TOP and SB_BOTTOM notification messages. The SB_TOP message indicates that the user has reached the top value of the scrolling range. An application scrolls the window content downward so that the top of the data object is visible. The SB_BOTTOM message indicates that the user has reached the bottom value of the scrolling range. If an application processes the SB_BOTTOM message, it scrolls the window content upward so that the bottom of the data object is visible.
If you want a keyboard interface for a standard scroll bar, you can create one yourself by processing the WM_KEYDOWN_win32_WM_KEYDOWN message in your window procedure and then performing the appropriate scrolling action based on the virtual-key code that accompanies the message. For information about how to create a keyboard interface for a scroll bar, see Creating a Keyboard Interface for a Standard Scroll Bar_win32_Creating_a_Keyboard_Interface_for_a_Standard_Scroll_Bar.
+$#A Scrolling the Client Area
The simplest way to scroll the content of a client area is to erase and then redraw it. This is the method an application is likely to use with SB_PAGEUP, SB_PAGEDOWN, and SB_TOP notification messages, which typically require completely new content.
For some notification messages, such as SB_LINEUP and SB_LINEDOWN, not all the content need be erased, because some remains visible after scrolling occurs. The ScrollWindowEx_win32_ScrollWindowEx function preserves a portion of the client area's content, move the preserved portion a specified amount, and then prepares the rest of the client area for painting new information. ScrollWindowEx uses the BitBlt_win32_BitBlt function to move a specific part of the data object to a new location within the client area. Any uncovered part of the client area (anything not preserved) is invalidated, erased, and painted when the next WM_PAINT_win32_WM_PAINT message occurs.
The ScrollWindowEx function can be used to exclude a portion of the client area from the scrolling operation. This keeps items with fixed positions, such as child windows, from moving within the client area. It automatically invalidates the portion of the client area that is to receive the new information, so the application does not have to compute its own clipping regions. For more information on clipping, see Clipping_win32_Clipping.
Usually an application scrolls the content of a window in the direction opposite that indicated by the scroll bar. For example, when the user clicks the shaft in the area below the scroll box, an application scrolls the object in the window upward to reveal a portion of the object that is below the visible portion.
+$#A Scroll Bar Colors and Metrics
The system-defined color value, COLOR_SCROLLBAR, controls the color within a scroll bar's shaft. Use the GetSysColor_win32_GetSysColor function to determine the color of the shaft and the SetSysColors_win32_SetSysColors function to set the color of the shaft. Note, however, that this change of color affects all scroll bars in the system.
You can get the dimensions of the bitmaps that Windows uses in standard scroll bars by calling the GetSystemMetrics_win32_GetSystemMetrics function. Following are the system metric values associated with scroll bars.
System metric Description
SM_CXHSCROLL Width of arrow bitmap on horizontal scroll bar
SM_CXHTHUMB Width of scroll box on horizontal scroll bar. In version 4.0 or later, this value retrieves the width a scroll bar that has a page size of zero.
SM_CXVSCROLL Width of arrow bitmap on vertical scroll bar
SM_CYHSCROLL Height of arrow bitmap on horizontal scroll bar
SM_CYVSCROLL Height of arrow bitmap on vertical scroll bar
SM_CYVTHUMB Height of scroll box on vertical scroll bar. In version 4.0 or later, this value retrieves the height of a scroll bar that has a page size of zero.

+$#A Using Scroll Bars
· Creating scroll bars_win32_Creating_Scroll_Bars
· Scrolling text_win32_Scrolling_Text
· Scrolling a bitmap_win32_Scrolling_a_Bitmap
· Creating a keyboard interface for a standard scroll bar_win32_Creating_a_Keyboard_Interface_for_a_Standard_Scroll_Bar

Note The examples in this section set the scroll range, scroll position, and page size by using SetScrollInfo_win32_SetScrollInfo function, which is available only with version 4.0 and later. For operating system versions earlier than 4.0, use the SetScrollRange_win32_SetScrollRange and SetScrollPos_win32_SetScrollPos functions.

+$#A Creating Scroll Bars
When creating an overlapped, pop-up, or child window, you can add standard scroll bars by using the CreateWindowEx_win32_CreateWindowEx function and specifying WS_HSCROLL, WS_VSCROLL, or both styles. Doing this adds a horizontal or vertical scroll bar, or both, to the window. The following example creates a window with standard horizontal and vertical scroll bars.
hwnd = CreateWindowEx(
0L, /* no extended styles */
"MyAppClass", /* window class */
"Scroll Bar Application", /* text for window title bar */
WS_OVERLAPPEDWINDOW | /* window styles */
WS_HSCROLL |
WS_VSCROLL,
CW_USEDEFAULT, /* default horizontal position */
CW_USEDEFAULT, /* default vertical position */
CW_USEDEFAULT, /* default width */
CW_USEDEFAULT, /* default height */
(HWND) NULL, /* no parent for overlapped windows */
(HMENU) NULL, /* window class menu */
hinst, /* instance owning this window */
(LPVOID) NULL /* pointer not needed */
);

To process scroll bar messages for these scroll bars, you must include appropriate code in the main window procedure.
When using the CreateWindowEx_win32_CreateWindowEx function to create a window, you can add a scroll bar control by specifying the SCROLLBAR window class. This creates a horizontal or vertical scroll bar, depending on whether SBS_HORZ or SBS_VERT is specified as the window style. The scroll bar size and its position relative to its parent window can also be specified. The following example creates a horizontal scroll bar control and positions it in the upper right corner of the window.
hwndScroll = CreateWindowEx(
0L, /* no extended styles */
"SCROLLBAR", /* scroll bar control class */
(LPSTR) NULL, /* text for window title bar */
WS_CHILD | SBS_HORZ, /* scroll bar styles */
0, /* horizontal position */
0, /* vertical position */
200, /* width of the scroll bar */
CW_USEDEFAULT, /* default height */
hwnd, /* handle of main window */
(HMENU) NULL, /* no menu for a scroll bar */
hinst, /* instance owning this window */
(LPVOID) NULL /* pointer not needed */
);

+$#A Scrolling Text
This section describes the changes you can make to an application's main window procedure to enable a user to scroll text. The example in Example of Scrolling Text_win32_Example_of_Scrolling_Text creates and displays an array of text strings and processes WM_HSCROLL_win32_WM_HSCROLL and WM_VSCROLL_win32_WM_VSCROLL messages generated by the scroll bars so that the user can scroll text both vertically and horizontally.
+$#A Scrolling Text with the WM_CREATE Message
Scrolling units are typically set while processing the WM_CREATE_win32_WM_CREATE message. It is convenient to base the scrolling units on the dimensions of the font associated with the window's display context (DC). To retrieve the font dimensions for a specific DC, use the GetTextMetrics_win32_GetTextMetrics function.
In the example in Example of Scrolling Text_win32_Example_of_Scrolling_Text, one vertical scrolling unit is equivalent to the height of a character cell, plus external leading. One horizontal scrolling unit is equivalent to the average width of a character cell. The horizontal scrolling positions, therefore, do not correspond to actual characters, unless the screen font is fixed-width.
+$#A Scrolling Text with the WM_SIZE Message
When processing the WM_SIZE_win32_WM_SIZE message, it is convenient to adjust the scrolling range and scrolling position to reflect the dimensions of the client area as well as the number of lines of text that will be displayed.
The SetScrollInfo_win32_SetScrollInfo function sets the minimum and maximum position values, the page size, and the scrolling position for a scroll bar. For versions earlier than 4.0, use the SetScrollRange_win32_SetScrollRange function to set the minimum and maximum position values, and the SetScrollPos_win32_SetScrollPos function to adjust the scroll box to reflect the scrolling position.
+$# Scrolling Text with the WM_HSCROLL and WM_VSCROLL Messages
The scroll bar sends WM_HSCROLL_win32_WM_HSCROLL and WM_VSCROLL_win32_WM_VSCROLL messages to the window procedure whenever the user clicks the scroll bar or drags the scroll box. The low-order words of WM_VSCROLL and WM_HSCROLL each contain a notification message that indicates the direction and magnitude of the scrolling action.
When the WM_HSCROLL and WM_VSCROLL messages are processed, the scroll bar notification message is examined and the scrolling increment is calculated. After the increment is applied to the current scrolling position, the window is scrolled to the new position by using the ScrollWindowEx_win32_ScrollWindowEx function, and the position of the scroll box is adjusted by using the SetScrollInfo_win32_SetScrollInfo function. For operating system versions earlier than 4.0, use the SetScrollPos_win32_SetScrollPos function to adjust the scroll box position.
After a window is scrolled, part of its client area is made invalid. To ensure that the invalid region is updated, the UpdateWindow_win32_UpdateWindow function is used to generate a WM_PAINT_win32_WM_PAINT message.
+$#A Scrolling Text with the WM_PAINT Message
When processing the WM_PAINT_win32_WM_PAINT message, it is convenient to draw the lines of text that you want to appear in the invalid portion of the window. The example in the following section uses the current scrolling position and the dimensions of the invalid region to determine the range of lines within the invalid region to display them.
+$#A Example of Scrolling Text
The following example shows how to have your application scroll text in response to input from the horizontal and vertical scroll bars.
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO si;

/* These variables are required to display text. */

static int xClient; /* width of client area */
static int yClient; /* height of client area */
static int xClientMax; /* maximum width of client area */

static int xChar; /* horizontal scrolling unit */
static int yChar; /* vertical scrolling unit */
static int xUpper; /* average width of uppercase letters */

static int xPos; /* current horizontal scrolling position */
static int yPos; /* current vertical scrolling position */

static int xMax; /* maximum horiz. scrolling position */
static int yMax; /* maximum vert. scrolling position */

int xInc; /* horizontal scrolling increment */
int yInc; /* vertical scrolling increment */

int i; /* loop counter */
int x, y; /* horiz. and vert. printing coords */

int FirstLine; /* first line in the invalidated area */
int LastLine; /* last line in the invalidated area */

/* Create an array of lines to display. */

#define LINES 27
static char *abc[] = { "anteater", "bear", "cougar", "dingo",
"elephant", "frog", "gazelle", "hyena", "iguana", "jackal",
"kangaroo", "llama", "moose", "newt", "octopus", "penguin",
"quail", "rat", "squid", "tortoise", "urus", "vole",
"walrus", "xylophone", "yak", "zebra",
"This line contains many words, but no character. Go figure." };

switch (uMsg) {

case WM_CREATE :

/* Get the handle of the client area's device context. */

hdc = GetDC (hwnd);

/* Extract font dimensions from the text metrics. */

GetTextMetrics (hdc, &tm);
xChar = tm.tmAveCharWidth;
xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2;
yChar = tm.tmHeight + tm.tmExternalLeading;

/* Free the device context. */

ReleaseDC (hwnd, hdc);

/*
* Set an arbitrary maximum width for client area.
* (xClientMax is the sum of the widths of 48 average
* lowercase letters and 12 uppercase letters.)
*/

xClientMax = 48 * xChar + 12 * xUpper;

return 0;

case WM_SIZE:

/* Retrieve the dimensions of the client area. */

yClient = HIWORD (lParam);
xClient = LOWORD (lParam);

/*
* Determine the maximum vertical scrolling position.
* The two is added for extra space below the lines
* of text.
*/

yMax = max (0, LINES + 2 - yClient/yChar);

/*
* Make sure the current vertical scrolling position
* does not exceed the maximum.
*/

yPos = min (yPos, yMax);

/*
* Adjust the vertical scrolling range and scroll box
* position to reflect the new yMax and yPos values.
*/

si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = yMax;
si.nPage = yClient / yChar;
si.nPos = yPos;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

/*
* Determine the maximum horizontal scrolling position.
* The two is added for extra space to the right of the
* lines of text.
*/

xMax = max (0, 2 + (xClientMax - xClient)/xChar);

/*
* Make sure the current horizontal scrolling position
* does not exceed the maximum.
*/

xPos = min (xPos, xMax);

/*
* Adjust the horizontal scrolling range and scroll box
* position to reflect the new xMax and xPos values.
*/

si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = xMax;
si.nPage = xClient / xChar;
si.nPos = xPos;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

return 0;

case WM_PAINT:

/* Prepare the window for painting. */

hdc = BeginPaint(hwnd, &ps);

/*
* Use the current vertical scrolling position and
* coordinates of the invalid rectangle to determine
* the range of new lines that should be drawn in the
* client area.
*/

FirstLine = max (0, yPos + ps.rcPaint.top/yChar - 1);
LastLine = min (LINES, yPos + ps.rcPaint.bottom/yChar);

/* Display these lines. */

for (i = FirstLine;i < LastLine;i++) {
x = xChar * (1 - xPos);
y = yChar * (1 - yPos + i);

TextOut (hdc, x, y, abc[i], lstrlen(abc[i]));
}

/* Indicate that painting is finished. */

EndPaint(hwnd, &ps);
break;

case WM_HSCROLL:
switch(LOWORD (wParam)) {

/* User clicked shaft left of the scroll box. */

case SB_PAGEUP:
xInc = -8;
break;

/* User clicked shaft right of the scroll box. */

case SB_PAGEDOWN:
xInc = 8;
break;

/* User clicked the left arrow. */

case SB_LINEUP:
xInc = -1;
break;

/* User clicked the right arrow. */

case SB_LINEDOWN:
xInc = 1;
break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:
xInc = HIWORD(wParam) - xPos;
break;

default:
xInc = 0;

}

/*
* If applying the horizontal scrolling increment does not
* take the scrolling position out of the scrolling range,
* increment the scrolling position, adjust the position
* of the scroll box, and update the window.
*/

if (xInc = max (-xPos, min (xInc, xMax - xPos))) {
xPos += xInc;
ScrollWindowEx (hwnd, -xChar * xInc, 0,
(CONST RECT *) NULL, (CONST RECT *) NULL,
(HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = xPos;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
UpdateWindow (hwnd);
}

return 0;

case WM_VSCROLL:
switch(LOWORD (wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:
yInc = min(-1, -yClient / yChar);
break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:
yInc = max(1, yClient / yChar);
break;

/* User clicked the top arrow. */

case SB_LINEUP:
yInc = -1;
break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:
yInc = 1;
break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:
yInc = HIWORD(wParam) - yPos;
break;

default:
yInc = 0;

}

/*
* If applying the vertical scrolling increment does not
* take the scrolling position out of the scrolling range,
* increment the scrolling position, adjust the position
* of the scroll box, and update the window. UpdateWindow
* sends the WM_PAINT message.
*/

if (yInc = max(-yPos, min(yInc, yMax - yPos))) {
yPos += yInc;
ScrollWindow(hwnd, 0, -yChar * yInc,
(CONST RECT *) NULL, (CONST RECT *) NULL,
(HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = YPos;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
UpdateWindow (hwnd);
}

return 0;

+$#A Scrolling a Bitmap
This section describes changes you can make to an application's main window procedure to enable the user to scroll a bitmap. The example in Example of Scrolling a Bitmap_win32_Example_of_Scrolling_a_Bitmap includes a menu item that copies the screen content to a bitmap and displays the bitmap in the client area.
The example also processes the WM_HSCROLL_win32_WM_HSCROLL and WM_VSCROLL_win32_WM_VSCROLL messages generated by the scroll bars so that the user may scroll the bitmap horizontally and vertically. Unlike the example for scrolled text, the bitmap example employs the BitBlt_win32_BitBlt function to draw the invalid portion of the client area.
+$#A Scrolling a Bitmap with the WM_CREATE Message
When the WM_CREATE_win32_WM_CREATE message is processed, the variables required for scrolling are initialized. Use the CreateCompatibleDC_win32_CreateCompatibleDC function to create a compatible device context (DC), the CreateBitmap_win32_CreateBitmap function to create a bitmap, and the SelectObject_win32_SelectObject function to select the bitmap for the DC. Note that a compatible DC is also known as a memory DC.
The device-specific information about the display device is retrieved. If a compatible DC is created for the screen, as in the example, use the GetDeviceCaps_win32_GetDeviceCaps function to get this information. The information includes the number of adjacent color bits per pixel, the number of color planes, and the height and width of the DC.
+$#A Scrolling a Bitmap with the WM_SIZE Message
Processing of the WM_SIZE_win32_WM_SIZE message requires adjusting the scrolling range and position, so it reflects the dimensions of the client area and the bitmap that will be displayed.
The SetScrollInfo_win32_SetScrollInfo function sets the minimum and maximum position values, the page size, and the scrolling position for a scroll bar. For operating system versions earlier than 4.0, use the SetScrollPos_win32_SetScrollPos function to adjust the scroll box position.
+$# Scrolling a Bitmap with the WM_HSCROLL and WM_VSCROLL Messages
When the WM_HSCROLL_win32_WM_HSCROLL and WM_VSCROLL_win32_WM_VSCROLL messages are processed, the scroll bar notification message is examined and the scrolling position is set to a new value that reflects the scrolling action of the user. If the scrolling position is within the scrolling range, the window is scrolled to the new position by using the ScrollWindow_win32_ScrollWindow function. The position of the scroll box is then adjusted by using the SetScrollInfo_win32_SetScrollInfo function. In versions earlier than 4.0, use the SetScrollPos_win32_SetScrollPos function.
After a window is scrolled, part of its client area is made invalid. To ensure that the invalid region is updated, use the UpdateWindow_win32_UpdateWindow function to generate a WM_PAINT_win32_WM_PAINT message. When processing the WM_PAINT message, an application must repaint the invalid region at the bottom of the client area. When scrolling or resizing the client area, the example uses the BitBlt_win32_BitBlt function to copy the appropriate portion of the bitmap to the invalid portion of the client area.
+$#A Example of Scrolling a Bitmap
The following example enables the user to capture the screen content into a bitmap and scroll the bitmap in the client area.
HDC hdc;
PAINTSTRUCT ps;
SCROLLINFO si;

/* These variables are required by BitBlt. */

static HDC hdcWin; /* window DC */
static HDC hdcScreen; /* DC for entire screen */
static HDC hdcScreenCompat; /* memory DC for screen */
static HBITMAP hbmpCompat; /* bitmap handle for old DC */
static BITMAP bmp; /* bitmap data structure */
static BOOL fBlt; /* TRUE if BitBlt occurred */
static BOOL fScroll; /* TRUE if scrolling occurred */
static BOOL fSize; /* TRUE if fBlt & WM_SIZE */

/* These variables are required for horizontal scrolling. */

static int xMinScroll; /* minimum horizontal scroll value */
static int xCurrentScroll; /* current horizontal scroll value */
static int xMaxScroll; /* maximum horizontal scroll value */

/* These variables are required for vertical scrolling. */

static int yMinScroll; /* minimum vertical scroll value */
static int yCurrentScroll; /* current vertical scroll value */
static int yMaxScroll; /* maximum vertical scroll value */

switch (uMsg) {
case WM_CREATE:

/*
* Create a normal DC and a memory DC for the entire
* screen. The normal DC provides a snapshot of the
* screen contents. The memory DC keeps a copy of this
* snapshot in the associated bitmap.
*/

hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,
(LPCSTR) NULL, (CONST DEVMODE *) NULL);
hdcScreenCompat = CreateCompatibleDC(hdcScreen);

/*
* Retrieve the metrics for the bitmap associated with the
* regular device context.
*/

bmp.bmBitsPixel =
(BYTE) GetDeviceCaps(hdcScreen, BITSPIXEL);
bmp.bmPlanes = (BYTE) GetDeviceCaps(hdcScreen, PLANES);
bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);
bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);

/* The width must be byte-aligned. */

bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;

/* Create a bitmap for the compatible DC. */

hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);

/* Select the bitmap for the compatible DC. */

SelectObject(hdcScreenCompat, hbmpCompat);

/* Initialize the flags. */

fBlt = FALSE;
fScroll = FALSE;
fSize = FALSE;

/* Initialize the horizontal scrolling variables. */

xMinScroll = 0;
xCurrentScroll = 0;
xMaxScroll = 0;

/* Initialize the vertical scrolling variables. */

yMinScroll = 0;
yCurrentScroll = 0;
yMaxScroll = 0;

break;

case WM_SIZE: {

int xNewSize;
int yNewSize;

xNewSize = LOWORD(lParam);
yNewSize = HIWORD(lParam);

if (fBlt)
fSize = TRUE;

/*
* The horizontal scrolling range is defined by
* (bitmap_width) - (client_width). The current horizontal
* scroll value remains within the horizontal scrolling range.
*/

xMaxScroll = max(bmp.bmWidth-xNewSize, 0);
xCurrentScroll = min(xCurrentScroll, xMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = xMinScroll;
si.nMax = xMaxScroll;
si.nPage = xNewSize;
si.nPos = xCurrentScroll;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

/*
* The vertical scrolling range is defined by
* (bitmap_height) - (client_height). The current vertical
* scroll value remains within the vertical scrolling range.
*/

yMaxScroll = max(bmp.bmHeight - yNewSize, 0);
yCurrentScroll = min(yCurrentScroll, yMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = yMinScroll;
si.nMax = yMaxScroll;
si.nPage = yNewSize;
si.nPos = yCurrentScroll;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

}
break;

case WM_PAINT: {

PRECT prect;

hdc = BeginPaint(hwnd, &ps);

/*
* If the window has been resized and the user has
* captured the screen, use the following call to
* BitBlt to paint the window's client area.
*/

if (fSize) {
BitBlt(ps.hdc,
0, 0,
bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat,
xCurrentScroll, yCurrentScroll,
SRCCOPY);

fSize = FALSE;
}

/*
* If scrolling has occurred, use the following call to
* BitBlt to paint the invalid rectangle.
*
* The coordinates of this rectangle are specified in the
* RECT structure to which prect points.
*
* Note that it is necessary to increment the seventh
* argument (prect->left) by xCurrentScroll and the
* eighth argument (prect->top) by yCurrentScroll in
* order to map the correct pixels from the source bitmap.
*/

if (fScroll) {
prect = &ps.rcPaint;

BitBlt(ps.hdc,
prect->left, prect->top,
(prect->right - prect->left),
(prect->bottom - prect->top),
hdcScreenCompat,
prect->left + xCurrentScroll,
prect->top + yCurrentScroll,
SRCCOPY);

fScroll = FALSE;
}

EndPaint(hwnd, &ps);
}
break;

case WM_HSCROLL: {

int xDelta; /* xDelta = new_pos - current_pos */
int xNewPos; /* new position */
int yDelta = 0;

switch (LOWORD(wParam)) {

/* User clicked the shaft left of the scroll box. */

case SB_PAGEUP:
xNewPos = xCurrentScroll - 50;
break;

/* User clicked the shaft right of the scroll box. */

case SB_PAGEDOWN:
xNewPos = xCurrentScroll + 50;
break;

/* User clicked the left arrow. */

case SB_LINEUP:
xNewPos = xCurrentScroll - 5;
break;

/* User clicked the right arrow. */

case SB_LINEDOWN:
xNewPos = xCurrentScroll + 5;
break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:
xNewPos = HIWORD(wParam);
break;

default:
xNewPos = xCurrentScroll;
}

/* New position must be between 0 and the screen width. */

xNewPos = max(0, xNewPos);
xNewPos = min(xMaxScroll, xNewPos);

/* If the current position does not change, do not scroll.*/

if (xNewPos == xCurrentScroll)
break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

xDelta = xNewPos - xCurrentScroll;

/* Reset the current scroll position. */

xCurrentScroll = xNewPos;

/*
* Scroll the window. (The system repaints most of the
* client area when ScrollWindowEx is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels that were invalidated.)
*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
SW_INVALIDATE);
UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = xCurrentScroll;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

}
break;

case WM_VSCROLL: {

int xDelta = 0;
int yDelta; /* yDelta = new_pos - current_pos */
int yNewPos; /* new position */

switch (LOWORD(wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:
yNewPos = yCurrentScroll - 50;
break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:
yNewPos = yCurrentScroll + 50;
break;

/* User clicked the top arrow. */

case SB_LINEUP:
yNewPos = yCurrentScroll - 5;
break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:
yNewPos = yCurrentScroll + 5;
break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:
yNewPos = HIWORD(wParam);
break;

default:
yNewPos = yCurrentScroll;
}

/* New position must be between 0 and the screen height. */

yNewPos = max(0, yNewPos);
yNewPos = min(yMaxScroll, yNewPos);

/* If the current position does not change, do not scroll.*/

if (yNewPos == yCurrentScroll)
break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

yDelta = yNewPos - yCurrentScroll;

/* Reset the current scroll position. */

yCurrentScroll = yNewPos;

/*
* Scroll the window. (The system repaints most of the
* client area when ScrollWindowEx is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels that were invalidated.)
*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,
SW_INVALIDATE);
UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = yCurrentScroll;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

}
break;

case WM_COMMAND: /* uMsg: command from app. menu */
switch(wParam) {
case IDM_STC:

/*
* Copy the contents of the current screen
* into the compatible DC.
*/

BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);

/*
* Copy the compatible DC to the client area.
*/

hdcWin = GetDC(hwnd);

BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat, 0, 0, SRCCOPY);

ReleaseDC(hwnd, hdcWin);

fBlt = TRUE;
break;

default:
return (DefWindowProc(hwnd, uMsg,
wParam, lParam));
}
break;

+$#A Creating a Keyboard Interface for a Standard Scroll Bar
Although a scroll bar control provides a built-in keyboard interface, a standard scroll bar does not. To implement a keyboard interface for a standard scroll bar, a window procedure must process the WM_KEYDOWN_win32_WM_KEYDOWN message and examine the virtual-key code specified by the wParam parameter. If the virtual-key code corresponds to an arrow key, the window procedure sends itself a WM_HSCROLL_win32_WM_HSCROLL or WM_VSCROLL_win32_WM_VSCROLL message with the low-order word of the wParam parameter set to the appropriate scroll bar notification message. For example, when the user presses the UP arrow key, the window procedure receives a WM_KEYDOWN message with wParam equal to VK_UP. In response, the window procedure sends itself a WM_VSCROLL message with the low-order word of wParam set to the SB_LINEUP notification message.
The following example shows how to include a keyboard interface for a standard scroll bar.
WORD wScrollNotify = 0xFFFF;

.
.
.

case WM_KEYDOWN:
switch (wParam) {
case VK_UP:
wScrollNotify = SB_LINEUP;
break;

case VK_PRIOR:
wScrollNotify = SB_PAGEUP;
break;

case VK_NEXT:
wScrollNotify = SB_PAGEDOWN;
break;

case VK_DOWN:
wScrollNotify = SB_LINEDOWN;
break;

case VK_HOME:
wScrollNotify = SB_TOP;
break;

case VK_END:
wScrollNotify = SB_BOTTOM;
break;
}

if (wScrollNotify != -1)
SendMessage(hwnd, WM_VSCROLL,
MAKELONG(wScrollNotify, 0), 0L);

break;
.
.
.

+$#AK Scroll Bar Reference
The following functions and messages are associated with scroll bars.
+$#A Scroll Bar Functions
Following are the functions used with scroll bars.
EnableScrollBar_win32_EnableScrollBarGetScrollPos_win32_GetScrollPosGetScrollRange_win32_GetScrollRangeScrollDC_win32_ScrollDCScrollWindow_win32_ScrollWindowScrollWindowEx_win32_ScrollWindowExSetScrollPos_win32_SetScrollPosSetScrollRange_win32_SetScrollRangeShowScrollBar_win32_ShowScrollBar
+$#A Scroll Bar Messages
Following are the messages used with scroll bars.
SBM_ENABLE_ARROWS_win32_SBM_ENABLE_ARROWSSBM_GETPOS_win32_SBM_GETPOSSBM_GETRANGE_win32_SBM_GETRANGESBM_SETPOS_win32_SBM_SETPOSSBM_SETRANGE_win32_SBM_SETRANGESBM_SETRANGEREDRAW_win32_SBM_SETRANGEREDRAWWM_CTLCOLORSCROLLBAR_win32_WM_CTLCOLORSCROLLBARWM_HSCROLL_win32_WM_HSCROLLWM_VSCROLL_win32_WM_VSCROLL

+$#AK Static Controls
The Microsoft® Windows® operating system provides dialog boxes and controls to support communication between an application and the user. A static control is a control that enables an application to provide the user with certain types of text and graphics that typically require no response.
+$#A About Static Controls
Applications often use static controls to label other controls or to separate a group of controls. Although static controls are child windows, they cannot be selected. Therefore, they cannot receive the keyboard focus and cannot have a keyboard interface. A static control that has the SS_NOTIFY style receives mouse input, notifying the parent window when the user clicks or double clicks the control. Static controls belong to the STATIC window class.
Although static controls can be used in overlapped, pop-up, and child windows, they are designed for use in dialog boxes, where Windows standardizes their behavior. By using static controls outside dialog boxes, a developer increases the risk that the application might behave in a nonstandard fashion. Typically, a developer either uses static controls in dialog boxes or uses the SS_OWNERDRAW style to create customized static controls.
For more information about dialog boxes, see Dialog Boxes_win32_Dialog_Boxes. For more information about window classes, see Window Classes_win32_Window_Classes. For more information about window subclassing, see Window Procedures_win32_Window_Procedures.
+$#A Static Control Types and Styles
The Microsoft® Win32® application programming interface (API) provides four types of static controls: simple graphics, text, images, and owner-drawn. Each type has one or more styles.
+$#A Simple Graphics
A simple graphics static control displays a frame or a filled rectangle. A frame can be drawn in a number of styles, included black, gray, or white. In addition, a frame and can drawn with an etched style to give it a three-dimensional appearance. The frame styles include SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME, SS_ETCHEDHORZ, SS_ETCHEDVERT, and SS_ETCHEDFRAME.
A rectangle can be filled with color in one of three styles: black, gray, or white. These styles are defined by the constants SS_BLACKRECT, SS_GRAYRECT, and SS_WHITERECT.
+$#A Text
A text static control displays text in a rectangle in one of five styles: left-aligned without wordwrap, left aligned with wordwrap, centered, right-aligned, or "simple." These styles are defined by the constants SS_LEFTNOWORDWRAP, SS_LEFT, SS_CENTER, SS_RIGHT, and SS_SIMPLE, respectively. Windows rearranges the text in these controls in predefined ways, except for "simple" text, which is not rearranged.
An application can change the text in a text static control at any time by using the SetWindowText_win32_SetWindowText function or the WM_SETTEXT_win32_WM_SETTEXT message.
The system displays as much text as it can in the static control and clips whatever does not fit. To calculate an appropriate size for the control, retrieve the font metrics for the text. For more information about fonts and font metrics, see Fonts and Text_win32_Fonts_and_Text.
+$#A Images
An image static control can display bitmaps, icons (including animated icons), or enhanced metafiles. The type of graphic that a particular static control displays depends on the control's style: SS_BITMAP, SS_ICON, or SS_ENHMETAFILE. An application specifies the style when it creates the control and also specifies the handle of the bitmap, icon, or metafile for the control to display. After the control is created, an application can associate a different graphic with the control by sending it an STM_SETIMAGE_win32_STM_SETIMAGE message, specifying the handle of the new graphic object. An application can retrieve the handle of the graphic object currently associated with a static control by sending it an STM_GETIMAGE_win32_STM_GETIMAGE message. An application sends messages to a static control by using the SendDlgItemMessage_win32_SendDlgItemMessage function.
+$#A Owner-Drawn
By using the SS_OWNERDRAW style, an application can take responsibility for painting a static control. The parent window of an owner-drawn static control (its owner) receives a WM_DRAWITEM_win32_WM_DRAWITEM message whenever the static control needs to be painted. The message includes the address of a DRAWITEMSTRUCT_win32_DRAWITEMSTRUCT_str structure that contains information that the owner window uses when drawing the control.
+$#A Static Control Styles
Many developers create dialog boxes by using standalone tools and so may not need to specify static control styles. However, if a developer uses the CreateWindow_win32_CreateWindow or CreateWindowEx_win32_CreateWindowEx function to create a static control for an application, the developer must use the following constants to define the style.
Style Description
SS_BITMAP Specifies a bitmap is to be displayed in the static control. The given text is the name of a bitmap (not a filename) defined elsewhere in the resource file. The style ignores the nWidth and nHeight parameters; the control automatically sizes itself to accommodate the bitmap.
SS_BLACKFRAME Specifies a box with a frame drawn in the same color as the window frames. This color is black in the default Windows color scheme.
SS_BLACKRECT Specifies a rectangle filled with the current window frame color. This color is black in the default Windows color scheme.
SS_CENTER Specifies a simple rectangle and centers the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next centered line.
SS_CENTERIMAGE Specifies that, if the bitmap or icon is smaller than the client area of the static control, the rest of the client area is filled with the color of the pixel in the top left corner of the bitmap or icon. If the static control contains a single line of text, the text is centered vertically in the client area of the control.
SS_ENHMETAFILE Specifies an enhanced metafile is to be displayed in the static control. The given text is the name of a metafile. An enhanced metafile static control has a fixed size; the metafile is scaled to fit the static control's client area.
SS_ETCHEDFRAME Draws the frame of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge_win32_DrawEdge function.
SS_ETCHEDHORZ Draws the top and bottom edges of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge function.
SS_ETCHEDVERT Draws the left and right edges of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge_win32_DrawEdge function.
SS_GRAYFRAME Specifies a box with a frame drawn with the same color as the screen background (desktop). This color is gray in the default Windows color scheme.
SS_GRAYRECT Specifies a rectangle filled with the current screen background color. This color is gray in the default Windows color scheme.
SS_ICON Specifies an icon is to be displayed in the dialog box. The given text is the name of an icon (not a filename) defined elsewhere in the resource file. The icon can be an animated cursor. The style ignores the nWidth and nHeight parameters; the control automatically sizes itself to accommodate the icon.
SS_LEFT Specifies a simple rectangle and left-aligns the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next left-aligned line.
SS_LEFTNOWORDWRAP Specifies a simple rectangle and left-aligns the given text in the rectangle. Tabs are expanded, but words are not wrapped. Text that extends past the end of a line is clipped.
SS_NOPREFIX Prevents interpretation of any ampersand (&) characters in the control's text as accelerator prefix characters. These are displayed with the ampersand removed and the next character in the string underlined. This static control style may be included with any of the defined static controls. An application can combine SS_NOPREFIX with other styles by using the bitwise OR (|) operator. This can be useful when filenames or other strings that may contain an ampersand (&) must be displayed in a static control in a dialog box.
SS_NOTIFY Sends the parent window STN_CLICKED_win32_STN_CLICKED, STN_DBLCLK_win32_STN_DBLCLK, STN_DISABLE_win32_STN_DISABLE, and STN_ENABLE_win32_STN_ENABLE notification messages when the user clicks or double-clicks the control.
SS_OWNERDRAW Specifies that the owner of the static control is responsible for drawing the control. The owner window receives a WM_DRAWITEM_win32_WM_DRAWITEM message whenever the control needs to be drawn.
SS_REALSIZEIMAGE Prevents a static icon or bitmap control (that is, static controls that have the SS_ICON or SS_BITMAP style) from being resized as it is loaded or drawn. If the icon or bitmap is larger than the destination area, the image is clipped.
SS_RIGHT Specifies a simple rectangle and right-aligns the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next right-aligned line.
SS_RIGHTJUST Specifies that the lower right corner of a static control with the SS_BITMAP or SS_ICON style is to remain fixed when the control is resized. Only the top and left sides are adjusted to accommodate a new bitmap or icon.
SS_SIMPLE Specifies a simple rectangle and displays a single line of left-aligned text in the rectangle. The text line cannot be shortened or altered in any way. The control's parent window or dialog box must not process the WM_CTLCOLORSTATIC_win32_WM_CTLCOLORSTATIC message.
SS_SUNKEN Draws a half-sunken border around a static control.
SS_WHITEFRAME Specifies a box with a frame drawn with the same color as the window background. This color is white in the default Windows color scheme.
SS_WHITERECT Specifies a rectangle filled with the current window background color. This color is white in the default Windows color scheme.

+$#A Static Control Default Message Processing
The window procedure for the predefined static control window class performs default processing for all messages that the static control procedure does not process. When the static control returns FALSE for any message, the predefined window procedure checks the messages and carries out the default action described in the following table. In the table, a text static control is a static control with the style SS_LEFTNOWORDWRAP, SS_LEFT, SS_CENTER, SS_RIGHT, or SS_SIMPLE.
Message Default action
WM_CREATE_win32_WM_CREATE Loads the graphic object and sizes the window to the object's size, for graphic static controls. Takes no action for other static controls.
WM_DESTROY_win32_WM_DESTROY Frees and destroys any graphic object, for graphic static controls. Takes no action for other static controls.
WM_ENABLE_win32_WM_ENABLE Repaints visible static controls.
WM_ERASEBKGND_win32_WM_ERASEBKGND Returns TRUE, indicating the control erases the background.
WM_GETDLGCODE_win32_WM_GETDLGCODE Returns DLGC_STATIC.
WM_GETFONT_win32_WM_GETFONT Returns the handle of the font for text static controls.
WM_GETTEXTLENGTH_win32_WM_GETTEXTLENGTH Returns the length, in characters, of the text for a text static control.
WM_LBUTTONDBLCLK_win32_WM_LBUTTONDBLCLK Sends the parent window an STN_DBLCLK_win32_STN_DBLCLK notification message if the control style is SS_NOTIFY.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Sends the parent window an STN_CLICKED_win32_STN_CLICKED notification message if the control style is SS_NOTIFY.
WM_NCLBUTTONDBLCLK_win32_WM_NCLBUTTONDBLCLK Sends the parent window an STN_DBLCLK_win32_STN_DBLCLK notification message if the control style is SS_NOTIFY.
WM_NCLBUTTONDOWN_win32_WM_NCLBUTTONDOWN Sends the parent window an STN_CLICKED_win32_STN_CLICKED notification message if the control style is SS_NOTIFY.
WM_NCHITTEST_win32_WM_NCHITTEST Returns HTCLIENT if the control style is SS_NOTIFY; otherwise, returns HTTRANSPARENT.
WM_PAINT_win32_WM_PAINT Repaints the control.
WM_SETFONT_win32_WM_SETFONT Sets the font and repaints for text static controls.
WM_SETTEXT_win32_WM_SETTEXT Sets the text and repaints for text static controls.

The predefined window procedure passes all other messages to DefWindowProc_win32_DefWindowProc for default processing.
+$#A Using Static Controls
The following example uses a timer and the STM_SETIMAGE_win32_STM_SETIMAGE message to animate an icon in a dialog box. The icon handles and the icon identifier (IDI_ICON) are defined in a global header file.
LRESULT APIENTRY StaticProc(hDlg, message, wParam, lParam)
HWND hDlg; /* window handle of dialog box */
UINT message; /* type of message */
UINT wParam; /* message-specific information */
LONG lParam;
{
UINT idTimer = 1;
static UINT i;

static HICON aIcons[11];

switch (message) {
case WM_INITDIALOG: /* initialize dialog box */
i = 0;

/* Load icons("hinst" is defined globally). */

hiconOne = LoadIcon(hinst, "OneIco");
aIcons[i] = hiconOne;
hiconTwo = LoadIcon(hinst, "TwoIco");
aIcons[++i] = hiconTwo;

.
. /* Continue with the remaining icons. */
.

i = 0;

/*
* Use STM_SETIMAGE to associate an icon with the
* IDI_ICON identifier.
*/

SendDlgItemMessage(hDlg, /* dialog box window handle */
IDI_ICON, /* icon identifier */
STM_SETIMAGE, /* message to send */
(WPARAM) IMAGE_ICON, /* image type */
(LPARAM) aIcons[i++]); /* icon handle */

/* Set a timer for 50-millisecond intervals. */

SetTimer(hDlg, idTimer, 50, (TIMERPROC) NULL);
return TRUE;

case WM_TIMER:

/*
* Use STM_SETIMAGE to associate a new icon with
* the IDI_ICON identifier whenever a WM_TIMER
* message is received.
*/

SendDlgItemMessage(hDlg, /* dialog box window handle */
IDI_ICON, /* icon identifier */
STM_SETIMAGE, /* message to send */
(WPARAM) IMAGE_ICON, /* image type */
(LPARAM) aIcons[i++]); /* new icon handle */
if (i == 10)
i = 0;

break;

case WM_COMMAND:
if (wParam == IDOK
|| wParam == IDCANCEL) {
EndDialog(hDlg, TRUE);
return TRUE;
}
return 0;

case WM_DESTROY: /* clean up */
KillTimer(hDlg, idTimer);
DeleteObject(hiconOne);
DeleteObject(hiconTwo);
.
. /* Continue with the remaining icons. */
.

return 0;
}
return FALSE;
UNREFERENCED_PARAMETER(lParam);
}

+$#A Static Control Messages
Following are the messages used with static controls. (STM_GETICON and STM_SETICON are used only with icons.)
STM_GETICON_win32_STM_GETICONSTM_SETICON_win32_STM_SETICONWM_CTLCOLORSTATIC_win32_WM_CTLCOLORSTATIC

+$#AK Control Panel Applications
Control Panel applications are special-purpose dynamic-link libraries (DLLs) that let users configure the environment of Microsoft® Windows®.
This overview describes Control Panel applications and explains the functions and messages that these applications use and process to complete their work.
+$#A About Control Panel Applications
Even though Windows provides a number of standard Control Panel applications, you can create additional applications to let users examine and modify the settings and operational modes of specific hardware and software.
+$#A Application Responsibilities and Operation
The primary responsibility of any Control Panel application is to display a dialog box and to carry out any tasks specified by the user. Despite this responsibility, Control Panel applications do not provide menus or other direct means for users to access their dialog boxes. Instead, these applications operate under the control of another application and display their dialog boxes only when requested by the controlling application.
Control Panel applications are usually controlled by a Windows system utility specifically designed to give users access to these applications. However, any application can load and manage Control Panel applications, as long as the controlling application sends messages and processes return values in the way that the Control Panel applications expect.
Most Control Panel applications display and manage a single dialog box, giving the user control of the settings and operational modes of a single system component. However, any given Control Panel application can provide any number of dialog boxes to control any number of system components. (These individual dialog boxes are sometimes called applets.) To distinguish between dialog boxes, a Control Panel application typically supplies the controlling application with a unique icon for each dialog box. The controlling application displays these icons and the user can choose a dialog box by choosing the corresponding icon.
+$#A Application Entry-Point Function
Every Control Panel application must export the standard entry-point function, CPlApplet_win32_CPlApplet. This function receives requests, in the form of Control Panel (CPL) messages, and carries out the requested work, such as initializing the application, displaying and managing the dialog box(es), and closing the application.
When the controlling application first loads the Control Panel application, it retrieves the address of the CPlApplet function and subsequently uses the address to call the function and pass it messages. The controlling application may send the following messages:
Message Description
CPL_DBLCLK_win32_CPL_DBLCLK Sent to notify CPlApplet_win32_CPlApplet that the user has chosen the icon associated with a given dialog box. CPlApplet should display the corresponding dialog box and carry out any user-specified tasks.
CPL_EXIT_win32_CPL_EXIT Sent after the last CPL_STOP_win32_CPL_STOP message and immediately before the controlling application uses the FreeLibrary_win32_FreeLibrary function to free the DLL containing the Control Panel application. CPlApplet should free any remaining memory and prepare to close.
CPL_GETCOUNT_win32_CPL_GETCOUNT Sent after the CPL_INIT_win32_CPL_INIT message to prompt CPlApplet_win32_CPlApplet to return a number indicating how many dialog boxes it supports.
CPL_INIT_win32_CPL_INIT Sent immediately after the DLL containing the Control Panel application is loaded, to prompt CPlApplet to perform initialization procedures, including memory allocation.
CPL_INQUIRE_win32_CPL_INQUIRE Sent after the CPL_GETCOUNT_win32_CPL_GETCOUNT message, to prompt CPlApplet_win32_CPlApplet to provide information about a given dialog box. This message has been superseded by the CPL_NEWINQUIRE_win32_CPL_NEWINQUIRE message.
CPL_NEWINQUIRE_win32_CPL_NEWINQUIRE Sent after the CPL_GETCOUNT message, to prompt CPlApplet to provide information about a given dialog box . The second parameter (lParam2) is a pointer to a NEWCPLINFO_win32_NEWCPLINFO_str structure.
CPL_SELECT_win32_CPL_SELECT Sent to notify CPlApplet_win32_CPlApplet that the user has selected the icon. associated with a given dialog box.
CPL_STOP_win32_CPL_STOP Sent once for each dialog box before the controlling application closes. CPlApplet should free any memory associated with the given dialog box.

+$#A Message Processing
CPlApplet_win32_CPlApplet processes all messages sent to a Control Panel application by a controlling application. The function expects to be sent messages in a given order. The controlling application expects the messages to be processed in a given way.
CPlApplet receives the CPL_INIT_win32_CPL_INIT message when the controlling application first loads the Control Panel application. The function should carry out any initialization, such as allocating memory, and return nonzero. If CPlApplet cannot complete the initialization, it must return zero, directing the controlling application to terminate communication and release the DLL.
CPlApplet receives the CPL_GETCOUNT_win32_CPL_GETCOUNT message only if the CPL_INIT message succeeded. The function must then return the number of dialog boxes supported by the Control Panel application.
CPlApplet receives one CPL_NEWINQUIRE_win32_CPL_NEWINQUIRE message for each dialog box supported by the Control Panel application. Each message includes the address of a NEWCPLINFO_win32_NEWCPLINFO_str structure and a unique identifier that the controlling application uses to identify the given dialog box. The function must set the dwSize member in the NEWCPLINFO structure to the size of the structure, set the hIcon member to the handle of the icon that represents the dialog box, and set the szName and szInfo members to the addresses of the short name and description of the dialog box. If the function also sets the dwHelpContext and szHelpFile members, the controlling application will provide context-sensitive Help for the dialog box. The lData member can be used for application-defined data.

Note The CPL_NEWINQUIRE message and NEWCPLINFO_win32_NEWCPLINFO_str structure supersede the CPL_INQUIRE_win32_CPL_INQUIRE message and CPLINFO_win32_CPLINFO_str structure, respectively.

CPlApplet_win32_CPlApplet receives a CPL_SELECT_win32_CPL_SELECT message as a notification that the user has selected the icon representing the dialog box. The function may receive this message any number of times. The message includes the dialog box identifier previously supplied by the controlling application and the lData value previously supplied by CPlApplet. Typically, Control Panel applications do not respond to the CPL_SELECT message.
CPlApplet receives a CPL_DBLCLK_win32_CPL_DBLCLK message as a notification that the user has chosen the icon representing the dialog box. The function may receive this message any number of times. The message includes the dialog box identifier and the lData value. The function should display the corresponding dialog box and process subsequent user input.
Before the controlling application terminates, CPlApplet receives the CPL_STOP_win32_CPL_STOP message once for each dialog box supported by the Control Panel application. The message includes the identifier for the dialog box and the lData value. The function should free any memory that it allocated for the given dialog box.
After the last CPL_STOP message, CPlApplet receives a CPL_EXIT_win32_CPL_EXIT message. The function should free all remaining allocated memory and unregister any private window classes that it may have registered. Immediately after the function returns from this message, the controlling application releases the Control Panel application by calling the FreeLibrary_win32_FreeLibrary function.
+$#A Application Setup
Every Control Panel application is a dynamic-link library. To make sure the DLL containing your Control Panel application can be located and automatically loaded by the Windows system utility, the DLL file must have the .CPL filename extension and must be set up in one of the following ways:
· Copied to the directory that contains the Windows system utility.
· Copied to the Windows SYSTEM directory.
· Registered under the the MMCPL key of the HKEY_CURRENT_USER key in the registry.

You should register the DLL in the registry if the DLL exports functions other than CPlApplet_win32_CPlApplet and provides functionality beyond the scope of a Control Panel application. For more information about the registry, seeRegistry_win32_Registry.
+$#A Creating Control Panel Applications
Although a Control Panel application may support more than one dialog box, it processes all requests through the single CPlApplet_win32_CPlApplet function. In the following example, the Control Panel application supports three dialog boxes that let the user set preferences for a component stereo system attached to the computer. The example uses an application-defined StereoApplets array that contains three structures, each corresponding to one of the dialog boxes. Each structure contains all the information required by the CPL_NEWINQUIRE_win32_CPL_NEWINQUIRE message, as well as the dialog box template and dialog box procedure required by the CPL_DBLCLK_win32_CPL_DBLCLK message. The code demonstrates how to fill the structures in the StereoApplets array.
#define NUM_APPLETS 3

typedef struct tagApplets
{
int icon; // icon resource identifier
int namestring; // name-string resource identifier
int descstring; // description-string resource identifier
int dlgtemplate; // dialog box template resource identifier
DLGPROC dlgfn; // dialog box procedure
} APPLETS;

APPLETS StereoApplets[NUM_APPLETS] =
{
AMP_ICON, AMP_NAME, AMP_DESC, AMP_DLG, AmpDlgProc,
TUNER_ICON, TUNER_NAME, TUNER_DESC, TUNER_DLG, TunerDlgProc,
TAPE_ICON, TAPE_NAME, TAPE_DESC, TAPE_DLG, TapeDlgProc,
};

LONG CALLBACK CPlApplet(hwndCPL, uMsg, lParam1, lParam2)
HWND hwndCPL; // handle of Control Panel window
UINT uMsg; // message
LPARAM lParam1; // first message parameter
LPARAM lParam2; // second message parameter
{
int i;
LPNEWCPLINFO lpNewCPlInfo;

i = (int) lParam1;

switch (uMsg) {
case CPL_INIT: // first message, sent once
hinst = GetModuleHandle("ecp.cpl");
return TRUE;

case CPL_GETCOUNT: // second message, sent once
return NUM_APPLETS;
break;

case CPL_NEWINQUIRE: // third message, sent once per app
lpNewCPlInfo = (LPNEWCPLINFO) lParam2;

lpNewCPlInfo->dwSize = (DWORD) sizeof(NEWCPLINFO);
lpNewCPlInfo->dwFlags = 0;
lpNewCPlInfo->dwHelpContext = 0;
lpNewCPlInfo->lData = 0;
lpNewCPlInfo->hIcon = LoadIcon(hinst,
(LPCTSTR) MAKEINTRESOURCE(StereoApplets[i].icon));
lpNewCPlInfo->szHelpFile[0] = '\0';

LoadString(hinst, StereoApplets[i].namestring,
lpNewCPlInfo->szName, 32);

LoadString(hinst, StereoApplets[i].descstring,
lpNewCPlInfo->szInfo, 64);

break;

case CPL_SELECT: // applet icon selected
break;

case CPL_DBLCLK: // applet icon double-clicked
MessageBeep(MB_ICONEXCLAMATION);

DialogBox(hinst,
MAKEINTRESOURCE(StereoApplets[i].dlgtemplate),
hwndCPL, StereoApplets[i].dlgfn);
break;

case CPL_STOP: // sent once per app. before CPL_EXIT
break;

case CPL_EXIT: // sent once before FreeLibrary is called
break;

default:
break;
}
return 0;
}

+$#AK Control Panel Reference
The following functions and messages are associated with the Control Panel.
+$#A Control Panel Functions
Following is the function used with the Control Panel.
CPlApplet_win32_CPlApplet
+$#A Control Panel Messages
Following are the messages used with the Control Panel.
CPL_DBLCLK_win32_CPL_DBLCLKCPL_EXIT_win32_CPL_EXITCPL_GETCOUNT_win32_CPL_GETCOUNTCPL_INIT_win32_CPL_INITCPL_INQUIRE_win32_CPL_INQUIRECPL_NEWINQUIRE_win32_CPL_NEWINQUIRECPL_SELECT_win32_CPL_SELECTCPL_STOP_win32_CPL_STOPWM_CPL_LAUNCH_win32_WM_CPL_LAUNCHWM_CPL_LAUNCHED_win32_WM_CPL_LAUNCHED

+$#AK File Installation Library
The Microsoft® Win32® application programming interface (API) includes a file installation library that makes it easier for applications to install files properly and enables setup programs to analyze files currently installed.
+$#A About File Installation
The functions included in the file installation library determine where a file should be installed, identify conflicts with currently installed files, and perform the installation process. These functions enable installation programs to avoid the following problems:
· Installing older versions of components over newer versions
· Changing the language in a mixed-language system without notification
· Installing multiple copies of a library in different directories
· Copying files to network directories shared by multiple users

The file installation library also includes functions that enable applications to query a version resource for file information and present the information in a clear format. This information includes the file's purpose, author, version number, and so on.
Version information can be added to any Microsoft® Windows® file that can have Windows resources, such as a dynamic-link library (DLL), an executable file, or a font file. To add the information, a developer must create a version resource and add the resource to the file by using a resource compiler and specifying VS_FILE_INFO as the resource type.
+$#A Creating an Installation Program
An installation program typically has the following goals:
· To place files in the correct location.
· To notify the user if the installation program is replacing an existing file with a version that is significantly different ¾ for example, replacing a German-language file with an English-language file, or replacing a newer file with an older file.

When writing the installation program, the developer must have the following information for each file on the installation disk(s):
· The name and location of the file (referred to as the source file).
· The name of the equivalent file on the user's hard disk (referred to as the destination file). This name is usually the same as the filename on the installation disk.
· The sharing status of the file ¾ that is, whether the file is private to the application being installed or could be shared by multiple applications.

For each file on the installation disk(s), the installation program must, at a minimum, call the VerFindFile_win32_VerFindFile and VerInstallFile_win32_VerInstallFile functions. These functions are described briefly in the following paragraphs.
The installation program can use VerFindFile with the destination-file name to determine where the file should be copied on the disk. This function can also be used to specify whether the file is private to the application or can be shared. If a problem occurs in finding the file, VerFindFile returns an error value. For example, if Windows is using the destination file, VerFindFile returns VFF_FILEINUSE. The installation program must notify the user of the problem and respond to the user's decision to continue or to end the installation.
VerInstallFile copies the source file to a temporary file in the directory specified by VerFindFile. If necessary, VerInstallFile expands the file by using the functions in the data decompression library.
VerInstallFile compares the version information of the temporary file to that of the destination file. If the two differ, VerInstallFile returns one or more error values. For example, it returns VIF_SRCOLD if the temporary file is older than the destination file and VIF_DIFFLANG if the files have different language identifiers or code-page values. The installation program must notify the user of the problem and respond to the user's decision to continue or to end the installation.
Some VerInstallFile_win32_VerInstallFile errors are recoverable. That is, the installation program can call VerInstallFile again, specifying the VIFF_FORCEINSTALL option, to install the file regardless of the version conflict. If VerInstallFile returns VIF_TEMPFILE and the user chooses not to force the installation, the installation program should delete the temporary file.
VerInstallFile could encounter a nonrecoverable error when attempting to force installation, even though the error did not exist previously. For example, the file could be locked by another user before the installation program attempted to force installation. If an installation program attempts to force installation after a non-recoverable error, VerInstallFile fails. The installation program must contain routines to recover from this type of error.
The recommended solution is to display for all errors a common dialog box with the buttons Install, Skip, and Install All. (Another solution is a dialog box with the buttons Yes, Yes to All, Skip, and Cancel.) The Install All button should prevent the installation program from prompting the user about similar errors by including the VIFF_FORCEINSTALL option in all subsequent uses of VerInstallFile. For nonrecoverable errors, the Install and Install All buttons should be disabled.
To display a useful error message to the user, the installation program usually must retrieve information from the version resources of the conflicting files. The file installation library provides four functions the installation program can use for this purpose: GetFileVersionInfoSize_win32_GetFileVersionInfoSize, GetFileVersionInfo_win32_GetFileVersionInfo, VerQueryValue_win32_VerQueryValue, and VerLanguageName_win32_VerLanguageName. GetFileVersionInfoSize returns the size of the version information. GetFileVersionInfo uses information retrieved by GetFileVersionInfoSize to retrieve a structure that contains the version information. VerQueryValue retrieves a specific member from that structure.
For example, if VerInstallFile_win32_VerInstallFile returns the VIF_DIFFTYPE error, the installation program should use the GetFileVersionInfoSize, GetFileVersionInfo, and VerQueryValue functions on the temporary and destination files to obtain the general type of each file. If the languages of the files conflict, the installation program should also use VerLanguageName to translate the binary language identifier into a text representation of the language. (For example, 0x040C translates to the string "French.")
If VerInstallFile returns a file error, such as VIF_ACCESSVIOLATION, the installation program should use the GetLastError_win32_GetLastError function to retrieve the most recent error value. The program should translate this value into an informative message to display to the user. The program must not yield control between the calls to VerInstallFile and GetLastError.
+$#A File Installation Library Functions
Following are the functions used for file installation.
GetFileVersionInfo_win32_GetFileVersionInfoGetFileVersionInfoSize_win32_GetFileVersionInfoSizeVerFindFile_win32_VerFindFileVerInstallFile_win32_VerInstallFileVerLanguageName_win32_VerLanguageNameVerQueryValue_win32_VerQueryValue
Obsolete or Deleted Functions
GetFileResource_win32_GetFileResource GetFileResourceSize_win32_GetFileResourceSize

+$#AK File Manager Extensions
In Microsoft® Windows®, a File Manager extension is a dynamic-link library (DLL) that adds a menu and buttons to File Manager. A File Manager extension can also display (in the status bar) Help text for the buttons. This guide describes how to create and install extensions for File Manager.
+$#A About File Manager Extensions
File Manager maintains a list of extensions in an initialization file and loads the extensions when starting. An extension DLL contains an entry point that processes menu commands and notification messages sent by File Manager. Up to five extension DLLs can be installed at any one time.
+$#A Creating a File Manager Extension
A File Manager extension must reside in a DLL that includes a standard entry point, the FMExtensionProc_win32_FMExtensionProc callback function. The extension must include the WFEXT.H header file that defines File Manager messages and structures. File Manager communicates with the extension DLL by sending the following messages to the DLL's FMExtensionProc function.
Message Meaning
1-99 User has selected an item from the extension-supplied menu. The value is the identifier of the selected menu item.
FMEVENT_HELPSTRING User is selecting an extension menu or toolbar command item. File Manager wants the extension to supply a helpstring.
FMEVENT_HELPMENUITEM User has pressed F1 while selecting an extension menu or toolbar command item. File Manager wants the extension to call WinHelp_win32_WinHelp appropriately for the command item.
FMEVENT_INITMENU User has selected the extension's menu. The extension should initialize items in the menu.
FMEVENT_LOAD File Manager is loading the extension DLL and prompts the DLL for information about the menu that the DLL supplies.
FMEVENT_SELCHANGE Selection in the File Manager directory window or Search Results window has changed.
FMEVENT_TOOLBARLOAD File Manager is creating the toolbar and prompts the extension DLL for information about any buttons the DLL adds to the toolbar.
FMEVENT_UNLOAD File Manager is unloading the extension DLL.
FMEVENT_USER_REFRESH User has chosen the Refresh command from the Window menu. The extension should update items in the menu, if necessary.

+$#A Creating the Entry-Point Function
File Manager communicates with an extension DLL through the FMExtensionProc callback function. Make sure to export this function by listing it in the EXPORTS statement of your extension's module-definition (.DEF) file. The FMExtensionProc function handles the messages listed in the preceding section by performing the tasks listed in the following table.
Task Action
Initializing the extension (FMEVENT_LOAD) Provides File Manager with the name and handle of the menu and saves the menu-item delta value.
Initializing the menu (FMEVENT_INITMENU) Initializes all top-level menu items and the items in any submenus.
Initializing the toolbar (FMEVENT_TOOLBARLOAD) Provides File Manager with information about the buttons to add to the toolbar, including the command identifier, Help string identifier, styles, and bitmap identifier.
Processing menu selections Carries out commands that the user chooses from the extension's menu.
Processing file selections (FMEVENT_SELCHANGE) Queries File Manager for information about the file that the user has selected from the directory window or Search Results window.
Updating items in the menu (FMEVENT_USER_REFRESH) Modifies the menu as appropriate when the user chooses File Manager's Refresh command from the Window menu.
Providing menu item help strings (FMEVENT_HELPSTRING) Provides File Manager with a help string for a specified command in a specified menu. File Manager displays this string in the area at the bottom left of the window.
Passing menu item help requests on to WinHelp (FMEVENT_HELPMENUITEM) Provides WinHelp with the window passed by File Manager and menu item information,
Quitting the extension DLL (FMEVENT_UNLOAD) Frees any memory allocated and prepares to exit.

The FMExtensionProc function is defined as follows.
LONG WINAPI FMExtensionProc(hwnd, wMsg, lParam)
HWND hwnd;
WORD wMsg;
LONG lParam;

The hwnd parameter identifies the File Manager window. An extension should use this window handle to specify the parent window for any dialog box or message box it must display. It should also use this handle to send query messages to File Manager. The uMsg parameter contains one of the File Manager messages listed previously. The lParam parameter contains a message-specific value. The return value from the FMExtensionProc_win32_FMExtensionProc function depends on the value of the uMsg parameter.
The menu added to File Manager may be a hierarchical (cascaded) menu and may contain grayed, disabled, or checked menu items in addition to command items. Menu items should be text only; owner-drawn menus and bitmap menus are not supported. Changing the bitmap for check marks is not supported.
Whenever File Manager calls the FMExtensionProc function, it waits to refresh its directory windows (for changes in the file system) until after the function returns. This allows the extension to perform large numbers of file operations without excessive repainting on the part of File Manager. It is not necessary for the extension to send the FM_REFRESH_WINDOWS_win32_FM_REFRESH_WINDOWS message to notify File Manager to repaint its directory windows.
+$#A Loading the Extension
File Manager first sends the FMEVENT_LOAD message to the FMExtensionProc function. The lParam parameter accompanying the FMEVENT_LOAD message points to an FMS_LOAD_win32_FMS_LOAD_str structure that File Manager uses to obtain information about the extension-supplied menu, including the menu name and menu handle.
File Manager also uses the FMS_LOAD structure to pass the menu-item delta value to the extension. To avoid conflicts with its own menu-item identifiers, File Manager renumbers the menu-item identifiers in an extension-supplied menu by adding the delta value to each identifier. An extension DLL that must modify its menu after File Manager has loaded it must use the delta value. For example, to delete a menu item, the extension DLL finds the sum of the delta value and the menu-item identifier and passes it as the idItem parameter to the DeleteMenu function.
If an extension returns FALSE in response to this message, File Manager calls the FreeLibrary function and ends any communication with the extension DLL. This is the only place where an extension can notify File Manager of initialization problems and thus prevent the extension from being loaded.
+$#A Adding Custom Buttons
A File Manager extension can add custom buttons to the File Manager toolbar by returning button information in response to the FMEVENT_TOOLBARLOAD message. File Manager sends this message to the FMExtensionProc function of the extension only if the extension successfully installed a custom menu during the processing of the FMEVENT_LOAD message. Use custom buttons as accelerator keys for menu commands specified in the custom menu.
When FMExtensionProc receives the FMEVENT_TOOLBARLOAD message, the lParam parameter points to an FMS_TOOLBARLOAD_win32_FMS_TOOLBARLOAD_str structure. The File Manager extension must copy information about the custom buttons to the structure, including the number of buttons and a specifier for the bitmap containing the button images (either the identifier for a bitmap resource or a handle to a memory bitmap). The extension must also include the address of an array of EXT_BUTTON_win32_EXT_BUTTON_str structures, each of which must contain a command identifier for the corresponding button. If the button is an accelerator for a menu command, the button identifier and corresponding menu-command identifier must be equal.
The bitmap containing the button images must be a bitmap resource or a memory bitmap. The extension must copy either the resource identifier or the memory bitmap's handle to the FMS_TOOLBARLOAD structure; and, in the case of a bitmap resource, the resource must be in the extension's executable file.
The bitmap must contain one image for each specified button. The bitmap height is always 15 pixels; the width is always a multiple of 16 pixels because each button image must be 16 pixels wide. Button images are concatenated from left to right. For example, the image of the first button specified in the array of EXT_BUTTON structures must be the leftmost image in the bitmap.
An extension can provide a line of Help text for a button by specifying the identifier of a string resource in the idsHelp member of the EXT_BUTTON structure. When the user selects the button, File Manager retrieves the string resource and displays it on the toolbar.
+$#A Processing Menu Selections
The menu resource that you define for your extension's menu must use menu-item identifiers in the range 1 through 99. When the user selects an item, the extension receives a command notification that is the identifier of the selected item as defined in the resource-definition file (which has the .RC filename extension). The command notification is not the sum of the delta value and the identifier. An extension DLL's FMExtensionProc function carries out commands by processing command notifications.
+$#A Initializing the Extension Menu
Whenever the user selects the extension's main menu item from File Manager's menu bar, File Manager sends the FMEVENT_INITMENU message to the extension DLL. An extension can use this message to initialize its menu items. For example, an extension can add check marks, disable items, or gray items during this message.
When the user selects submenus within the extension's menu, File Manager does not send the FMEVENT_INITMENU message. An extension DLL must initialize all items at the same time, including those in submenus.
+$#A Updating the Extension Menu
When the user chooses the Refresh command from the Window menu, File Manager sends an FMEVENT_USER_REFRESH message to an extension DLL. The extension can use this opportunity to update its menu items.
+$#A Processing File Selections
When the user selects a filename in the directory window or in the Search Results window, File Manager sends the FMEVENT_SELCHANGE_win32_FMEVENT_SELCHANGE message to an extension DLL. The extension can use this opportunity to send a query message to File Manager to obtain more information about the user's selection.
Because the user can change the selection often, the extension should return promptly after processing the FMEVENT_SELCHANGE message to avoid slowing the user's selection process.
+$#A Quitting the Extension Dynamic-Link Library
When File Manager quits, it sends the FMEVENT_UNLOAD message to each extension DLL and then calls the FreeLibrary_win32_FreeLibrary function to free the DLLs. Each DLL should free any memory that it has allocated.
+$#A Installing Extensions
File Manager installs extensions that have settings in the [AddOns] section of the WINFILE.INI initialization file. Each setting contains an entry and a value. An entry consists of a string that represents the name of an extension. The value assigned to the entry consists of a string that specifies the path to the extension DLL. An application can use the WritePrivateProfileString_win32_WritePrivateProfileString function to add a setting to WINFILE.INI. The following example shows a setting in WINFILE.INI.
[AddOns]
MyExtension=C:\NT\SYSTEM\MYEXT.DLL

File Manager does not display an error message if it cannot find an extension DLL, so an extension DLL can be deleted in order to uninstall it. Even so, an application that installs an extension DLL should provide an uninstall option to remove the extension's setting from the WINFILE.INI file.
+$#A Extension Messages
An extension can send the following window messages to retrieve relevant information from File Manager. File Manager is guaranteed to respond correctly only to messages sent from the FMExtensionProc_win32_FMExtensionProc function.
Message Description
FM_GETDRIVEINFO File Manager returns drive information from the active window. An extension provides a pointer to an FMS_GETDRIVEINFO structure; File Manager fills the structure with drive information.
FM_GETFILESEL File Manager returns information about a selected file from the active File Manager window (either the directory window or the Search Results window). An extension provides a pointer to an FMS_GETFILESEL structure; File Manager fills the structure with file information.
FM_GETFILESELLFN Same as the FM_GETFILESEL message except that the selected file may have a long filename.
FM_GETFOCUS File Manager returns a value that identifies the type of window with input focus.
FM_GETSELCOUNT File Manager returns the count of selected files in the directory and Search Results windows.
FM_GETSELCOUNTLFN Same as the FM_GETSELCOUNT message except that the count includes files with long filenames.
FM_REFRESH_WINDOWS File Manager repaints either its active window or all of its windows. This message is similar to File Manager's Refresh command on the Window menu.
FM_RELOAD_EXTENSIONS File Manager reloads all extensions. First, File Manager unloads all extensions, sending an FMEVENT_UNLOAD message to each extension. Then, it reloads the extensions, sending an FMEVENT_LOAD message to each extension. The FM_RELOAD_EXTENSIONS message allows an extension to uninstall itself by removing its setting from the WINFILE.INI file; this action causes File Manager to reload the remaining extensions. Other applications and programs (for example, installation programs) can also post this message by calling the PostMessage function.

+$#A Processing Commands and Messages from File Manager
The following code shows the FMExtensionProc function for a sample extension DLL. It demonstrates how an extension processes the menu commands and notification messages sent by File Manager.
HINSTANCE hinst;
HMENU hmenu;
WORD wMenuDelta;
BOOL fMultiple = FALSE;
BOOL fLFN = FALSE;

LONG WINAPI FMExtensionProc(hwnd, wMsg, lParam)
HWND hwnd;
WORD wMsg;
LONG lParam;

{
CHAR szBuf[200];
INT count;

switch (wMsg) {
case FMEVENT_LOAD:

#define lpload ((LPFMS_LOAD) lParam)

/* Save the menu-item delta value. */

wMenuDelta = lpload->wMenuDelta;

/* Fill the FMS_LOAD structure. */

lpload->dwSize = sizeof(FMS_LOAD);
lstrcpy(lpload->szMenuName, "&Extension");

/* Return the handle of the menu. */

hinst = GetModuleHandle("ext.dll");
lpload->hMenu = GetSubMenu(LoadMenu(hinst,
MAKEINTRESOURCE(MYMENU)), 0);

return (LONG)TRUE;

case FMEVENT_UNLOAD:

/* Perform any cleanup procedures here. */

break;

case FMEVENT_INITMENU:

/* Copy the menu handle. */

hmenu = (HMENU) lParam;

/*
* Add check marks to menu items as appropriate.
* Add menu-item delta values to menu-item
* identifiers to specify the menu items to check.
*/

CheckMenuItem(hmenu,
wMenuDelta + IDM_MULTIPLE,
fMultiple ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hmenu,
wMenuDelta + IDM_LFN,
fLFN ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED);
break;

case FMEVENT_TOOLBARLOAD:
{
static EXT_BUTTON extbtn[] = {
{1, 0, 0},
};

/* Fill the FMS_TOOLBARLOAD structure. */

#define lptbld ((LPFMS_TOOLBARLOAD) lParam)

lptbld->dwSize = sizeof(FMS_TOOLBARLOAD);
lptbld->lpButtons = (LPEXT_BUTTON) &extbtn;
lptbld->cButtons = 1;
lptbld->cBitmaps = 1;
lptbld->idBitmap = ID_BUTTONBITMAP;

return (LONG)TRUE;

}

case FMEVENT_USER_REFRESH:
MessageBox(hwnd, "User refresh event",
"Hey!", MB_OK);
break;

case FMEVENT_SELCHANGE:
break;

/*
* The following messages are generated when the user
* chooses items from the extension menu.
*/

case FMEVENT_HELPSTRING:

#define lphs ((LPFMS_HELPSTRING)lParam)

if (lphs->idCommand == -1)
lstrcpy(lphs->szHelp, "Help for extension menu");
else
wsprintf(lphs->szHelp, "Help for item %d", lphs->idCommand);

break;

case FMEVENT_HELPMENUITEM:

wsprintf(szBuf, "Help for %d", lParam);
MessageBox(hwnd, szBuf, "WinHelp call", MB_OK);

/*
* Use: WinHelp(hwnd, "ExtHelp.hlp", HELP_CONTEXT, lParam);
*/

break;

case IDM_GETFOCUS:
wsprintf(szBuf, "Focus %d",
(INT) SendMessage(hwnd, FM_GETFOCUS, 0, 0));
MessageBox(hwnd, szBuf, "Focus", MB_OK);
break;

case IDM_GETCOUNT:
count = (INT) SendMessage(hwnd,
fLFN ? FM_GETSELCOUNTLFN : FM_GETSELCOUNT,
0, 0);

wsprintf(szBuf, "%d files selected", count);
MessageBox(hwnd, szBuf, "Selection Count", MB_OK);
break;

case IDM_GETFILE:
{
FMS_GETFILESEL file;

count = (INT) SendMessage(hwnd,
fLFN ? FM_GETSELCOUNTLFN : FM_GETSELCOUNT,
FMFOCUS_DIR,
0);

while (count >= 1) {
/*
* Selection indices are zero-based (0 is
* first).
*/

count--;
SendMessage(hwnd, FM_GETFILESEL, count,
(LONG) (LPFMS_GETFILESEL) &file);
wsprintf(szBuf, "file %s\nSize %ld",
(LPSTR) file.szName, file.dwSize);
MessageBox(hwnd, szBuf, "File Information",
MB_OK);

if (!fMultiple)
break;
}
break;
}

case IDM_GETDRIVE:
{
FMS_GETDRIVEINFO drive;

SendMessage(hwnd, FM_GETDRIVEINFO, 0,
(LONG) (LPFMS_GETDRIVEINFO)&drive);
wsprintf(szBuf,
"%s\nFree %ld\nTotal %ld\nVolume %s\nShare %s",
(LPSTR) drive.szPath, drive.dwFreeSpace,
drive.dwTotalSpace, (LPSTR) drive.szVolume,
(LPSTR) drive.szShare);
MessageBox(hwnd, szBuf, "Drive Info", MB_OK);
break;
}

case IDM_LFN:
MessageBox(hwnd, "IDM_LFN", "Hi", MB_OK);
fLFN = !fLFN;
break;

case IDM_MULTIPLE:
MessageBox(hwnd, "IDM_MULTIPLE", "Hi", MB_OK);
fMultiple = !fMultiple;
break;

case IDM_REFRESH:
case IDM_REFRESHALL:
SendMessage(hwnd, FM_REFRESH_WINDOWS,
wMsg == IDM_REFRESHALL, 0);
break;

case IDM_RELOAD:
PostMessage(hwnd, FM_RELOAD_EXTENSIONS, 0, 0);
break;
}
return 0L;
}

+$#A Adding the Undelete Command
File Manager supports a hook for adding an Undelete command to the File menu (below the Delete command). If an "undelete" dynamic-link library is specified in the WINFILE.INI file, File Manager adds the Undelete command to the File menu when starting. When the user chooses the Undelete command, File Manager calls the DLL.
The [settings] section of the WINFILE.INI file should include a reference to the undelete DLL, as follows.
[settings]
UNDELETE.DLL=C:\MYDIR\OTHER.DLL

An undelete DLL must include a standard entry point, the UndeleteFile_win32_UndeleteFile function. This function must be exported by specifying the name of the function in the EXPORTS statement of the DLL's module-definition (.DEF) file.
The UndeleteFile_win32_UndeleteFile function is defined as follows:
DWORD APIENTRY UndeleteFile(hwndParent, lpszDir)
HWND hwndParent;
LPSTR lpszDir;

The hwndParent parameter identifies the parent window for any dialog boxes the DLL creates. The lpszDir parameter specifies the initial directory to be used (for example, C:\TEMP).
+$#A File Manager Extension Functions
Following are the functions used with File Manager extensions.
FMExtensionProc_win32_FMExtensionProcUndeleteFile_win32_UndeleteFile

+$#AK Screen Saver Library
The Microsoft® Win32® application programming interface (API) supports special applications called screen savers that start when the mouse and keyboard have been idle for a period of time. Screen savers exist for two main reasons:
· To avoid phosphor burn caused by static images on a screen
· To conceal sensitive information left on a screen

+$#A About Screen Savers
The Desktop application in Windows Control Panel lets users select from a list of screen savers, specify how much time should elapse before the screen saver is started, configure screen savers, and preview screen savers. Screen savers are loaded automatically when Windows starts or when a user activates the screen saver feature by using Control Panel.
Once a screen saver is chosen, Windows monitors keystrokes and mouse movements, starting the screen saver after a period of inactivity. However, Windows does not start the screen saver if any of the following conditions exist:
· The active application is not a Windows-based application.
· A computer-based training (CBT) window is present.
· The active application receives the WM_SYSCOMMAND_win32_WM_SYSCOMMAND message with wParam set to the SC_SCREENSAVE value, but does not pass the message to the DefWindowProc_win32_DefWindowProc function.

Screen savers contain specific exported functions, resource definitions, and variable declarations. The static-link library SCRNSAVE.LIB contains the main function and other startup code required for a screen saver. When a screen saver starts, the startup code in SCRNSAVE.LIB creates a full-screen window. The window class for this window is declared as follows.
WNDCLASS cls;

cls.hCursor = NULL;
cls.hIcon = LoadIcon(hInst, MAKEINTATOM(ID_APP));
cls.lpszMenuName = NULL;
cls.lpszClassName = "WindowsScreenSaverClass";
cls.hbrBackground = GetStockObject(BLACK_BRUSH);
cls.hInstance = hInst;
cls.style = CS_VREDRAW | CS_HREDRAW |
CS_SAVEBITS | CS_DBLCLKS;
cls.lpfnWndProc = (WNDPROC) ScreenSaverProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;

To create a screen saver, most developers create a source-code module containing three required functions and link them with SCRNSAVE.LIB. A screen saver module is responsible only for configuring itself and for providing visual effects.
One of the three required functions in a screen saver module is ScreenSaverProc_win32_ScreenSaverProc. This function processes specific messages and passes any unprocessed messages back to SCRNSAVE.LIB. Following are some of the typical messages processed by ScreenSaverProc.
Message Meaning
WM_CREATE_win32_WM_CREATE Retrieve any initialization data from the file REGEDIT.INI. Set a window timer for the screen saver window. Perform any other required initialization.
WM_ERASEBKGND_win32_WM_ERASEBKGND Erase the screen saver window and prepare for subsequent drawing operations.
WM_TIMER_win32_WM_TIMER Perform drawing operations.
WM_DESTROY_win32_WM_DESTROY Destroy the timer(s) created when the application processed the WM_CREATE_win32_WM_CREATE message. Perform any additional required cleanup.

ScreenSaverProc_win32_ScreenSaverProc passes unprocessed messages to SCRNSAVE.LIB by calling the DefScreenSaverProc_win32_DefScreenSaverProc function. The following table describes how this function processes various messages.
Message Action
WM_SETCURSOR_win32_WM_SETCURSOR Sets the cursor to the null cursor, removing it from the screen.
WM_PAINT_win32_WM_PAINT Paints the screen background.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Terminates the screen saver.
WM_MBUTTONDOWN_win32_WM_MBUTTONDOWN Terminates the screen saver.
WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN Terminates the screen saver.
WM_KEYDOWN_win32_WM_KEYDOWN Terminates the screen saver.
WM_MOUSEMOVE_win32_WM_MOUSEMOVE Terminates the screen saver.
WM_ACTIVATE_win32_WM_ACTIVATE If wParam is FALSE, terminates the screen saver.

The second required function in a screen saver module is ScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialog. This function displays a dialog box that enables the user to configure the screen saver. (Thus, an application must provide a corresponding dialog box template.) Windows displays the configuration dialog box when the user chooses the Setup button in the Screen Saver box in the Desktop application in Control Panel. The data entered by the user with the configuration dialog box is stored in the REGEDIT.INI file.
The third required function in a screen saver module is RegisterDialogClasses_win32_RegisterDialogClasses. This function must be called by all screen saver applications. However, applications that do not require special windows or custom controls in the configuration dialog box can simply return TRUE. Applications requiring custom controls or special windows should use this function to register the corresponding window classes.
In addition to creating a module that supports the three functions just described, a screen saver should supply an icon. This icon is visible only when the screen saver is run as a standalone application. (To be run by Control Panel, a screen saver must have the .SCR filename extension; to be run as a standalone application, it must have the .EXE filename extension.) The icon must be identified in the screen saver's resource file by the constant ID_APP, which is defined in the header file SCRNSAVE.H.
One final requirement is a screen saver description string. The resource file for a screen saver must contain a descriptive string that Control Panel displays as the screen saver name. The description string must be the first string in the resource file's string table (identified with the ordinal value 1).
+$#A Using the Screen Saver Functions
· Creating a screen saver_win32_Creating_a_Screen_Saver
· Installing new screen savers_win32_Installing_New_Screen_Savers
· Adding help_win32_Adding_Help

+$#A Creating a Screen Saver
This section contains example code taken from a screen saver application. At intervals ranging from 1 through 10 seconds, this application repaints the screen with one of four colors: white, light gray, dark gray, and black. The application paints the screen each time it receives a WM_TIMER_win32_WM_TIMER message. The user can adjust the interval at which this message is sent by displaying the application's configuration dialog box and adjusting a single horizontal scroll bar.
+$#A Supporting the Configuration Dialog Box
Most screen savers provide a configuration dialog box to let the user specify customization data such as unique colors, drawing speeds, line thicknesses, fonts, and so on. To support the configuration dialog box, the application must provide a dialog box template and must also support the ScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialog function. Following is the dialog box template for the sample application.
DLG_SCRNSAVECONFIGURE DIALOG 6, 18, 160, 63LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRALSTYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENUCAPTION "Sample Screen-Saver Setup"FONT 8, "MS Sans Serif"BEGIN GROUPBOX "Redraw Speed", 101, 0, 6, 98, 40 SCROLLBAR ID_SPEED, 5, 31, 89, 10 LTEXT "Fast", 103, 6, 21, 20, 8 LTEXT "Slow", 104, 75, 21, 20, 8 PUSHBUTTON "OK", ID_OK, 117, 10, 40, 14 PUSHBUTTON "Cancel", ID_CANCEL, 117, 32, 40, 14END

You must define the constant used to identify the dialog box template by using the decimal value 2003, as in the following example.
#define DLG_SCRNSAVECONFIGURE 2003

The following example shows the ScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialog function found in the sample application.
#define MINVEL 1 /* minimum redraw-speed value */
#define MAXVEL 10 /* maximum redraw-speed value */
#define DEFVEL 5 /* default redraw-speed value */

LONG lSpeed = DEFVEL; /* redraw-speed variable */

extern HINSTANCE hMainInstance; /* screen saver instance handle */

CHAR szAppName[80]; /* .INI section name */
CHAR szTemp[20]; /* temporary array of characters */
CHAR szRedrawSpeed[] = "Redraw Speed"; /* .INI speed entry */

BOOL WINAPI ScreenSaverConfigureDialog(hDlg, message, wParam, lParam)
HWND hDlg;
UINT message;
DWORD wParam;
LONG lParam;
{
static HWND hSpeed; /* handle of speed scroll bar */
static HWND hOK; /* handle of OK push button */

switch(message)
{
case WM_INITDIALOG:

/* Retrieve the application name from the .RC file. */

LoadString(hMainInstance, idsAppName, szAppName, 40);

/* Retrieve the .INI (or registry) filename. */

LoadString(hMainInstance, idsIniFile, szIniFile,
MAXFILELEN);

/* Retrieve any redraw-speed data from the registry. */

lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,
DEFVEL, szIniFile);

/*
* If the initialization file does not contain an entry
* for this screen saver, use the default value.
*/

if(lSpeed > MAXVEL || lSpeed < MINVEL)
lSpeed = DEFVEL;

/* Initialize the redraw-speed scroll bar control. */

hSpeed = GetDlgItem(hDlg, ID_SPEED);
SetScrollRange(hSpeed, SB_CTL, MINVEL, MAXVEL, FALSE);
SetScrollPos(hSpeed, SB_CTL, lSpeed, TRUE);

/* Retrieve a handle of the OK push button control. */

hOK = GetDlgItem(hDlg, ID_OK);

return TRUE;

case WM_HSCROLL:

/*
* Process scroll bar input, adjusting the lSpeed
* value as appropriate.
*/

switch (LOWORD(wParam))
{
case SB_PAGEUP:
--lSpeed;
break;

case SB_LINEUP:
--lSpeed;
break;

case SB_PAGEDOWN:
++lSpeed;
break;

case SB_LINEDOWN:
++lSpeed;
break;

case SB_THUMBPOSITION:
lSpeed = HIWORD(wParam);
break;

case SB_BOTTOM:
lSpeed = MINVEL;
break;

case SB_TOP:
lSpeed = MAXVEL;
break;

case SB_THUMBTRACK:
case SB_ENDSCROLL:
return TRUE;
break;
}
if ((int) lSpeed <= MINVEL)
lSpeed = MINVEL;
if ((int) lSpeed >= MAXVEL)
lSpeed = MAXVEL;

SetScrollPos((HWND) lParam, SB_CTL, lSpeed, TRUE);
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_OK:

/*
* Write the current redraw-speed variable to
* the .INI file.
*/

wsprintf(szTemp, "%ld", lSpeed);
WritePrivateProfileString(szAppName, szRedrawSpeed,
szTemp, szIniFile);

case ID_CANCEL:
EndDialog(hDlg, LOWORD(wParam) == ID_OK);
return TRUE;
}
}
return FALSE;
}

In addition to providing the dialog box template and supporting the ScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialog function, an application must also support the RegisterDialogClasses_win32_RegisterDialogClasses function. This function registers any nonstandard window classes required by the screen saver. Because the sample application used only standard window classes in its dialog box procedure, this function simply returns TRUE, as in the following example.
BOOL WINAPI RegisterDialogClasses(hInst)
HANDLE hInst;
{
return TRUE;
}

+$#A Supporting the Screen Saver Window Procedure
Each screen saver must support a window procedure named ScreenSaverProc_win32_ScreenSaverProc. Like most window procedures, ScreenSaverProc processes a set of specific messages and passes any unprocessed messages to a default procedure. However, instead of passing them to the DefWindowProc_win32_DefWindowProc function, ScreenSaverProc passes unprocessed messages to the DefScreenSaverProc_win32_DefScreenSaverProc function. Another difference between ScreenSaverProc and a normal window procedure is that the handle passed to ScreenSaverProc identifies the entire desktop rather than a client window. The following example shows the ScreenSaverProc window procedure for the sample screen saver.
LONG WINAPI ScreenSaverProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
DWORD wParam;
LONG lParam;
{
static HDC hdc; /* device-context handle */
static RECT rc; /* RECT structure */
static UINT uTimer; /* timer identifier */

switch(message)
{
case WM_CREATE:

/* Retrieve the application name from the .RC file. */

LoadString(hMainInstance, idsAppName, szAppName, 40);

/* Retrieve the .INI (or registry) filename. */

LoadString(hMainInstance, idsIniFile, szIniFile,
MAXFILELEN);

/* Retrieve any redraw-speed data from the registry. */

lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,
DEFVEL, szIniFile);

/*
* Set a timer for the screen saver window using the
* redraw-rate stored in REGEDIT.INI.
*/

uTimer = SetTimer(hwnd, 1, lSpeed * 1000, NULL);
break;

case WM_ERASEBKGND:

/*
* The WM_ERASEBKGND message is issued before the
* WM_TIMER message, allowing the screen saver to
* paint the background as appropriate.
*/

hdc = GetDC(hwnd);
GetClientRect (hwnd, &rc);
FillRect (hdc, &rc, GetStockObject(BLACK_BRUSH));
ReleaseDC(hwnd,hdc);
break;

case WM_TIMER:

/*
* The WM_TIMER message is issued at (lSpeed * 1000)
* intervals, where lSpeed == .001 seconds. This
* code repaints the entire desktop with a white,
* light gray, dark gray, or black brush each
* time a WM_TIMER message is issued.
*/

hdc = GetDC(hwnd);
GetClientRect(hwnd, &rc);
if (i++ <= 4)
FillRect(hdc, &rc, GetStockObject(i));
else
(i = 0);
ReleaseDC(hwnd,hdc);
break;

case WM_DESTROY:

/*
* When the WM_DESTROY message is issued, the screen saver
* must destroy any of the timers that were set at WM_CREATE
* time.
*/

if (uTimer)
KillTimer(hwnd, uTimer);
break;
}

/*
* DefScreenSaverProc processes any messages
* ignored by ScreenSaverProc.
*/

return DefScreenSaverProc(hwnd, message, wParam, lParam);
}

+$#A Creating a Module-Definition File
The ScreenSaverProc_win32_ScreenSaverProc and ScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialog functions must be exported in the application's module-definition file; RegisterDialogClasses_win32_RegisterDialogClasses should not be exported, however. The following example shows the module-definition file for the sample application.
NAME SSTEST.SCR

DESCRIPTION 'SCRNSAVE : Test'

STUB 'WINSTUB.EXE'
EXETYPE WINDOWS

CODE MOVEABLE
DATA MOVEABLE MULTIPLE

HEAPSIZE 1024
STACKSIZE 4096

EXPORTS
ScreenSaverProc
ScreenSaverConfigureDialog

+$#A Installing New Screen Savers
When compiling the list of available screen savers, Control Panel searches the Windows startup directory for files with the .SCR extension. Because screen savers are standard Windows executable files with .EXE extensions, you must rename them so they have .SCR extensions and copy them to the correct directory.
+$#A Adding Help
The configuration dialog box for a screen saver typically includes a Help button. Screen saver applications can check for the Help button identifier and call the WinHelp_win32_WinHelp function in the same way Help is provided in other Windows-based applications.
+$#A Screen Saver Functions
Following are the functions used with screen savers.
DefScreenSaverProc_win32_DefScreenSaverProcRegisterDialogClasses_win32_RegisterDialogClassesScreenSaverConfigureDialog_win32_ScreenSaverConfigureDialogScreenSaverProc_win32_ScreenSaverProc

+$#AK Shell Dynamic Data Exchange Interface
This overview describes the dynamic data exchange (DDE) interface of Program Manager (PROGMAN.EXE). Program Manager is a shell ¾ that is, an application that enables users to group, start, and otherwise control other applications for the Microsoft® Windows® operating system.
+$#A About Program Manager Initialization
Program Manager starts automatically when the user starts Windows and runs as long as Windows is in use. When it starts, Program Manager displays one or more windows within its main window. Each window contains icons that correspond to logically related Windows-based applications. For example, the Accessories window contains icons for Windows Write, Paintbrush, Notepad, and other accessory applications.
Program Manager stores initialization and configuration information in the registry. This information is stored under the Settings, Groups, and Restrictions keys in the following section of the registry:
HKEY_CURRENT_USER\Software\Microsoft\WindowsNT\ CurrentVersion\Program Manager
Additional information is stored in the Program Groups key in the following section of the registry:
HKEY_LOCAL_MACHINE\SOFTWARE
+$#A Settings Key
The Settings key contains information that Program Manager uses to configure its environment. The following entries appear in the Settings key.
Entry Description
MinOnRun Specifies whether to minimize Program Manager when an application is started.
AutoArrange Specifies whether Program Manager should automatically arrange icons within groups.
SaveSettings Specifies whether to save the position of the Program Manager window when Program Manager terminates.
DISPLAY.DRV Specifies the filename of the display driver in use when Program Manager last terminated. When Program Manager starts, it compares this value to the filename of the display driver currently in use. If the names are different, Program Manager re-extracts the application icons.
Window Specifies the location and dimensions of the Program Manager window.
Order Specifies the order that the groups listed in the Groups key appear in the Program Manager window.
Startup Specifies the name of the startup group. Program Manager automatically starts the applications in the startup group whenever it starts.

+$#A Groups Key
The windows that appear in the Program Manager window correspond to group files. From the user's perspective, a group file is a collection of icons that represent logically related applications, but from the programmer's perspective, a group file is actually a collection of data. This data includes the color information for the icons (AND and XOR masks), an offset to the resource header of each icon, the ideal resolution for displaying each icon, the name of the executable (.EXE) file that contains the application, and so on.
There are two types of groups: common and personal. A common group is shared among multiple users through network connections; a personal group is not shared. Program Manager stores information about personal groups under the Groups key. It stores information about common groups under the HKEY_LOCAL_MACHINE\SOFTWARE key.
The Groups key of the registry identifies the names of the group files that Program Manager displays as unique windows or icons. The groups must be numbered, but they need not be listed in any particular order. Program Manager never changes the number of an existing group.
+$#A Restrictions Key
The Restrictions key controls whether certain features of Program Manager are enabled or disabled. The following entries can appear in the Restrictions key.
Entry Description
NoRun Specifies whether to disable the Run command on the File menu. If this entry is set to 1, the Run command is disabled. If this entry is set to 0, the Run command is enabled. The default is 0 if no value is specified.
NoClose Specifies whether to prevent the user from closing Program Manager through the File menu, the System menu, the ALT+F4 key combination, or the Task List. If this entry is set to 1, closing is prevented. If this entry is set to 0, closing is allowed. The default is 0 if no value is specified.
EditLevel Controls the extent to which the user can modify read-write groups. (Shared read-only groups cannot be modified.) This entry can be set to one of the following values.

Value Action
0 Allows any modifications to the group. This is the default value.
1 Prevents the user from creating, deleting, or renaming groups.
2 Prevents the user from creating, deleting, or renaming groups and from creating or deleting items in a group.
3 Prevents the user from creating, deleting, or renaming groups; from creating or deleting items in a group; and from changing command lines for items in a group.
4 Prevents the user from changing any property of an item in a group; from creating, deleting, or renaming groups; from creating or deleting items in a group; and from changing command lines for items in a group.

NoFileMenu Specifies whether to disable the File menu and all of its commands. If this entry is set to 1, the File menu is disabled. If this entry is set to 0, the menu is enabled. The default setting is 0 if no value is specified.
NoSaveSettings Specifies whether to disable the Save Settings on Exit command on the Options menu. If this entry is set to 1, the Save Settings on Exit command is disabled. If this entry is set to 0, the command is enabled. The default setting is 0 if no value is specified.

Setting the value of NoRun to 1 and the value of EditLevel to 3 prevents a user from using Program Manager to run any applications not already in a program group.
+$#A Command-String Interface
Program Manager has a DDE command-string interface that allows other applications to create, display, delete, and reload groups; add items to groups; replace items in groups; delete items from groups; and close Program Manager. The following commands perform these actions:
AddItem ExitProgman
CreateGroup Reload
DeleteGroup ReplaceItem
DeleteItem ShowGroup

For example, the setup program for an application can use these commands to instruct Program Manager to install the application's icon in a group.
Multiple commands can be concatenated; each command must be contained in square brackets, and parameters must be contained in parentheses and separated by commas. Quotation marks must delimit arguments that contain spaces, brackets, or parentheses. For example, the following set of commands adds WINAPP.EXE to the Windows Applications group:
[CreateGroup("Windows Applications")]
[ShowGroup("My Group",1)]
[AddItem(winapp.exe,Win App,winapp.exe,2)]

To use these commands, an application must first initiate a DDE conversation with Program Manager. The application and topic names for the conversation are both PROGMAN. Then the application sends the WM_DDE_EXECUTE message, specifying the appropriate command and its parameters.

Note The user can configure Windows to use a shell other than Program Manager as the default. Therefore, do not design an application that assumes Program Manager will be available for a DDE conversation.

The following sections describe Program Manager DDE command strings in detail. In the syntax blocks in the following sections, brackets enclose optional parameters.
+$#A CreateGroup
The syntax for the CreateGroup command has this form:
CreateGroup(GroupName[,CommonGroupFlag])
The CreateGroup command instructs Program Manager to create a new group or activate the window of an existing group.
Following are the parameters for this command:
GroupName
Identifies the group to be created or activated. This parameter is a string. If a group already exists with the name specified by GroupName, CreateGroup activates the group window.
CommonGroupFlag
Specifies whether the new group is a common group or a personal group. If this parameter is 1, a common group is created; if it is 0, a personal group is created. To create a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails. The default value of this parameter is 1 (common group) if no value is specified and if the user has administrative privileges. The default value is 0 (personal group) if no value is specified and the user does not have administrative privileges.

Note If the second parameter of the CreateGroup command specifies the path of the group file (as was required in Windows version 3.1), the parameter is ignored.

+$#A ShowGroup
The syntax for the ShowGroup command has this form:
ShowGroup(GroupName,ShowCommand[,CommonGroupFlag])
The ShowGroup command instructs Program Manager to minimize, maximize, or restore the window of an existing group.
Following are the parameters for this command:
GroupName
Identifies the group window to be minimized, maximized, or restored.
ShowCommand
Specifies the action Program Manager is to perform on the group window. This parameter is an integer. It must have one of the following values.
Value Action
1 Activates and displays the group window. If the window is minimized or maximized, Windows restores it to its original size and position.
2 Activates the group window and displays it as an icon.
3 Activates the group window and displays it as a maximized window.
4 Displays the group window in its most recent size and position. The window that is currently active remains active.
5 Activates the group window and displays it in its current size and position.
6 Minimizes the group window.
7 Displays the group window as an icon. The currently active window remains active.
8 Displays the group window in its current state. The currently active window remains active.

CommonGroupFlag
Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being minimized, maximized, or restored is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To minimize, maximize, or restore a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

+$#A DeleteGroup
The syntax for the DeleteGroup command has this form:
DeleteGroup(GroupName[,CommonGroupFlag])
The DeleteGroup command instructs Program Manager to delete an existing group.
Following are the parameters for this command:
GroupName
Identifies the group to be deleted.
CommonGroupFlag
Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being deleted is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To delete a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

Note If the second parameter of the DeleteGroup command specifies the path of the group file (as was required in Windows version 3.1), the parameter is ignored.

+$#A Reload
The syntax for the Reload command has this form:
Reload(GroupName[,CommonGroupFlag])
The ReloadGroup command instructs Program Manager to remove and reload an existing group. An application that modifies group files can use this command to cause Program Manager to update the groups when it has finished making modifications.
Following are the parameters for this command:
GroupName
Identifies the group to be removed and reloaded. If no value is specified for the GroupName parameter, Program Manager unloads all groups and reloads the groups specified in the Groups key of the registry. The Settings and Restrictions keys are not reread.
CommonGroupFlag
Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being reloaded is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To reload a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

+$#A AddItem
The syntax for the AddItem command has this form:
AddItem(CmdLine[, Name[,IconPath[,IconIndex[,xPos,yPos[,DefDir[, HotKey[,fMinimize[fSeparateMemSpace] ] ] ] ] ] ])
The AddItem command instructs Program Manager to add an icon to an existing group. There is a limit of 50 items per group.
Following are the parameters for this command:
CmdLine
Specifies the full command line required to run the application. This parameter is a string. At a minimum, this string is the name of the executable file for the application. It can also include the full path of the application and any parameters required by the application.
Name
Specifies the title that is displayed below the icon in the group window.
IconPath
Identifies the filename for the icon to be displayed in the group window. This parameter is a string. This file can be either a Windows-based executable file or an icon file. If no value is specified for the IconPath parameter, Program Manager uses the first icon in the file specified by the CmdLine parameter if that file is an executable file. If CmdLine specifies an associated file, Program Manager uses the first icon of the associated executable file. The association is taken from the registry. (For more information about the registry, see Registry_win32_Registry.) If CmdLine specifies neither an executable file nor an associated executable file, Program Manager uses a default icon.
IconIndex
Specifies the index of the icon in the file identified by the IconPath parameter. The IconIndex parameter is an integer. PROGMAN.EXE contains five built-in icons that can be used for programs not written for Windows.
xPos
Specifies the horizontal position of the icon in the group window. This parameter is an integer. Both the xPos and yPos parameters must be used to specify the position of the icon. If the position is not specified, Program Manager places the icon in the next available space.
yPos
Specifies the vertical position of the icon in the group window. This parameter is an integer. Both the xPos and yPos parameters must be used to specify the position of the icon. If the position is not specified, Program Manager places the icon in the next available space.
DefDir
Specifies the name of the default (or working) directory. This parameter is a string.
HotKey
Identifies a hot (or shortcut) key that is specified by the user.
fMinimize
Specifies whether an application window is to be minimized when first displayed.
fSeparateMemSpace
Specifies whether a 16-bit application runs in a separate memory space.

+$#A ReplaceItem
The syntax for the ReplaceItem command has this form:
ReplaceItem(ItemName)
The ReplaceItem command instructs Program Manager to delete an item and record the position of the deleted item. Program Manager adds a new item (specified by the next AddItem command) at this recorded position.
Following is the parameter for this command:
ItemName
Specifies the item to be deleted. Its position is recorded by Program Manager.

+$#A DeleteItem
The syntax for the DeleteItem command has this form:
DeleteItem(ItemName)
The DeleteItem command instructs Program Manager to delete an item from the currently active group.
Following is the parameter for this command:
ItemName
Specifies the item to be deleted from the currently active group.

+$#A ExitProgman
The syntax for the ExitProgman command has this form:
ExitProgman(bSaveGroups)
If Program Manager was started by another application, the ExitProgman command instructs Program Manager to terminate and, optionally, save its group information.
Following is the parameter for this command:
bSaveGroups
Specifies a Boolean value that, if it is nonzero, causes Program Manager to save its group information before closing. If bSaveGroups is zero, Program Manager does not save its group information.

+$#A Requesting Group Information
Program Manager can provide information about its groups to an application. Applications can request this information from Program Manager by using the PROGMAN topic.
An application can obtain a list of Program Manager groups by issuing a request for the Group item. Program Manager provides the list in CF_TEXT format. The list consists of group-name strings separated by carriage returns.
An application can use a group name as an item name to request information about the group. Program Manager provides this information in CF_TEXT format. The fields of group information are separated by commas. The first line of the information contains the group name (in quotation marks), the path of the group file, and the number of items in the group. Each subsequent line contains information about an item in the group, including the command line (in quotation marks), the default directory, the icon path, the position in the group, the icon index, the shortcut key (in numeric form), and the minimize flag.

+$#AK Shell Library
A shell is an application that enables users to group, start, and otherwise control other applications. This overview describes features of the shell for the Microsoft® Windows® operating system.
+$#A About the Shell Library
The following features are supported by the dynamic-link library SHELL.DLL:
· The drag-drop feature
· Associations (used) to find and start applications
· Extraction of icons from executable files

The header file for the functions that support these features is SHELLAPI.H. The library is SHELL32.LIB.
+$#A Using the Shell Features
· Using the drag-drop feature_win32_Using_the_Drag_Drop_Feature
· Using associations to find and start applications_win32_Using_Associations_to_Find_and_Start_Applications
· Extracting icons from executable files_win32_Extracting_Icons_from_Executable_Files

+$#A Using the Drag-Drop Feature
When an application implements the drag-drop feature, a user can select one or more files in File Manager, drag them to an open application, and drop them there. The application in which the files were dropped receives a message it can use to retrieve the filenames and the coordinates of the point at which the files were dropped.
An application that can accept dropped files from File Manager calls the DragAcceptFiles_win32_DragAcceptFiles function for one or more of its windows. When the user releases the mouse button to drop a file or files in the window specified in the call to DragAcceptFiles, File Manager sends the application a WM_DROPFILES_win32_WM_DROPFILES message. (File Manager does not send the WM_DROPFILES message to an application unless the application calls DragAcceptFiles.) WM_DROPFILES contains a handle of a structure the application can query to retrieve the name of the dropped file and the coordinates of the cursor when the file was dropped. The application can use the DragQueryFile_win32_DragQueryFile function to retrieve a count of the files that were dropped and their names. The DragQueryPoint_win32_DragQueryPoint function returns the window coordinates of the cursor when the user released the mouse button.
To free the memory allocated by the system for the WM_DROPFILES message, an application calls the DragFinish_win32_DragFinish function when it is finished.
For example, an application can call the DragAcceptFiles function when it starts and call a drag-drop function when it receives a WM_DROPFILES message, as shown in the following example.
case WM_CREATE:
DragAcceptFiles(hwnd, TRUE);
break;

case WM_DROPFILES:
DragFunc(hwnd, wParam); /* application-defined function */
break;

case WM_DESTROY:
DragAcceptFiles(hwnd, FALSE);
break;

The following example uses the DragQueryPoint_win32_DragQueryPoint function to determine where to begin to write text. The first call to the DragQueryFile_win32_DragQueryFile function determines the number of dropped files. The loop writes the name of each file, beginning at the point returned by DragQueryPoint.
POINT pt;
WORD cFiles, a;
char lpszFile[80];

DragQueryPoint((HANDLE) wParam, &pt);

cFiles = DragQueryFile((HANDLE) wParam, 0xFFFF, (LPSTR) NULL, 0);
for(a = 0; a < cFiles; pt.y += 20, a++) {
DragQueryFile((HANDLE) wParam, a, lpszFile, sizeof(lpszFile));
TextOut(hdc, pt.x, pt.y, lpszFile, lstrlen(lpszFile));
}

DragFinish((HANDLE) wParam);

+$#A Using Associations to Find and Start Applications
File Manager includes an Associate dialog box that makes it possible for users to associate a filename extension with a specific application. File Manager stores these associations in the registry (under HKEY_CURRENT_USER\Software\Description\Microsoft\Windows\CurrentVersion\Extensions). If a file has a filename extension that is associated with an application, that application starts automatically whenever a user double-clicks that file in File Manager.
Using the FindExecutable_win32_FindExecutable and ShellExecute_win32_ShellExecute functions, applications can take advantage of such associations to find and start applications or open and print files.
An application can use the FindExecutable function to retrieve the name and handle of the executable file that is associated with a specified filename. The ShellExecute function either opens or prints a specified file, depending on the value of its lpOperation parameter. To open a document file, the function relies on the association of the filename extension.
Windows 95: You can use ShellExecute to open or explore a Windows 95 folder. To open a folder, use either of the following calls:
ShellExecute(handle, NULL, "path_to_folder", NULL, NULL, SW_SHOWNORMAL);
or
ShellExecute(handle, "open", "path_to_folder", NULL, NULL, SW_SHOWNORMAL);

To explore a folder, use the following call:
ShellExecute(handle, "explore", "path_to_folder", NULL, NULL, SW_SHOWNORMAL);

If the lpOperation parameter is NULL, the function opens the file specified by its lpFile parameter. If lpOperation is "open" or "explore", the function will force a open window or explorer.

+$#A Extracting Icons from Executable Files
An application can use the ExtractIcon_win32_ExtractIcon function to retrieve the handle of an icon from a specified executable file, dynamic-link library, or icon file. The following example uses the DragQueryPoint_win32_DragQueryPoint function to retrieve the coordinates of the point where a file was dropped, the DragQueryFile_win32_DragQueryFile function to retrieve the filename of a dropped file, and the ExtractIcon function to retrieve the handle of the first icon in the file, if any.
POINT pt;
WORD cFiles;
HDC hdc;
char lpszFile[80];
HANDLE hCurrentInst, hicon;

DragQueryPoint((HANDLE) wParam, &pt);

cFiles = DragQueryFile((HANDLE) wParam, 0xFFFF, NULL, NULL);

if(cFiles > 1) {
TextOut(hdc, pt.x, pt.y,
"Please drop only one icon file.", 31);
return FALSE;
}
else {
DragQueryFile((HANDLE) wParam, 0, lpszFile, sizeof(lpszFile));
hCurrentInst = (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE);
hicon = ExtractIcon(hCurrentInst, lpszFile, 0);
if (hicon == NULL)
TextOut(hdc, pt.x, pt.y, "No icons found.", 15);
else if (hicon == (HICON) 1)
TextOut(hdc, pt.x, pt.y,
"File must be .EXE, .ICO, or .DLL.", 33);
else
DrawIcon(hdc, pt.x, pt.y, hicon);
}

DragFinish((HANDLE) wParam);

+$#AK Shell Library Reference
The following functions, interfaces, messages, and structures are associated with shell features.
+$#A Shell Library Functions
The following functions are used to implement shell features.
DragAcceptFiles_win32_DragAcceptFilesDragFinish_win32_DragFinishDragQueryFile_win32_DragQueryFileDragQueryPoint_win32_DragQueryPointExtractAssociatedIcon_win32_ExtractAssociatedIconExtractIcon_win32_ExtractIconFindExecutable_win32_FindExecutableShellAbout_win32_ShellAboutShellExecute_win32_ShellExecute ShellExecuteEx_win32_ShellExecuteEx

+$#A Shell Library Interfaces
The following interfaces are used to implement shell features.
IContextMenu_win32_IContextMenuICopyHook_win32_ICopyHookIExtractIcon_win32_IExtractIconIShellPropSheetExt_win32_IShellPropSheetExtIShellExtInit_win32_IShellExtInitIShellLink_win32_IShellLinkIShellFolder_win32_IShellFolderIEnumIDList_win32_IEnumIDList

+$#A Shell Library Messages
The following message is used to implement shell features.
WM_DROPFILES_win32_WM_DROPFILES
+$#AK Shell Library Structures
The following structure is used to implement shell features.
SHELLEXECUTEINFO_win32_SHELLEXECUTEINFO_str

+$#AK About Windows 95 Shell Extensions
Applications can extend the shell of the Microsoft Windows 95 operating system in a number of ways. A shell extension enhances the shell by providing additional ways to manipulate file objects, by simplifying the task of browsing through the file system and networks, or by giving the user easier access to tools that manipulate objects in the file system. For example, a shell extension can assign an icon to each file or add commands to the context menu and file menu for a file.
Windows 95 supports the following types of shell extensions:
· Context menu handlers: These add items to the context menu for a particular file object. (The context menu is displayed when the user clicks a file object with the alternate mouse button.)
· Drag-and-drop handlers: These are context menu handlers that the system calls when the user drops an object after dragging it to a new position.
· Icon handlers: These typically add instance-specific icons for file objects. They can also be used to add icons for all files belonging to the same class.
· Property-sheet handlers: These add pages to the property sheet dialog box that the shell displays for a file object. The pages are specific to a class of files or a particular file object.
· Copy-hook handlers: These are called when a folder object is about to be copied, moved, deleted, or renamed. The handler can either allow or prevent the operation.

The design of a shell extension is based on the Component Object Model in Object Linking and Embedding (OLE). The shell accesses an object through interfaces. An application implements the interfaces in a shell extension dynamic-link library (DLL), which is similar to an In-Process Server DLL in OLE.
For more information, see "Extending the Windows 95 Shell" in the Windows 95 Guide to Programming.
+$#AK Windows 95 Shell Extensions on Windows NT
Applications that employ Windows 95 shell-extension handlers (icon handlers, property-page handlers, context-menu handlers, copy hooks, drag-and-drop handlers, and other such DLL-based shell extension classes) can run on the next release of Windows NT with the new shell. To do so, you must take one additional step while registering during setup. This topic describes how to install such applications on Windows NT.
Because shell extensions handlers run in a system process, namely the shell process, Windows NT administrators require control over which shell handlers are allowed to run. When the new shell is available on Windows NT, administrators will have control over which shell handlers can run in much the same way they can now control which device drivers can run.
For the Windows NT shell to recognize and run a shell-extension handler, the handler's CLSID must be listed in the Registry Editor under a new registry key (as well as being registered in the typical way as specified for Windows 95). This registry key contains a list of the handlers that are approved for the shell to run. By default, this key's access control permissions allow only someone with administrator privileges to modify the list.
The CLSID for the extension must be registered at the following location:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved
To register the extension, a "named value" should be added to the "Approved" key. The name of the value must be the string form of the CLSID as obtained from the StringFromCLSID() function, which is used elsewhere in the registry. The value itself should be the ProgID. The ProgID is stored here simply to make inspection of the registry easier, as it is easier to decipher than a CLSID. The Windows NT shell does not look at this value, however, only at the presence or absence of the CLSID.
Your setup application may or may not be able to write to this key, depending on the privileges of the person installing the application. The setup application should attempt to open the key described above, requesting the KEY_SET_VALUE permission. If it succeeds, the new CLSID can be added to fully register the corresponding shell extension. If the request fails with a security violation, then the person installing the application does not have permission to register new shell extensions. In this case, the setup application might warn the user that some application features will not be available unless an administrator turns them on (by installing the application, or by writing the registry keys directly). Or, if the shell extension is crucial to the application's functioning, the setup application might cause the installation to fail completely, notifying the user that the program must be installed by an administrator.
When setting up on Windows 95, it is not necessary to write the CLSID to this key, although writing it is harmless. Note, however, that the key may not exist in a Windows 95 installation, so that if your setup application does attempt to open the key, it may fail.
The following sample code details this process. Full error handling has been omitted.
#include <windows.h>
#include <string.h>

void main(void)
{
//
// First, attempt to open the registry key where approved extensions are
// listed. Note the extra slashes within the second parameter (the
// registry path string).
//

long err;
HKEY hkApproved;

err = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,

"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
0,
KEY_SET_VALUE,
&hkApproved);

if (err == ERROR_ACCESS_DENIED)
{
//
// The user does not have permissions to add a new value to this key. In this
// case, a reasonable action would be to warn the user that some
// application features will not be available unless an administrator
// installs the application. If the shell extension is central to the
// functioning of the application, tell the user that the install
// can only be performed by an administrator, and stop the install.
//
}

else if (err == ERROR_FILE_NOT_FOUND)
{
//
// The key does not exist. This should only happen if setup is running
// on Windows 95 instead of Windows NT, or if you are installing on an older
// version of either operating system that does not have the Windows 95-style UI.
//
}

else if (err != ERROR_SUCCESS)
{
//
// some other problem...
//
}

else
{
//
// The open of the key succeeded. Now register the new shell extension
// under this key. This requires having the ProgID and string form of the
// CLSID handy.
//

//
// Assume that lpstrProgID contains our ProgID string.
//

LPSTR lpstrProgID = "My Bogus Class";

//
// Assume that clsidExtension contains the CLSID struct. The code below
// creates a string form this CLSID. If a string version of
// the CLSID is already handy, skip this code.
//

CLSID clsidExtension = {0x11111111, 0x1111, 0x1111, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11};

HRESULT hr;
LPOLESTR lpolestrCLSID;
CHAR rgchCLSID[40];

CoInitialize(NULL);

hr = StringFromCLSID(clsidExtension, &lpolestrCLSID);

//
// StringFromCLSID returns a Unicode string, so convert to ANSI for
// calling the registry. Note that on Windows NT you can call the Unicode
// version of the registry API instead.
//

WideCharToMultiByte(CP_ACP, 0, lpolestrCLSID, -1, rgchCLSID, 40,
NULL, NULL);

CoTaskMemFree(lpolestrCLSID);

CoUninitialize();

//
// Now add the new value to the registry.
// Note that each new shell extension CLSID must be registered here.
//

err = RegSetValueEx(
hkApproved,
rgchCLSID,
0,
REG_SZ,
(const BYTE *)lpstrProgID,
strlen(lpstrProgID));

//
// Finally, close the key.
//

err = RegCloseKey(hkApproved);

}
}

+$#AK Bitmaps
A bitmap is a powerful graphics object used to create, manipulate (scale, scroll, rotate, and paint), and store images as files on a disk.
+$#A About Bitmaps
A bitmap is one of seven objects that can be selected into a device context. The other six objects are the pen, brush, font, region, logical palette, and path. (For more information about device contexts and related objects, see Device Contexts_win32_Device_Contexts.)
In the Microsoft® Win32® application programming interface (API), Control Panel applications are examples of applications that use bitmaps. When a user selects wallpaper for the desktop, the user actually selects a bitmap, which Windows uses to paint the desktop background.
Windows creates this pattern by repeatedly drawing a 32- by 32-pixel pattern on the desktop. This pattern is stored as a bitmap in the file REDBRICK.BMP.
From the user's point of view, a bitmap is a rectangle of pixels that form a visual image. However, from the developer's perspective, a bitmap is a collection of structures that specify or contain the following elements:
· A header that describes the resolution of the device on which the rectangle of pixels was created, the dimensions of the rectangle, the size of the array of bits, and so on.
· A logical palette.
· An array of bits that defines the relationship between pixels in the bitmapped image and entries in the logical palette.
The following illustration shows the developer's perspective of the bitmap found in the file REDBRICK.BMP. It shows a palette array, a 32- by 32-pixel rectangle, and the index array that maps colors from the palette to pixels in the rectangle.
{ewc msdncd, EWGraphic, group10651 0 /a "SDK_01.bmp"}
In the preceding example, the rectangle of pixels was created on a VGA display device using a palette of 16 colors. A 16-color palette requires 4-bit indices; therefore, the array that maps palette colors to pixel colors is composed of 4-bit indices as well. (For more information about logical color-palettes, see Colors_win32_Colors.)
Note that in the above bitmap, Windows maps indices to pixels beginning with the bottom scan line of the rectangular region and ending with the top scan line. (A scan line is a single row of adjacent pixels on a video display.) For example, the first row of the array (row 0) corresponds to the bottom row of pixels, scan line 31. This is because the above bitmap is a "bottom-up" device-independent bitmap (DIB), a common type of bitmap. In "top-down" DIBs and in device-dependent bitmaps (DDBs), Windows maps indices to pixels beginning with the top scan line.
+$#A Bitmap Types
There are two types of bitmaps: device-dependent (DDBs) and device-independent (DIBs). DDBs were common in early (pre-3.0) versions of Windows. In fact, they were the only bitmaps available to developers. However, as display technology improved and as the variety of display devices increased among Windows users, certain inherent problems surfaced. For example, there was no method of storing (or retrieving) the resolution of the display type on which a bitmap was created, so a drawing application could not quickly determine whether a bitmap was suitable for the type of video display device on which the application was running. To solve this problem, Microsoft created DIBs.
+$#A Device-Independent Bitmaps
A DIB contains the following color and dimension information:
· The color format of the device on which the rectangular image was created.
· The resolution of the device on which the rectangular image was created.
· The palette for the device on which the image was created.
· An array of bits that maps red, green, blue (RGB_win32_RGB) triplets to pixels in the rectangular image.
· A data-compression identifier that indicates the data compression scheme (if any) used to reduce the size of the array of bits.
This information is stored in a BITMAPINFO_win32_BITMAPINFO_str structure consisting of a BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str structure followed by two or more RGBQUAD_win32_RGBQUAD_str structures. The BITMAPINFOHEADER specifies the dimensions of the pixel rectangle, describes the device's color technology, and identifies the compression schemes used to reduce the bitmap's size. The RGBQUAD structures identify the colors that appear in the pixel rectangle.
There are two varieties of DIB: "bottom-up" DIBs, in which the origin lies at the lower left corner, and "top-down" DIBs, in which the origin lies at the upper left corner. If the height of a DIB, as indicated by the biHeight member of the BITMAPINFOHEADER structure, is a positive value, it is a bottom-up DIB; if the height is a negative value, it is a top-down DIB. Top-down DIBs cannot be compressed.
The color format is specified in terms of a count of color planes and color bits. The count of color planes is always 1; the count of color bits is 1 for monochrome bitmaps, 4 for VGA bitmaps, and 8, 16, 24, or 32 for bitmaps on other color devices. An application retrieves the number of color bits a particular display (or printer) uses by calling the GetDeviceCaps_win32_GetDeviceCaps function, specifying BITSPIXEL as the second argument.
The resolution of a display device is specified in pixels per meter. An application can retrieve the horizontal resolution for a video display, or printer, by following a three-step process:
1. Call the GetDeviceCaps function, specifying HORZRES as the second argument.
2. Call GetDeviceCaps a second time, specifying HORZSIZE as the second argument.
3. Divide the first return value by the second return value.

The application can retrieve the vertical resolution by using the same three-step process with different parameters: VERTRES in place of HORZRES, and VERTSIZE in place of HORZSIZE.
The palette is represented by an array of RGBQUAD_win32_RGBQUAD_str structures that specify the red, green, and blue intensity components for each color in a display device's color palette. Each color index in the palette array maps to a specific pixel in the rectangular region associated with the bitmap. The size of this array, in bits, is equivalent to the width of the rectangle, in pixels, multiplied by the height of the rectangle, in pixels, multiplied by the count of color bits for the device. An application can retrieve the size of the device's palette by calling the GetDeviceCaps_win32_GetDeviceCaps function, specifying the NUMCOLORS constant as the second argument.
The Win32 API supports the compression of the palette array for 8-bit-per-pixel and 4-bit-per-pixel bottom-up DIBs. These arrays can be compressed by using the run-length encoding (RLE) scheme. The RLE scheme uses 2-byte values, the first byte specifying the number of consecutive pixels that use a color index and the second byte specifying the index. For more information about bitmap compression, see the description of the BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str structure.
An application can create a DIB from a DDB by initializing the required structures and calling the GetDIBits_win32_GetDIBits function. To determine whether a device supports this function, call the GetDeviceCaps function, specifying RC_DI_BITMAP as the RASTERCAPS flag.
An application can use a DIB to set pixels on the display device by calling the SetDIBitsToDevice_win32_SetDIBitsToDevice or the StretchDIBits_win32_StretchDIBits function. To determine whether a device supports the SetDIBitsToDevice function, call the GetDeviceCaps function, specifying RC_DIBTODEV as the RASTERCAPS flag. Specify RC_STRETCHDIB as the RASTERCAPS flag to determine if the device supports StretchDIBits.
An application that simply needs to display a preexisting DIB can use the SetDIBitsToDevice function. For example, a spreadsheet application can open existing charts and display them in a window by using the SetDIBitsToDevice function. To repeatedly redraw a bitmap in a window, however, the application should use the BitBlt_win32_BitBlt function. For example, a multimedia application that combines animated graphics with sound would benefit from calling the BitBlt function because it executes faster than SetDIBitsToDevice.
+$#A Device-Dependent Bitmaps
Device-dependent bitmaps are supported only for compatibility with applications written for Windows versions earlier than 3.0. A developer writing a new application, or porting an application written for a previous version of Windows to the Win32 platform, should use DIBs.
DDBs are described by using a single structure, the BITMAP_win32_BITMAP_str structure. The members of this structure specify the width and height of a rectangular region, in pixels; the width of the array that maps entries from the device palette to pixels; and the device's color format, in terms of color planes and bits per pixel. An application can retrieve the color format of a device by calling the GetDeviceCaps_win32_GetDeviceCaps function and specifying the appropriate constants.
There are two types of DDBs: discardable and nondiscardable. A discardable DDB is a bitmap that Windows discards if the bitmap is not selected into a DC and if system memory is low. The CreateDiscardableBitmap_win32_CreateDiscardableBitmap function creates discardable bitmaps. The CreateBitmap_win32_CreateBitmap, CreateCompatibleBitmap_win32_CreateCompatibleBitmap, and CreateBitmapIndirect_win32_CreateBitmapIndirect functions create nondiscardable bitmaps.
An application can create a DDB from a DIB by initializing the required structures and calling the CreateDIBitmap_win32_CreateDIBitmap function. Specifying CBM_INIT in the call to CreateDIBitmap is equivalent to calling the CreateCompatibleBitmap function to create a DDB in the format of the device and then calling the SetDIBits_win32_SetDIBits function to translate the DIB bits to the DDB. To determine whether a device supports the SetDIBits function, call the GetDeviceCaps function, specifying RC_DI_BITMAP as the RASTERCAPS flag.
+$#A Bitmaps, Device Contexts, and Drawing Surfaces
A device context (DC) is a data structure defining the graphics objects, their associated attributes, and the graphics modes affecting output on a device. An application creates a DC by calling the CreateDC_win32_CreateDC function; it retrieves a window manager device context by calling the GetDC_win32_GetDC function.
+$#A The Drawing Surface
Before returning a handle that identifies that DC, the window manager selects a drawing surface into the DC. If the application called the CreateDC_win32_CreateDC function to create a device context for a VGA display, the dimensions of this drawing surface are 640 by 480 pixels. If the application called the GetDC_win32_GetDC function, the dimensions reflect the size of the client area.
When an application passes the handle returned by CreateDC or GetDC to one of the graphics device interface (GDI) drawing functions, the requested output appears on the drawing surface selected into the device context.
+$#A Compatible Device Contexts
To enable applications to place output in memory rather than sending it to an actual device, the Win32 API provides a special device context for bitmap operations called a compatible device context. A compatible DC enables Windows to treat a portion of memory as a virtual device. It is an array of bits in memory that an application can use temporarily to store the color data for bitmaps created on a normal drawing surface.
An application creates a compatible DC by calling the CreateCompatibleDC_win32_CreateCompatibleDC function and receives a handle that identifies a normal device context. Windows creates a temporary one-bit placeholder for the array. Before performing drawing operations using the compatible DC handle, the application must increase the size of this array. To do this, the application can call the CreateBitmap_win32_CreateBitmap, CreateBitmapIndirect_win32_CreateBitmapIndirect, or CreateCompatibleBitmap_win32_CreateCompatibleBitmap function to create a bitmap of the appropriate dimensions and then call the SelectObject_win32_SelectObject function to select the bitmap into the DC. After the bitmap is selected into the compatible DC, Windows replaces the single-bit array with an array large enough to store color information for the specified rectangle of pixels.
When an application passes the handle returned by CreateCompatibleDC to one of the GDI drawing functions, the requested output does not appear on a device's drawing surface. Instead, Windows stores the color information for the resultant line, curve, text or region in the array of bits. The application can copy the image stored in memory back onto a drawing surface by calling the BitBlt_win32_BitBlt function, identifying the compatible DC as the source device context and a window or screen DC as the target device context.
When displaying a DIB or a DDB created from a DIB on a palette device, you can improve the speed at which the image is drawn by arranging the logical palette to match the layout of the system palette. To do this, call GetDeviceCaps_win32_GetDeviceCaps with the NUMRESERVED value to get the number of reserved colors in the system. Then call GetSystemPaletteEntries_win32_GetSystemPaletteEntries and fill in the first and last NUMRESERVED/2 entries of the logical palette with the corresponding system colors. For example, if NUMRESERVED is 20, you would fill in the first and last 10 entries of the logical palette with the system colors. Then fill in the remaining 256 - NUMRESERVED colors of the logical palette (in our example, the remaining 236 colors) with colors from the DIB and set the PC_NOCOLLAPSE flag on each of these colors. For more information about color and palettes, see Colors_win32_Colors.
+$#A Bitmap Rotation
Windows provides a function to copy a bitmap into a parallelogram; this function, PlgBlt_win32_PlgBlt, performs a bit-block transfer from a rectangle in a source device context into a parallelogram in a destination device context. In order to rotate the bitmap, an application must provide the coordinates, in world units, to be used for the corners of the parallelogram. (For more information about rotation and world units, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.)
+$#A Bitmap Scaling
The Win32 API also provides a function to scale a bitmap; this function, StretchBlt_win32_StretchBlt, performs a bit-block transfer from a rectangle in a source device context into a rectangle in a destination device context. However, unlike the BitBlt_win32_BitBlt function, which duplicates the source rectangle dimensions in the destination rectangle, StretchBlt allows an application to specify the dimensions of both the source and the destination rectangles. When the destination bitmap is smaller than the source bitmap, Windows combines rows or columns of color data (or both) in the bitmap before rendering the corresponding image on the display device. Windows combines the color data according to the specified stretch mode, which the application defines by calling the SetStretchBltMode_win32_SetStretchBltMode function. When the destination bitmap is larger than the source bitmap, Windows scales or magnifies each pixel in the resultant image accordingly.
+$#A Bitmaps as Brushes
The Win32 API provides a number of functions that use the brush currently selected into a device context to perform bitmap operations. For example, the PatBlt_win32_PatBlt function replicates the brush in a rectangular region within a window, and the FloodFill_win32_FloodFill function replicates the brush inside an area in a window bounded by the specified color (unlike PatBlt, FloodFill does fill nonrectangular shapes).
The PatBlt function name (an abbreviation for pattern block transfer) implies that the function simply replicates the brush (or pattern) until it fills a specified rectangle. However, the function is actually much more powerful. Before replicating the brush, it combines the color data for the pattern with the color data for the existing pixels on the video display by using a raster operation (ROP). An ROP is a bitwise operation that is applied to the bits of color data for the replicated brush and the bits of color data for the target rectangle on the display device. There are 256 ROPs in the Win32 API; however, the PatBlt function recognizes only those that require a pattern and a destination (not those that require a source). The following table identifies the five most common ROPs.
ROP Description
PATCOPY Copies the pattern to the destination bitmap.
PATINVERT Combines the destination bitmap with the pattern by using the Boolean OR operator.
DSTINVERT Inverts the destination bitmap.
BLACKNESS Turns all output to binary zeroes.
WHITENESS Turns all output to binary ones.

The FloodFill_win32_FloodFill function replicates the brush within a region bounded by a specified color. However, unlike the PatBlt_win32_PatBlt function, FloodFill does not combine the color data for the brush with the color data for the pixels on the display; it simply sets the color of all pixels within the enclosed region on the display to the color of the brush that is currently selected into the device context.
+$#A Bitmap Storage
Bitmaps should be saved in a file that uses the established Windows format and assigned a name with the three-character .BMP extension. The established Windows format consists of a BITMAPFILEHEADER_win32_BITMAPFILEHEADER_str structure followed by a BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str structure. An array of RGBQUAD_win32_RGBQUAD_str structures (also called a color table) follows the BITMAPINFOHEADER structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).
The Windows format is shown in the following illustration.
{ewc msdncd, EWGraphic, group10651 1 /a "SDK_02.bmp"}
The members of the BITMAPFILEHEADER structure identify the file; specify the size of the file, in bytes; and specify the offset from the first byte in the header to the first byte of bitmap data. The members of the BITMAPINFOHEADER structure specify the bitmap's width and height, in pixels; the color format (count of color planes and color bits per pixel) of the display device on which the bitmap was created; whether the bitmap data was compressed before storage and the type of compression used; the number of bytes of bitmap data; the resolution of the display device on which the bitmap was created; and the number of colors represented in the data. The RGBQUAD structures specify the RGB intensity values for each of the colors in the device's palette. The color-index array maps indices values from the RGBQUAD array to pixels in a rectangular region on the display.
The following hexadecimal output shows the contents of the file REDBRICK.BMP.
0000 42 4d 76 02 00 00 00 00 00 00 76 00 00 00 28 00
0010 00 00 20 00 00 00 20 00 00 00 01 00 04 00 00 00
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80
0040 00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80
0050 00 00 80 80 80 00 c0 c0 c0 00 00 00 ff 00 00 ff
0060 00 00 00 ff ff 00 ff 00 00 00 ff 00 ff 00 ff ff
0070 00 00 ff ff ff 00 00 00 00 00 00 00 00 00 00 00
0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00
0090 00 00 00 00 00 00 11 11 01 19 11 01 10 10 09 09
00a0 01 09 11 11 01 90 11 01 19 09 09 91 11 10 09 11
00b0 09 11 19 10 90 11 19 01 19 19 10 10 11 10 09 01
00c0 91 10 91 09 10 10 90 99 11 11 11 11 19 00 09 01
00d0 91 01 01 19 00 99 11 10 11 91 99 11 09 90 09 91
00e0 01 11 11 11 91 10 09 19 01 00 11 90 91 10 09 01
00f0 11 99 10 01 11 11 91 11 11 19 10 11 99 10 09 10
0100 01 11 11 11 19 10 11 09 09 10 19 10 10 10 09 01
0110 11 19 00 01 10 19 10 11 11 01 99 01 11 90 09 19
0120 11 91 11 91 01 11 19 10 99 00 01 19 09 10 09 19
0130 10 91 11 01 11 11 91 01 91 19 11 00 99 90 09 01
0140 01 99 19 01 91 10 19 91 91 09 11 99 11 10 09 91
0150 11 10 11 91 99 10 90 11 01 11 11 19 11 90 09 11
0160 00 19 10 11 01 11 99 99 99 99 99 99 99 99 09 99
0170 99 99 99 99 99 99 00 00 00 00 00 00 00 00 00 00
0180 00 00 00 00 00 00 90 00 00 00 00 00 00 00 00 00
0190 00 00 00 00 00 00 99 11 11 11 19 10 19 19 11 09
01a0 10 90 91 90 91 00 91 19 19 09 01 10 09 01 11 11
01b0 91 11 11 11 10 00 91 11 01 19 10 11 10 01 01 11
01c0 90 11 11 11 91 00 99 09 19 10 11 90 09 90 91 01
01d0 19 09 91 11 01 00 90 10 19 11 00 11 11 00 10 11
01e0 01 10 11 19 11 00 90 19 10 91 01 90 19 99 00 11
01f0 91 01 11 01 91 00 99 09 09 01 10 11 91 01 10 91
0200 99 11 10 90 91 00 91 11 00 10 11 01 10 19 19 09
0210 10 00 99 01 01 00 91 01 19 91 19 91 11 09 10 11
0220 00 91 00 10 90 00 99 01 11 10 09 10 10 19 09 01
0230 91 90 11 09 11 00 90 99 11 11 11 90 19 01 19 01
0240 91 01 01 19 09 00 91 10 11 91 99 09 09 90 11 91
0250 01 19 11 11 91 00 91 19 01 00 11 00 91 10 11 01
0260 11 11 10 01 11 00 99 99 99 99 99 99 99 99 99 99
0270 99 99 99 99 99 90

The following table shows the data bytes associated with the structures in a bitmap file.
Structure Corresponding bytes
BITMAPFILEHEADER_win32_BITMAPFILEHEADER_str 0x00 - 0x0D
BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str 0x0E - 0x31
RGBQUAD_win32_RGBQUAD_str array 0x32 - 0x75
Color-index array 0x76 - 0x275

+$#A Using Bitmaps
· Capturing an image_win32_Capturing_an_Image
· Scaling an image_win32_Scaling_an_Image
· Storing an image_win32_Storing_an_Image

+$#A Capturing an Image
You can use a bitmap to capture an image, and you can store the captured image in memory, display it at a different location in your application's window, or display it in another window.
In some cases, you may want your application to capture images and store them only temporarily. For example, when you scale or "zoom" a picture created in a drawing application, the application must temporarily save the normal view of the image and display the zoomed view. Later, when the user selects the normal view, the application must replace the zoomed image with a copy of the normal view that it temporarily saved.
To store an image temporarily, your application must call CreateCompatibleDC_win32_CreateCompatibleDC to create a DC that is compatible with the current window DC. After you create a compatible DC, you create a bitmap with the appropriate dimensions by calling the CreateCompatibleBitmap_win32_CreateCompatibleBitmap function and then select it into this device context by calling the SelectObject_win32_SelectObject function.
After the compatible device context is created and the appropriate bitmap has been selected into it, you can capture the image. The Win32 API provides the BitBlt_win32_BitBlt function to capture images. This function performs a bit block transfer ¾ that is, it copies data from a source bitmap into a destination bitmap. Because it copies data from bitmaps, you'd expect that two arguments to this function would be bitmap handles; however, this is not the case. Instead, BitBlt receives handles that identify two device contexts and copies the bitmap data from a bitmap selected into the source DC into a bitmap selected into the target DC. In this case, the target DC is the compatible DC, so when BitBlt completes the transfer, the image has been stored in memory. To redisplay the image, call BitBlt a second time, specifying the compatible DC as the source DC and a window (or printer) DC as the target DC.
The following example code, from an application that captures an image of the entire desktop, creates a compatible device context and a bitmap with the appropriate dimensions, selects the bitmap into the compatible DC, and then copies the image using the BitBlt_win32_BitBlt function.
/*
* Create a normal DC and a memory DC for the entire screen. The
* normal DC provides a "snapshot" of the screen contents. The
* memory DC keeps a copy of this "snapshot" in the associated
* bitmap.
*/

hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL);
hdcCompatible = CreateCompatibleDC(hdcScreen);

/* Create a compatible bitmap for hdcScreen. */

hbmScreen = CreateCompatibleBitmap(hdcScreen,
GetDeviceCaps(hdcScreen, HORZRES),
GetDeviceCaps(hdcScreen, VERTRES));

if (hbmScreen == 0)
errhandler("hbmScreen", hwnd);

/* Select the bitmaps into the compatible DC. */

if (!SelectObject(hdcCompatible, hbmScreen))
errhandler("Compatible Bitmap Selection", hwnd);

/* Hide the application window. */

ShowWindow(hwnd, SW_HIDE);

/*
* Copy color data for the entire display into a
* bitmap that is selected into a compatible DC.
*/

if (!BitBlt(hdcCompatible,
0,0,
bmp.bmWidth, bmp.bmHeight,
hdcScreen,
0,0,
SRCCOPY))

errhandler("Screen to Compat Blt Failed", hwnd);

/* Redraw the application window. */

ShowWindow(hwnd, SW_SHOW);

+$#A Scaling an Image
Some applications scale images ¾ that is, they display zoomed or reduced views of an image. For example, a drawing application may provide a zoom feature that enables the user to view and edit a drawing on a pixel-by-pixel basis.
Applications scale images by calling the StretchBlt_win32_StretchBlt function. Like the BitBlt_win32_BitBlt function, StretchBlt copies bitmap data from a bitmap in a source DC into a bitmap in a target DC. However, unlike the BitBlt function, StretchBlt scales the image based on the specified dimensions of the source and target rectangles. If the source rectangle is larger than the target rectangle, the resultant image will appear to have shrunk; if the source rectangle is smaller than the target rectangle, the resultant image will appear to have expanded.
If the target rectangle is smaller than the source rectangle, StretchBlt removes color data from the image according to a specified stretch mode as shown in the following table.
Stretch Mode Method
BLACKONWHITE Performs a logical AND operation on the color data for the eliminated pixels and the color data for the remaining pixels.
WHITEONBLACK Performs a logical OR operation on the color data for the eliminated pixels and the color data for the remaining pixels.
COLORONCOLOR Eliminates the color data of the deleted pixels completely.
HALFTONE Approximates the original (source) color data in the destination.

You set the stretch mode by calling the SetStretchBltMode_win32_SetStretchBltMode function.
The following example code is taken from an application that displays an image either at its original size or a twice the original size. (This application uses the default stretch mode.)
hdcScaled = CreateCompatibleDC(hdcScreen);

hbmScaled = CreateCompatibleBitmap(hdcScreen,
GetDeviceCaps(hdcScreen, HORZRES) * 2,
GetDeviceCaps(hdcScreen, VERTRES) * 2);

if (hbmScaled == 0)
errhandler("hbmScaled", hwnd);

/* Select the bitmaps into the compatible DC. */

if (!SelectObject(hdcScaled, hbmScaled))
errhandler("Scaled Bitmap Selection", hwnd);

case WM_COMMAND: /* message: command from application menu */
switch(wParam) {

case IDM_SCALEX1:
if (fBlt){
fScaled = FALSE;
hdcWin = GetDC(hwnd);
BitBlt(hdcWin,
0,0,
bmp.bmWidth, bmp.bmHeight,
hdcCompatible,
0,0,
SRCCOPY);
ReleaseDC(hwnd, hdcWin);
}
break;

case IDM_SCALEX2:
if (fBlt){
fScaled = TRUE;
StretchBlt(hdcScaled,
0, 0,
bmp.bmWidth * 2, bmp.bmHeight * 2,
hdcCompatible,
0, 0,
bmp.bmWidth, bmp.bmHeight,
SRCCOPY);

hdcWin = GetDC(hwnd);
BitBlt(hdcWin,
0,0,
bmp.bmWidth, bmp.bmHeight,
hdcScaled,
0,0,
SRCCOPY);
ReleaseDC(hwnd, hdcWin);
}
break;

+$#A Storing an Image
Many applications store images permanently as files. For example, drawing applications store pictures, spreadsheet applications store charts, CAD applications store drawings, and so on.
If you are writing an application that will store a bitmapped image in a file, you should use the Windows file format described in Bitmap Storage_win32_Bitmap_Storage. In order to store a bitmap in this format, you must initialize a BITMAPINFO_win32_BITMAPINFO_str structure (consisting of a BITMAPFILEHEADER_win32_BITMAPFILEHEADER_str structure and an array of RGBQUAD_win32_RGBQUAD_str structures), as well as an array of palette indices.
The following example code defines a function that allocates memory for and initializes members within a BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str structure.
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp) {
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;

/* Retrieve the bitmap's color format, width, and height. */

if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
errhandler("GetObject", hwnd);

/* Convert the color format to a count of bits. */

cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);

if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else
cClrBits = 32;

/*
* Allocate memory for the BITMAPINFO structure. (This structure
* contains a BITMAPINFOHEADER structure and an array of RGBQUAD data
* structures.)
*/

if (cClrBits != 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (2^cClrBits));

/*
* There is no RGBQUAD array for the 24-bit-per-pixel format.
*/

else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));

/* Initialize the fields in the BITMAPINFO structure. */

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = 2^cClrBits;

/* If the bitmap is not compressed, set the BI_RGB flag. */

pbmi->bmiHeader.biCompression = BI_RGB;

/*
* Compute the number of bytes in the array of color
* indices and store the result in biSizeImage.
*/

pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8
* pbmi->bmiHeader.biHeight
* cClrBits;

/*
* Set biClrImportant to 0, indicating that all of the
* device colors are important.
*/

pbmi->bmiHeader.biClrImportant = 0;

return pbmi;

}

The following example code defines a function that initializes the remaining structures, retrieves the array of palette indices, opens the file, copies the data, and closes the file.
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{

HANDLE hf; /* file handle */
BITMAPFILEHEADER hdr; /* bitmap file-header */
PBITMAPINFOHEADER pbih; /* bitmap info-header */
LPBYTE lpBits; /* memory pointer */
DWORD dwTotal; /* total count of bytes */
DWORD cb; /* incremental count of bytes */
BYTE *hp; /* byte pointer */
DWORD dwTmp;

pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); if (!lpBits)
errhandler("GlobalAlloc", hwnd);

/*
* Retrieve the color table (RGBQUAD array) and the bits
* (array of palette indices) from the DIB.
*/

if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight,
lpBits, pbi, DIB_RGB_COLORS))
errhandler("GetDIBits", hwnd);

/* Create the .BMP file. */

hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
(LPSECURITY_ATTRIBUTES) NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

if (hf == INVALID_HANDLE_VALUE)
errhandler("CreateFile", hwnd);

hdr.bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */

/* Compute the size of the entire file. */

hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);

hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;

/* Compute the offset to the array of color indices. */

hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);

/* Copy the BITMAPFILEHEADER into the .BMP file. */

if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
errhandler("WriteFile", hwnd);

/* Copy the BITMAPINFOHEADER and RGBQUAD array into the file. */

if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
errhandler("WriteFile", hwnd);

/* Copy the array of color indices into the .BMP file. */

dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
while (cb > MAXWRITE) {
if (!WriteFile(hf, (LPSTR) hp, (int) MAXWRITE,
(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
errhandler("WriteFile", hwnd);
cb-= MAXWRITE;
hp += MAXWRITE;
}
if (!WriteFile(hf, (LPSTR) hp, (int) cb,
(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))
errhandler("WriteFile", hwnd);

/* Close the .BMP file. */

if (!CloseHandle(hf))
errhandler("CloseHandle", hwnd);

/* Free memory. */ GlobalFree((HGLOBAL)lpBits);}

+$#A Bitmap Functions
Following are the functions used with bitmaps.
BitBlt_win32_BitBltCreateBitmap_win32_CreateBitmapCreateBitmapIndirect_win32_CreateBitmapIndirectCreateCompatibleBitmap_win32_CreateCompatibleBitmapCreateDIBitmap_win32_CreateDIBitmapCreateDIBSection_win32_CreateDIBSectionCreateDiscardableBitmap_win32_CreateDiscardableBitmapExtFloodFill_win32_ExtFloodFillFloodFill_win32_FloodFillGetBitmapBits_win32_GetBitmapBitsGetBitmapDimensionEx_win32_GetBitmapDimensionExGetDIBColorTable_win32_GetDIBColorTableGetDIBits_win32_GetDIBitsGetPixel_win32_GetPixelGetStretchBltMode_win32_GetStretchBltModeLoadBitmap_win32_LoadBitmapMaskBlt_win32_MaskBltPatBlt_win32_PatBltPlgBlt_win32_PlgBltSetBitmapBits_win32_SetBitmapBitsSetBitmapDimensionEx_win32_SetBitmapDimensionExSetDIBColorTable_win32_SetDIBColorTableSetDIBits_win32_SetDIBitsSetDIBitsToDevice_win32_SetDIBitsToDeviceSetPixel_win32_SetPixelSetPixelV_win32_SetPixelVSetStretchBltMode_win32_SetStretchBltModeStretchBlt_win32_StretchBltStretchDIBits_win32_StretchDIBits
Obsolete or Deleted Functions
GetBitmapDimension_win32_GetBitmapDimensionSetBitmapDimension_win32_SetBitmapDimension

+$#AK Brushes
A brush is a graphics tool that a Microsoft® Win32®-based application uses to paint the interior of polygons, ellipses, and paths. Drawing applications use brushes to paint shapes; word processing applications use brushes to paint rules; computer-aided design (CAD) applications use brushes to paint the interiors of cross-section views; and spreadsheet applications use brushes to paint the sections of pie charts and the bars in bar graphs.
+$#A About Brushes
There are two types of brushes: logical and physical. A logical brush is a description of the ideal bitmap that an application would use to paint shapes. A physical brush is the actual bitmap that a device driver creates based on an application's logical-brush definition. For more information about bitmaps, see Bitmaps_win32_Bitmaps.
When an application calls one of the functions that create a brush, it retrieves a handle that identifies a logical brush. When the application passes this handle to the SelectObject_win32_SelectObject function, the device driver for the corresponding display or printer creates the physical brush.
+$#A Brush Origin
When an application calls a drawing function to paint a shape, Windows positions a brush at the start of the paint operation and maps a pixel in the brush bitmap to the window origin of the client area. (The window origin is the upper-left corner of the window's client area.) The coordinates of the pixel that Windows maps are called the brush origin. The default brush origin is located in the upper-left corner of the brush bitmap, at the coordinates (0,0). Windows then copies the brush across the client area, forming a pattern that is as tall as the bitmap. The copy operation continues, row by row, until the entire client area is filled. However, the brush pattern is visible only within the boundaries of the specified shape.
There are instances when the default brush origin should not be used. For example, it may be necessary for an application to use the same brush to paint the backgrounds of its parent and child windows and blend a child window's background with that of the parent window. To do this, the application should reset the brush origin by calling the SetBrushOrgEx_win32_SetBrushOrgEx function and shifting the origin the required number of pixels. (An application can retrieve the current brush origin by calling the GetBrushOrgEx_win32_GetBrushOrgEx function.)
The following illustration shows a five-pointed star filled by using an application-defined brush. The illustration shows a zoomed image of the brush, as well as the location to which it was mapped at the beginning of the paint operation.
{ewc msdncd, EWGraphic, group10652 0 /a "SDK_01.bmp"}
+$#A Logical Brush Types
There are four types of logical brushes (solid, stock, hatch, and pattern) as shown in the following illustration.
{ewc msdncd, EWGraphic, group10652 1 /a "SDK_02.bmp"}
The stock and hatch types each have several predefined brushes, as described in Hatch Brush_win32_Hatch_Brush.
+$#A Solid Brush
A solid brush is a logical brush that contains 64 pixels of the same color. An application can create a solid logical brush by calling the CreateSolidBrush_win32_CreateSolidBrush function, specifying the color of the brush required. After creating the solid brush, the application can select it into its device context and use it to paint filled shapes.
+$#A Stock Brush
There are seven predefined logical stock brushes maintained by the graphics device interface (GDI). There are also 21 predefined logical stock brushes maintained by the window management interface (USER).
The following rectangles were painted by using the seven predefined stock brushes.
{ewc msdncd, EWGraphic, group10652 2 /a "SDK_03.bmp"}
An application can retrieve a handle identifying one of the seven stock brushes by calling the GetStockObject_win32_GetStockObject function, specifying the brush type.
The 21 stock brushes maintained by the window management interface correspond to the colors of window elements such as menus, scroll bars, and buttons. An application can obtain a handle identifying one of these brushes by calling the GetSysColorBrush_win32_GetSysColorBrush function and specifying a system-color value. An application can retrieve the color corresponding to a particular window element by calling the GetSysColor_win32_GetSysColor function. An application can set the color corresponding to a window element by calling the SetSysColors_win32_SetSysColors function.
+$#A Hatch Brush
There are six predefined logical hatch brushes maintained by GDI. The following rectangles were painted by using the six predefined hatch brushes.
{ewc msdncd, EWGraphic, group10652 3 /a "SDK_04.bmp"}
An application can create a hatch brush by calling the CreateHatchBrush_win32_CreateHatchBrush function, specifying one of the six hatch styles.
+$#A Pattern Brush
A pattern (or custom) brush is created from an application-defined bitmap or device-independent bitmap (DIB). The following rectangles were painted by using different pattern brushes.
{ewc msdncd, EWGraphic, group10652 4 /a "SDK_05.bmp"}
To create a logical pattern brush, an application must first create a bitmap. After creating the bitmap, the application can create the logical pattern brush by calling the CreatePatternBrush_win32_CreatePatternBrush or CreateDIBPatternBrushPt_win32_CreateDIBPatternBrushPt function, supplying a handle that identifies the bitmap (or DIB). The brushes that appear in the preceding illustration were created from monochrome bitmaps. For a description of bitmaps, DIBs, and the functions that create them, see Bitmaps_win32_Bitmaps.
+$#A Using Brushes
You can use a brush to paint the interior of virtually any shape by using a GDI function. This includes the interiors of rectangles, ellipses, polygons, and paths. Depending on the requirements of your application, you can use a solid brush of a specified color, a stock brush, a hatch brush, or a pattern brush.
This section contains code samples that demonstrate the creation of a custom brush dialog box. The dialog box contains a grid that represents the bitmap Windows uses as a brush. A user can use this grid to create a pattern-brush bitmap and then view the custom pattern by clicking the Test Pattern button.
The following illustration shows a pattern created by using the Custom Brush dialog box.
{ewc msdncd, EWGraphic, group10652 5 /a "SDK_06.bmp"}
To display a dialog box, you must first create a dialog box template. The following dialog box template defines the Custom Brush dialog box.
CustBrush DIALOG 6, 18, 160, 118
STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Custom Brush"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "", IDD_GRID, "Static", SS_BLACKFRAME |
WS_CHILD, 3, 2, 83, 79
CONTROL "", IDD_RECT, "Static", SS_BLACKFRAME |
WS_CHILD, 96, 11, 57, 28
PUSHBUTTON "Test Pattern", IDD_PAINTRECT, 96, 47, 57, 14
PUSHBUTTON "OK", IDD_OK, 29, 98, 40, 14
PUSHBUTTON "Cancel", IDD_CANCEL, 92, 98, 40, 14
END

The Custom Brush dialog box contains five controls: a bitmap-grid window, a pattern-viewing window, and three push buttons, labeled Test Pattern, OK and Cancel. The Test Pattern push button enables the user to view the pattern. The dialog box template specifies the overall dimensions of the dialog box window, assigns a value to each control, specifies the location of each control, and so forth. (For more information about creating dialog box templates, see Dialog Boxes_win32_Dialog_Boxes.)
The control values in the dialog box template are constants that have been defined as follows in the application's header file.
#define IDD_GRID 120
#define IDD_RECT 121
#define IDD_PAINTRECT 122
#define IDD_OK 123
#define IDD_CANCEL 124

After you create a dialog box template and include it in the application's resource-definition file, you must write a dialog procedure. This procedure processes messages that Windows sends to the dialog box. The following excerpt from an application's source code shows the dialog box procedure for the Custom Brush dialog box and the two application-defined functions it calls.
int APIENTRY BrushDlgProc(HWND hdlg, WORD message, LONG wParam,
LONG lParam)
{
static HWND hwndGrid; /* grid-window control */
static HWND hwndBrush; /* pattern-brush control */
static RECT rctGrid; /* grid-window rectangle */
static RECT rctBrush; /* pattern-brush rectangle */
static UINT bBrushBits[8]; /* bitmap bits */
static RECT rect[64]; /* grid-cell array */
static HBITMAP hbm; /* bitmap handle */
HBRUSH hbrush; /* current brush */
HBRUSH hbrushOld; /* default brush */
HRGN hrgnCell; /* test-region handle */
HDC hdc; /* DC handle */
int x, y, deltaX, deltaY; /* drawing coordinates */
POINTS ptlHit; /* mouse coordinates */
int i; /* count variable */

switch (message)
{
case WM_INITDIALOG:

/*
* Retrieve a window handle for the grid-window and
* pattern-brush controls
*/

hwndGrid = GetDlgItem(hdlg, IDD_GRID);
hwndBrush = GetDlgItem(hdlg, IDD_RECT);

/* Initialize the array of bits that defines the
* custom brush pattern with the value 1 to produce a
* solid white brush).
*/
for (i=0; i<8; i++)
bBrushBits[i] = 0xFF;

/*
* Retrieve dimensions for the grid-window and pattern-brush
* controls.
*/

GetClientRect(hwndGrid, &rctGrid);
GetClientRect(hwndBrush, &rctBrush);

/* Determine the width and height of a single cell. */

deltaX = (rctGrid.right - rctGrid.left)/8;
deltaY = (rctGrid.bottom - rctGrid.top)/8;

/* Initialize the array of cell rectangles. */

for (y=rctGrid.top, i=0; y < rctGrid.bottom; y += deltaY){
for (x=rctGrid.left; x < (rctGrid.right - 8) && i < 64;
x += deltaX, i++) {
rect[i].left = x; rect[i].top = y;
rect[i].right = x + deltaX;
rect[i].bottom = y + deltaY;
}
}
return FALSE;

case WM_PAINT:

/* Draw the grid. */

hdc = GetDC(hwndGrid);

for (i=rctGrid.left; i<rctGrid.right;
i+=(rctGrid.right - rctGrid.left)/8){
MoveToEx(hdc, i, rctGrid.top, NULL);
LineTo(hdc, i, rctGrid.bottom);
}
for (i=rctGrid.top; i<rctGrid.bottom;
i+=(rctGrid.bottom - rctGrid.top)/8){
MoveToEx(hdc, rctGrid.left, i, NULL);
LineTo(hdc, rctGrid.right, i);
}
ReleaseDC(hwndGrid, hdc);
return FALSE;

case WM_LBUTTONDOWN:

/* Store the mouse coordinates in a POINT structure. */

ptlHit = MAKEPOINTS((POINTS FAR *)lParam);

/*
* Create a rectangular region with dimensions and
* coordinates that correspond to those of the grid
* window.
*/

hrgnCell = CreateRectRgn(rctGrid.left, rctGrid.top,
rctGrid.right, rctGrid.bottom);

/* Retrieve a window DC for the grid window. */

hdc = GetDC(hwndGrid);

/* Select the region into the DC. */

SelectObject(hdc, hrgnCell);

/* Test for a button click in the grid-window rectangle. */

if (PtInRegion(hrgnCell, ptlHit.x, ptlHit.y)){

/*
* A button click occurred in the grid-window rectangle;
* isolate the cell in which it occurred.
*/

for(i=0; i<64; i++){
DeleteObject(hrgnCell);

hrgnCell = CreateRectRgn(rect[i].left, rect[i].top,
rect[i].right, rect[i].bottom);

if (PtInRegion(hrgnCell, ptlHit.x, ptlHit.y)){
InvertRgn(hdc, hrgnCell);

/* Set the appropriate brush bits. */

if (i % 8 == 0)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x80;
else if (i % 8 == 1)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x40;
else if (i % 8 == 2)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x20;
else if (i % 8 == 3)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x10;
else if (i % 8 == 4)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x08;
else if (i % 8 == 5)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x04;
else if (i % 8 == 6)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x02;
else if (i % 8 == 7)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x01;

/* Exit the "for" loop after the bit is set. */

break;
} /* end if */

} /* end for */

} /* end if */

/* Release the DC for the control. */

ReleaseDC(hwndGrid, hdc);
return TRUE;

case WM_COMMAND:
switch (wParam){
case IDD_PAINTRECT:

hdc = GetDC(hwndBrush);

/* Create a monochrome bitmap. */

hbm = CreateBitmap(8, 8, 1, 1,
(LPBYTE)bBrushBits);

/* Select the custom brush into the DC. */

hbrush = CreatePatternBrush(hbm);

hbrushOld = SelectObject(hdc, hbrush);

/* Use the custom brush to fill the rectangle. */

Rectangle(hdc, rctBrush.left, rctBrush.top,
rctBrush.right, rctBrush.bottom);

/* Clean up memory. */
SelectObject(hdc, hbrushOld);
DeleteObject(hbrush);
DeleteObject(hbm);

ReleaseDC(hwndBrush, hdc);
return TRUE;

case IDD_OK:

case IDD_CANCEL:
EndDialog(hdlg, TRUE);
return TRUE;

} /* end switch */
break;
default:
return FALSE;
}
}

int GetStrLngth(LPTSTR cArray)
{
int i = 0;

while (cArray[i++] != 0);
return i-1;

}

DWORD RetrieveWidth(LPTSTR cArray, int iLength)
{
int i, iTmp;
double dVal, dCount;

dVal = 0.0;
dCount = (double)(iLength-1);
for (i=0; i<iLength; i++){
iTmp = cArray[i] - 0x30;
dVal = dVal + (((double)iTmp) * pow(10.0, dCount--));
}

return (DWORD)dVal;
}

The dialog box procedure for the Custom Brush dialog box processes four messages, as described in the following table.
Message Action
WM_INITDIALOG_win32_WM_INITDIALOG Retrieves a window handle and dimensions for the grid-window and pattern-brush controls, computes the dimensions of a single cell in the grid-window control, and initializes an array of grid-cell coordinates.
WM_PAINT_win32_WM_PAINT Draws the 67rid pattern in the grid-window control.
WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN Determines whether the cursor is within the grid-window control when the user presses the left mouse button. If so, the dialog box procedure inverts the appropriate grid cell and records the state of that cell in an array of bits that is used to create the bitmap for the custom brush.
WM_COMMAND_win32_WM_COMMAND Processes input for the three push button controls. If the user presses the Test Pattern button, the dialog box procedure paints the Test Pattern control with the new custom brush pattern. If the user presses the OK or Cancel button, the dialog box procedure performs actions accordingly.

For more information about messages and message processing, see Messages and Message Queues_win32_Messages_and_Message_Queues.
After you write the dialog box procedure, export it in the module-definition file, include the function definition for the procedure in the application's header file, and then call the dialog box procedure at the appropriate point in the application.
The following excerpt from the module-definition file shows how the dialog box procedure for the Custom Brush is exported.
; Export all functions that are called by a Windows routine.

EXPORTS
MainWndProc @1 ; name of window processing function
BrushDlgProc @2 ; name of custom-brush processing function

The following excerpt from the application's header file shows the function definition for the dialog box procedure and the two functions it calls.
int APIENTRY BrushDlgProc(HWND hdlg, WORD message, LONG wParam,
LONG lParam);
int GetStrLngth(LPTSTR cArray);
DWORD RetrieveWidth(LPTSTR cArray, int iLength);

Finally, the following code shows how the dialog box procedure is called from the application's source-code file. In this example, the dialog box procedure is called when the user chooses an option from the application's menu.
switch (message) {
case WM_CREATE:
break;
case WM_COMMAND: /* message: command from application menu */
switch(wParam) {

case IDM_CUSTOMBRUSH:
DialogBox((HANDLE)GetModuleHandle(NULL),
(LPTSTR)"CustBrush", hWnd,
(DLGPROC)BrushDlgProc);
break;

+$#A Brush Functions
Following are the functions that can be used with brushes.
CreateBrushIndirect_win32_CreateBrushIndirectCreateDIBPatternBrush_win32_CreateDIBPatternBrushCreateDIBPatternBrushPt_win32_CreateDIBPatternBrushPtCreateHatchBrush_win32_CreateHatchBrushCreatePatternBrush_win32_CreatePatternBrushCreateSolidBrush_win32_CreateSolidBrushGetBrushOrgEx_win32_GetBrushOrgEx GetSysColorBrush_win32_GetSysColorBrushSetBrushOrgEx_win32_SetBrushOrgEx
Obsolete or Deleted Functions
FixBrushOrgEx_win32_FixBrushOrgEx

+$#AK Clipping
Clipping is the process of limiting output to a region or path within the client area of an application's window.
+$#A About Clipping
Clipping is used by Microsoft® Win32®-based applications in a variety of ways. Word processing and spreadsheet applications clip keyboard input to keep it from appearing in the margins of a page or spreadsheet. Computer-aided design (CAD) and drawing applications clip graphics output to keep it from overwriting the edges of a drawing or picture.
A clipping region is a region with edges that are either straight lines or curves. A clip path is a region with edges that are straight lines, Bézier curves, or combinations of both. For more information about regions, see Regions_win32_Regions. For more information about paths, see Paths_win32_Paths.
+$#A Clipping Regions
A clipping region is one of the graphic objects that an application can select into a device context (DC). It is typically rectangular. Some device contexts provide a predefined or default clipping region while others do not. For example, if you obtain a device context handle from the BeginPaint_win32_BeginPaint function, the DC contains a predefined rectangular clipping region that corresponds to the invalid rectangle that requires repainting. However, if you obtain a device context handle by calling the CreateDC_win32_CreateDC or GetDC_win32_GetDC function, the DC does not contain a default clipping region. For more information about device contexts returned by the BeginPaint function, see Painting and Drawing_win32_Painting_and_Drawing. For more information about device contexts returned by the CreateDC and GetDC functions, see Device Contexts_win32_Device_Contexts.
Applications can perform a variety of operations on clipping regions. Some of these operations require a handle identifying the region and some do not. For example, an application can perform the following operations directly on a device context's clipping region:
· Determine whether graphics output appears within the region's borders by passing coordinates of the corresponding line, arc, bitmap, text, or filled shape to the PtVisible_win32_PtVisible function.
· Determine whether part of the client area intersects a region by calling the RectVisible_win32_RectVisible function.
· Move the existing region by a specified offset by calling the OffsetClipRgn_win32_OffsetClipRgn function.
· Exclude a rectangular part of the client area from the current clipping region by calling the ExcludeClipRect_win32_ExcludeClipRect function.
· Combine a rectangular part of the client area with the current clipping region by calling the IntersectClipRect_win32_IntersectClipRect function.
After obtaining a handle identifying the clipping region, an application can perform any operation that is common with regions; for example:
· Combining a copy of the current clipping region with a second region by calling the CombineRgn_win32_CombineRgn function.
· Compare a copy of the current clipping region to a second region by calling the EqualRgn_win32_EqualRgn function.
· Determine whether a point lies within the interior of a copy of the current clipping region by calling the PtInRegion_win32_PtInRegion function.
+$#A Clip Paths
Like a clipping region, a clip path is another graphics object that an application can select into a device context. Unlike a clipping region, a clip path is always created by an application and it is used for clipping to one or more irregular shapes. For example, an application can use the lines and curves that form the outlines of characters in a string of text to define a clip path.
To create a clip path, it's first necessary to create a path that describes the required irregular shape. Paths are created by calling the appropriate graphics device interface (GDI) drawing functions after calling the BeginPath_win32_BeginPath function and before calling the EndPath_win32_EndPath function. This collection of functions is called a path bracket. For more information about paths and path brackets, see Paths_win32_Paths.
After the path is created, it can be converted to a clip path by calling the SelectClipPath_win32_SelectClipPath function, identifying a device context, and specifying a usage mode. The usage mode determines how Windows combines the new clip path with the device context's original clipping region. The following table describes the usage modes.
Mode Description
RGN_AND The clip path includes the intersection (overlapping areas) of the device context's clipping region and the current path.
RGN_COPY The clip path is the current path.
RGN_DIFF The clip path includes the device context's clipping region with any intersecting parts of the current path excluded.
RGN_OR The clip path includes the union (combined areas) of the device context's clipping region and the current path.
RGN_XOR The clip path includes the union of the device context's clipping region and the current path but excludes the intersection.

+$#A Using Clipping
This section contains example code that shows how to generate a clip path consisting of a character string. The example creates a logical font and uses it to draw a string within a clip path, then fills the path by drawing horizontal and vertical lines.

/*
* DoClipPat - Draws a clip path using the specified string
* Return value - TRUE if successful; FALSE otherwise
* lplf - address of a LOGFONT structure that defines the font to
* use to draw the clip path
* lpsz - address of a string to use for the clip path
*/

BOOL DoClipPath(LPLOGFONT lplf, LPSTR lpsz)
{
LOGFONT lf; /* logical font structure */
HFONT hfont; /* new logical font handle */
HFONT hfontOld; /* original logical font handle */
HDC hdc; /* display DC handle */
int nXStart, nYStart; /* drawing coordinates */
RECT rc; /* rectangle structure for painting window */
SIZE sz; /* size structure that receives text extents */
int nStrLen; /* length of the string */
int i; /* loop counter */

/* Retrieve a cached DC for the window. */

hdc = GetDC(hwnd);

/* Erase the current window contents. */

GetClientRect(hwnd, &rc);
FillRect(hdc, &rc, GetStockObject(WHITE_BRUSH));

/*
* Use the specified font to create a logical font and select it
* into the DC.
*/

hfont = CreateFontIndirect(lplf);
if (hfont == NULL)
return FALSE;
hfontOld = SelectObject(hdc, hfont);

/* Create a clip path. */

nStrLen = lstrlen(lpsz);
BeginPath(hdc);
TextOut(hdc, nXStart, nYStart, lpsz, nStrLen);
EndPath(hdc);
SelectClipPath(hdc, RGN_DIFF);

/*
* Retrieve the dimensions of the rectangle surrounding
* the text.
*/

GetTextExtentPoint32(hdc, lpsz, nStrLen, &sz);

/* Draw horizontal lines through the clip path. */

for (i = nYStart + 1; i < (nYStart + sz.cy); i += 3) {
MoveToEx(hdc, nXStart, i, (LPPOINT) NULL);
LineTo(hdc, (nXStart + sz.cx), i);
}

/* Draw vertical lines through the clip path. */

for (i = nXStart + 1; i < (nXStart + sz.cx); i += 3){
MoveToEx(hdc, i, nYStart, (LPPOINT) NULL);
LineTo(hdc, i, (nYStart + sz.cy));
}

/* Select the original font into the DC and release the DC. */

SelectObject(hdc, hfontOld);
DeleteObject(hfont);
ReleaseDC(hwnd, hdc);

return TRUE;
}

For an example that demonstrates how an application creates a rectangular clipping region, see Regions_win32_Regions.
+$#A Clipping Functions
Following are the functions that can be used with clipping.
ExcludeClipRect_win32_ExcludeClipRectExtSelectClipRgn_win32_ExtSelectClipRgnGetClipBox_win32_GetClipBoxGetClipRgn_win32_GetClipRgnGetMetaRgn_win32_GetMetaRgnIntersectClipRect_win32_IntersectClipRectOffsetClipRgn_win32_OffsetClipRgnPtVisible_win32_PtVisibleRectVisible_win32_RectVisibleSelectClipPath_win32_SelectClipPathSelectClipRgn_win32_SelectClipRgnSetMetaRgn_win32_SetMetaRgn

+$#AK Colors
Color is an important element in the pictures and images generated by Windows-based applications. This overview describes how Windows-based applications can manage and use colors with pens, brushes, text, or bitmaps.
+$#A About Colors
Color can be used to communicate ideas, show relationships between items, and improve the appeal and quality of output. Windows enables applications to discover the color capabilities of given devices and to choose from the available colors those that best suit their needs.
Although not described in this overview, image color matching is an important feature of color management that helps ensure that color images look the same whether displayed on screen or printed on paper. For more information, see Image Color Matching_win32_Image_Color_Matching.
+$#A Color Basics
The color capabilities of devices, such as displays and printers, can range from monochrome to thousands of colors. Because an application may need to generate output for devices throughout this range, it should be prepared be prepared to handle varying color capabilities.
An application can discover the number of colors available for a given device by using the GetDeviceCaps_win32_GetDeviceCaps function to retrieve the NUMCOLORS value. This value specifies the count of colors available for use by the application. Usually, this count corresponds to a physical property of the output device, such as the number of inks in the printer or the number of distinct color signals the display adapter can transmit to the monitor.
Although the NUMCOLORS value specifies the count of colors, it does not identify what the available colors are. An application can discover what colors are available by enumerating all pens having the PS_SOLID type. Because the device driver that supports a given device usually has a full range of solid pens and because Windows requires that solid pens have only colors that the device can generate, enumerating these pens is often equivalent to enumerating the colors. An application can enumerate the pens by using the EnumObjects_win32_EnumObjects function.
+$#A Color Values
By default, Windows defines color as a combination of three primary colors ¾ red, green, and blue. Windows identifies a color by giving it a color value (sometimes called an RGB triplet), which consists of three 8-bit values specifying the intensities of its color components. Black has the minimum intensity for red, green, and blue, so the color value for black is (0, 0, 0). White has the maximum intensity for red, green, and blue, so its color value is (255, 255, 255).

Note If image color matching is enabled, the definition of color and the meaning of a color value depends on the type of color space that is currently set for the device context.

Windows and applications use parameters and variables having the COLORREF_win32_COLORREF_str type to pass and store color values. For example, the EnumObjects_win32_EnumObjects function identifies the color of each pen by setting the lopnColor member in a LOGPEN_win32_LOGPEN_str structure to a color value. Applications can extract the individual values of the red, green, and blue components from a color value by using the GetRValue_win32_GetRValue, GetGValue_win32_GetGValue, and GetBValue_win32_GetBValue macros, respectively. Applications can create a color value from individual component values by using the RGB_win32_RGB macro. When creating or examining a logical palette, an application uses the RGBQUAD_win32_RGBQUAD_str structure to define color values and to examine individual component values.
+$#A Color Approximations and Dithering
Although an application can use color without regard to the color capabilities of the device, the resulting output may not be as informative and pleasing as output for which color is carefully chosen. Few, if any, devices guarantee an exact match for every possible color value; therefore, if an application requests a color that the device cannot generate, Windows approximates that color by using a color that the device can generate. For example, if an application attempts to create a red pen for a black and white printer, it will receive a black pen instead ¾ Windows uses black as the approximation for red.
An application can discover whether Windows will approximate a given color by using the GetNearestColor_win32_GetNearestColor function. The function takes a color value and returns the color value of the closest matching color the device can generate. The method Windows uses to determine this approximation depends on the device driver and its color capabilities. In most cases, the approximated color's overall intensity is closest to that of the requested color.
When an application creates a pen or sets the color for text, Windows always approximates a color if no exact match exists. When an application creates a solid brush, Windows may attempt to simulate the requested color by dithering. Dithering simulates a color by alternating two or more colors in a pattern. For example, different shades of pink can be simulated by alternating different combinations of red and white. Depending on the colors and the pattern, dithering can produce reasonable simulations. It is most useful for monochrome devices, because it expands the number of available "colors" well beyond simple black and white.
The method used to create dithered colors depends on the device driver. Most device drivers use a standard dithering algorithm, which generates a pattern based on the intensity values of the requested red, green, and blue colors. In general, any requested color that cannot be generated by the device is subject to simulation, but an application is not notified when Windows simulates a color. Furthermore, an application cannot modify or change the dithering algorithm of the device driver. An application, however, can bypass the algorithm by creating and using pattern brushes. In this way, the application creates its own dithered colors by combining solid colors in the bitmap that it uses to create the brush.
+$#A Color in Bitmaps
Windows handles colors in bitmaps differently than colors in pens, brushes, and text. Compatible bitmaps, created by using the CreateBitmap_win32_CreateBitmap or CreateCompatibleBitmap_win32_CreateCompatibleBitmap function, are device specific and retain color information in a device-dependent format. No color values are used, and the colors are not subject to approximations and dithering.
Device-independent bitmaps (DIBs) retain color information either as color values or color palette indices. If color values are used, the colors are subject to approximation, but not dithering. Color palette indices can only be used with devices that support color palettes. Although Windows does not approximate or dither colors identified by indices, the resulting color may be different than that intended, because the indices yield valid results only in the context of the color palette that was current at the time the bitmap was created. If the palette changes, so do the colors in the bitmap.
+$#A Color Mixing
Color mixing lets an application create new colors by combining the pen or brush color with colors in the existing image. The application can choose either to draw the pen or brush color as is (effectively drawing over any existing image) or to mix the color with the colors already present.
The foreground mix mode, sometimes called the binary raster operation, determines how these colors are mixed. An application can merge colors, preserving all components of both colors; mask colors, removing or moderating components that are not common; or exclusively mask colors, removing or moderating components that are common. There are several variations on these basic mixing operations.
Color mixing is subject to color approximation. If the result of color mixing is a color that the device cannot generate, Windows approximates the result, using a color it can generate. If an application mixes dithered colors, the individual colors used to create the dithered color are mixed, and the results are subject to color approximation.
An application sets the foreground mix mode by using the SetROP2_win32_SetROP2 function and retrieves the current mode by using the GetROP2_win32_GetROP2 function.
Although there is a background mix mode, that mode does not control the mixing of colors. Instead, it specifies whether a background color is used when drawing styled lines, hatched brushes, and text.
+$#A Color Palettes
A color palette is an array that contains color values identifying the colors that can currently be displayed or drawn on the output device. Color palettes are used by devices that are capable of generating many colors but that can only display or draw a subset of these at any given time. For such devices, Windows maintains a system palette to track and manage the current colors of the device. Applications do not have direct access to the system palette. Instead, Windows associates a default palette with each device context. Applications can use the colors in the default palette or define their own colors by creating logical palettes and associating them with individual device contexts.
An application can determine whether a device supports color palettes by checking for the RC_PALETTE bit in the RASTERCAPS value returned by the GetDeviceCaps_win32_GetDeviceCaps function.
+$#A Default Palette
The default palette is an array of color values identifying the colors that can be used with a device context by default. Windows associates the default palette with a context whenever an application creates a context for a device that supports color palettes. The default palette ensures that colors are available for use by an application without any further action.
The default palette typically has 20 entries (colors), but the exact number of entries may vary from device to device. This number is equal to the NUMCOLORS value returned by the GetDeviceCaps_win32_GetDeviceCaps function. An application can retrieve the color values for colors in the default palette by enumerating solid pens, the same technique used to discover the colors available on nonpalette devices. The colors in the default palette depend on the device. Display devices, for example, often use the 16 standard colors of the VGA display and 4 other colors defined by Windows. Printer devices may use other default colors.
When using the default palette, applications use color values to specify pen and text colors. If the requested color is not in the palette, Windows approximates the color by using the closest color in the palette. If an application requests a solid brush color that is not in the palette, Windows simulates the color by dithering with colors that are in the palette.
To avoid approximations and dithering, applications can also specify pen, brush, and text colors by using color palette indices rather than color values. A color palette index is an integer value that identifies a specific palette entry. Applications can use color palette indices in place of color values but must use the PALETTEINDEX_win32_PALETTEINDEX macro to create the indices.
Color palette indices are only useful for devices that support color palettes. To avoid this device dependence, applications that use the same code to draw to both palette and nonpalette devices should use palette-relative color values to specify pen, brush, and text colors. These values are identical to color values except when creating solid brushes. (On palette devices, a solid brush color specified by a palette-relative color value is subject to color approximation instead of dithering.) Applications must use the PALETTERGB_win32_PALETTERGB macro to create palette-relative color values.
Windows does not allow an application to change the entries in the default palette. To use colors other than those in the default palette, an application must create its own logical palette and select the palette into the device context.
+$#A Logical Palette
A logical palette is a color palette that an application creates and associates with a given device context. Logical palettes let applications define and use colors that meet their specific needs. Applications can create any number of logical palettes, using them for separate device contexts or switching between them for a single device context. The maximum number of palettes that an application can create depends on the resources of the system.
An application creates a logical palette by using the CreatePalette_win32_CreatePalette function. The application fills a LOGPALETTE_win32_LOGPALETTE_str structure, which specifies the number of entries and the color values for each entry, and then the application passes the structure to CreatePalette. The function returns a palette handle that the application uses in all subsequent operations to identify the palette. To use colors in the logical palette, the application selects the palette into a device context by using the SelectPalette_win32_SelectPalette function and then realizes the palette by using the RealizePalette_win32_RealizePalette function. The colors in the palette are available as soon as the logical palette is realized.
An application should limit the size of its logical palettes to just enough entries to represent the colors needed. Applications cannot create logical palettes larger than the maximum palette size, a device-dependent value. Applications can obtain the maximum size by using the GetDeviceCaps_win32_GetDeviceCaps function to retrieve the SIZEPALETTE value.
Although an application can specify any color value for a given entry in a logical palette, not all colors can be generated by the given device. Windows does not provide a way to discover which colors are supported, but the application can discover the total number of these colors by retrieving the color resolution of the device. The color resolution, specified in color bits per pixel, is equal to the COLORRES value returned by the GetDeviceCaps function. A device that has a color resolution of 18 has 262,144 possible colors. If an application requests a color that is not supported, Windows chooses an appropriate approximation.
Once a logical palette is created, an application can change colors in the palette by using the SetPaletteEntries_win32_SetPaletteEntries function. If the logical palette has been selected and realized, changing the palette does not immediately affect the colors being displayed. The application must use the UnrealizeObject_win32_UnrealizeObject and RealizePalette functions to update the colors. In some cases, the application may need to deselect, unrealize, select, and realize the logical palette to ensure that the colors are updated exactly as requested. If an application selects a logical palette into more than one device context, changes to the logical palette affect all device contexts for which it is selected.
An application can change the number of entries in a logical palette by using the ResizePalette_win32_ResizePalette function. If the application reduces the size, the remaining entries are unchanged. If the application extends the size, Windows sets the color for each new entry to black (0, 0, 0) and the flag to zero.
An application can retrieve the color and flag values for entries in a given logical palette by using the GetPaletteEntries_win32_GetPaletteEntries function. An application can retrieve the index for the entry in a given logical palette that most closely matches a specified color value by using the GetNearestPaletteIndex_win32_GetNearestPaletteIndex function.
When an application no longer needs a logical palette, it can delete it by using the DeleteObject_win32_DeleteObject function. The application must make sure the logical palette is no longer selected into a device context before deleting the palette.
+$#A Palette Animation
Palette animation is a technique to simulate motion by rapidly changing the colors of selected entries in a color palette. An application can carry out palette animation by creating a logical palette that contains "reserved" entries and then using the AnimatePalette_win32_AnimatePalette function to change colors in those reserved entries.
An application creates a reserved entry in a logical palette by setting the peFlags member of the PALETTEENTRY_win32_PALETTEENTRY_str structure to the PC_RESERVED flag. Once this logical palette is selected and realized, the application can call the AnimatePalette function to change one or more reserved entries. If the given palette is associated with the active window, Windows updates the colors on the screen immediately.
+$#A System Palette
Windows maintains a system palette for each device that uses palettes. The system palette contains the color values for all colors that can currently be displayed or drawn by the device. Other than viewing the contents of the system palette, applications cannot access the system palette directly. Instead, Windows has complete control of the system palette and permits access only through the use of logical palettes.
An application can view the contents of the system palette by using the GetSystemPaletteEntries_win32_GetSystemPaletteEntries function. This function retrieves the contents of one or more entries, up to the total number of entries in the system palette. The total is always equal to the number returned for the SIZEPALETTE value by the GetDeviceCaps_win32_GetDeviceCaps function and is the same as the maximum size for any given logical palette.
Although applications cannot change colors in the system palette directly, they may cause changes when realizing logical palettes. To realize a palette, Windows examines each requested color and attempts to find an entry in the system palette that contains an exact match. If Windows finds a matching color, it maps the logical palette index to the corresponding system palette index. If Windows does not find an exact match, it copies the requested color to an unused system palette entry before mapping the indices. If all system palette entries are in use, Windows maps the logical palette index to the system palette entry whose color most closely matches the requested color. Once this mapping is set, applications cannot override it. For example, applications cannot use system palette indices to specify colors; only logical palette indices are permitted.
Applications can modify the way indices are mapped by setting the peFlags member of the PALETTEENTRY_win32_PALETTEENTRY_str structure to selected values when creating the logical palette. For example, the PC_NOCOLLAPSE flag directs Windows to immediately copy the requested color to an unused system palette entry regardless of whether a system palette entry already contains that color. Also, the PC_EXPLICIT flag directs Windows to map the logical palette index to an explicitly given system palette index. (The application gives the system palette index in the low-order word of the PALETTEENTRY structure.)
Palettes can be realized as either a background palette or a foreground palette by specifying TRUE or FALSE respectively for the bForceBackground parameter in the SelectPalette_win32_SelectPalette function. There can be only one foreground palette in the system at a time. If the window is the currently active window or a descendent of the currently active window, it can realize a foreground palette. Otherwise the palette is realized as a background palette regardless of the value of the bForceBackground parameter. The critical property of a foreground palette is that, when realized, it can overwrite all entries (except for the static entries) in the system palette. Windows accomplishes this by marking all of the entries that are not static in the system palette as unused before the realization of a foreground palette, thereby eliminating all of the used entries. No preprocessing occurs on the system palette for a background palette realization. The foreground palette sets all of the possible nonstatic colors. Background palettes can set only what remains open and are prioritized in a first-come, first-serve manner. Typically, applications use background palettes for child windows which realize their own individual palettes. This helps minimize the number of changes that occur to the system palette.
An unused system palette entry is any entry that is not reserved and does not contain a static color. Reserved entries are explicitly marked with the PC_RESERVED value. These entries are created when an application realizes a logical palette for palette animation. Static color entries are created by Windows and correspond to the colors in the default palette. The GetDeviceCaps_win32_GetDeviceCaps function can be used to retrieve the NUMRESERVED value, which specifies the number of system palette entries reserved for static colors.
Because the system palette has a limited number of entries, selecting and realizing a logical palette for a given device may affect the colors associated with other logical palettes for the same device. These color changes are especially dramatic when they occur on the display. An application can make sure that reasonable colors are used for its currently selected logical palette by resetting the palette before each use. An application resets the palette by calling the UnrealizeObject_win32_UnrealizeObject and RealizePalette_win32_RealizePalette functions. Using these functions causes Windows to remap the colors in the logical palette to reasonable colors in the system palette.
+$#A System Palette and Static Colors
Ordinarily, the system palette entries that Windows reserves for static colors cannot be changed. An application can override this default behavior by using the SetSystemPaletteUse_win32_SetSystemPaletteUse function to reduce the number of static color entries and, thereby, increase the number of unused system palette entries. However, because changing the static colors can have an immediate and dramatic effect on all windows on the display, an application should not call SetSystemPaletteUse, unless it has a maximized window and the input focus.
When an application calls SetSystemPaletteUse with the SYSPAL_NOSTATIC value, Windows fr ees all but two of the reserved entries, allowing those entries to receive new color values when the application subsequently realizes its logical palette. The remaining two static color entries remain reserved and are set to white and black. An application can restore the reserved entries by calling SetSystemPaletteUse with the SYSPAL_STATIC value. It can discover the current system palette usage by using the GetSystemPaletteUse_win32_GetSystemPaletteUse function.
Furthermore, after setting the system palette usage to SYSPAL_NOSTATIC, the application must immediately realize its logical palette, call the GetSysColor_win32_GetSysColor function to save the current system color settings, call the SetSysColors_win32_SetSysColors function to set the system colors to reasonable values using black and white, and finally send the WM_SYSCOLORCHANGE_win32_WM_SYSCOLORCHANGE message to other top-level windows to allow them to be redrawn with the new system colors. When setting system colors using black and white, the application should make sure adjacent or overlapping items, such as window frames and borders, are set to black and white, respectively.
Before the application loses the input focus, closes its window, or terminates, it must immediately call SetSystemPaletteUse with the SYSPAL_STATIC value, realize its logical palette, restore the system colors to their previous values, and send the WM_SYSCOLORCHANGE message. Windows sends a WM_PAINT_win32_WM_PAINT message to any window that is affected by a system color change. Applications that have brushes using the existing system colors should delete those brushes and recreate them using the new system colors.
+$#A Palette Messages
Changes to the system palette for the display device can have dramatic and sometimes undesirable effects on the colors used in windows on the desktop. To minimize the impact of these changes, Windows provides a set of messages that help applications manage their logical palettes while ensuring that colors in the active window are as close as possible to the colors intended.
Windows sends a WM_QUERYNEWPALETTE_win32_WM_QUERYNEWPALETTE message to a top-level or overlapped window just before activating the window. This message gives an application the opportunity to select and realize its logical palette so that it receives the best possible mapping of colors for its logical palette. When the application receives the message, it should use the SelectPalette_win32_SelectPalette, UnrealizeObject_win32_UnrealizeObject, and RealizePalette_win32_RealizePalette functions to select and realize the logical palette. Doing so directs Windows to update colors in the system palette so that its colors match as many colors in the logical palette as possible.
When an application causes changes to the system palette as a result of realizing its logical palette, Windows sends a WM_PALETTECHANGED_win32_WM_PALETTECHANGED message to all top-level and overlapped windows. This message gives applications the opportunity to update the colors in the client areas of their windows, replacing colors that have changed with colors that more closely match the intended colors. An application that receives the WM_PALETTECHANGED message should use UnrealizeObject and RealizePalette to reset the logical palettes associated with all inactive windows and then update the colors in the client area for each inactive window by using the UpdateColors_win32_UpdateColors function. This technique does not guarantee the greatest number of exact color matches; however, it does ensure that colors in the logical palette are mapped to reasonable colors in the system palette.

Note To avoid creating an infinite loop, an application should never realize the palette for the window whose handle matches the handle passed in the wParam parameter of the WM_PALETTECHANGED message.

The UpdateColors_win32_UpdateColors function typically updates a client area of an inactive window faster than redrawing the area. However, because UpdateColors performs color translation based on the color of each pixel before the system palette changed, each call to this function results in the loss of some color accuracy. This means UpdateColors cannot be used to update colors when the window becomes active. In such cases, the application should redraw the client area.
Windows may send the WM_QUERYNEWPALETTE_win32_WM_QUERYNEWPALETTE message when changes to the logical palette are made. Also, Windows may send the WM_PALETTEISCHANGING_win32_WM_PALETTEISCHANGING message to all top-level and overlapped windows when the system palette is about to change.
+$#A Halftone Palette and Color Adjustment
Halftone palettes are intended to be used whenever the stretching mode of a device context is set to HALFTONE. An application creates a halftone palette by using the CreateHalftonePalette_win32_CreateHalftonePalette function. The application must select and realize this palette into the device context before calling the StretchBlt_win32_StretchBlt or StretchDIBits_win32_StretchDIBits function.
Windows automatically adjusts the input color of source bitmaps whenever applications call the StretchBlt and StretchDIBits functions and the stretching mode of a device context is set to HALFTONE. These color adjustments affect certain attributes of the image, such as contrast and brightness. An application can set the color adjustment values by using the SetColorAdjustment_win32_SetColorAdjustment function. The application can retrieve the color adjustment values for the specified device context by using the GetColorAdjustment_win32_GetColorAdjustment function.
+$#A Using Color
· Enumerating colors_win32_Enumerating_Colors
· Creating colored pens and brushes_win32_Creating_Colored_Pens_and_Brushes

+$#A Enumerating Colors
You can determine how many colors a device supports and what those colors are by retrieving the count of colors for the device and enumerating the colors of the solid pens. To retrieve the number of colors, use the GetDeviceCaps_win32_GetDeviceCaps function with the NUMCOLORS value. To enumerate solid pens, use the EnumObjects_win32_EnumObjects function and a corresponding callback function that receives information about each pen.
// GetTheColors - returns the count and color values of solid colors// Returns the address of array containing colors// hdc - handle of device contextCOLORREF *GetTheColors(HDC hdc){ int cColors; COLORREF *aColors; // Get the number of colors. cColors = GetDeviceCaps(hdcCurrent, NUMCOLORS); // Allocate space for the array. aColors = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) * (cColors+1)); // Save the count of colors in first element. aColors[0] = (LONG)cColors; // Enumerate all pens and save solid colors in the array. EnumObjects(hdc, OBJ_PEN, (GOBJENUMPROC)MyEnumProc, (LONG)aColors); // Refresh the count of colors. aColors[0] = (LONG)cColors; return aColors;}int MyEnumProc(LPVOID lp, LPBYTE lpb){ LPLOGPEN lopn; COLORREF *aColors; int iColor; lopn = (LPLOGPEN)lp; aColors = (COLORREF *)lpb; if (lopn->lopnStyle==PS_SOLID) { // Check for too many colors. if ((iColor = (int)aColors[0]) <= 0) return 0; aColors[iColor] = lopn->lopnColor; aColors[0]--; } return 1;}
+$#A Creating Colored Pens and Brushes
Although you can specify any color for a pen when creating it, Windows uses only colors that are available on the device. This means Windows uses the closest matching color when it realizes the pen for drawing. When creating brushes, Windows generates a dithered color if you specify a color that the device does not support. In either case, you can use the RGB_win32_RGB macro to specify a color when creating a pen or brush.
// DrawARectangle - draws a red rectangle with a green border// No return value.// hdc - handle of the device contextvoid DrawARectangle(HDC hdc)
{ HPEN hpen, hpenOld; HBRUSH hbrush, hbrushOld; // Create a green pen. hpen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0)); // Create a red brush. hbrush = CreateSolidBrush(RGB(255, 0, 0)); // Select the new pen and brush, and then draw. hpenOld = SelectObject(hdc, hpen); hbrushOld = SelectObject(hdc, hbrush); Rectangle(hdc, 100,100, 200,200); // Do not forget to clean up. SelectObject(hdc, hpenOld); DeleteObject(hpen); SelectObject(hdc, hbrushOld); DeleteObject(hbrush);}
+$#AK Color Reference
The following functions and messages are associated with color.
+$#A Color Functions
Following are the functions used with color.
AnimatePalette_win32_AnimatePaletteCreateHalftonePalette_win32_CreateHalftonePaletteCreatePalette_win32_CreatePaletteGetColorAdjustment_win32_GetColorAdjustmentGetNearestColor_win32_GetNearestColorGetNearestPaletteIndex_win32_GetNearestPaletteIndexGetPaletteEntries_win32_GetPaletteEntriesGetSystemPaletteEntries_win32_GetSystemPaletteEntriesGetSystemPaletteUse_win32_GetSystemPaletteUseRealizePalette_win32_RealizePaletteResizePalette_win32_ResizePaletteSelectPalette_win32_SelectPaletteSetColorAdjustment_win32_SetColorAdjustmentSetPaletteEntries_win32_SetPaletteEntriesSetSystemPaletteUse_win32_SetSystemPaletteUseUnrealizeObject_win32_UnrealizeObjectUpdateColors_win32_UpdateColors
+$#A Color Messages
Following are the messages used with color.
WM_PALETTECHANGED_win32_WM_PALETTECHANGEDWM_PALETTEISCHANGING_win32_WM_PALETTEISCHANGINGWM_QUERYNEWPALETTE_win32_WM_QUERYNEWPALETTEWM_SYSCOLORCHANGE_win32_WM_SYSCOLORCHANGE

+$#AK Coordinate Spaces and Transformations
Applications written for the Microsoft® Windows® operating system use coordinate spaces and transformations to scale, rotate, translate, shear, and reflect graphics output. A coordinate space is a planar space based on the Cartesian coordinate system. This system requires two axes that are perpendicular and equal in length. There are four coordinate spaces in Windows: world, page, device, and physical device (client area, or desktop, or page of printer paper). A transformation is an algorithm that alters ("transforms") the size, orientation, and shape of objects.
+$#A About Coordinate Spaces and Transformations
Coordinate spaces and transformations are used, for example, by desktop publishing applications to "zoom" parts of a page or to display adjacent pages in a window; computer-aided design (CAD) applications use them to rotate objects, scale drawings, or to create perspective views; and spreadsheet applications use them to move and size graphs.
The following illustrations show successive views of an object created in a drawing application. The first illustration shows the object as it appears in the original drawing; the five remaining illustrations show the effects of applying various transformations.
{ewc msdncd, EWGraphic, group10655 0 /a "SDK_01.bmp"}
Original View
{ewc msdncd, EWGraphic, group10655 1 /a "SDK_02.bmp"}
Scaled View
{ewc msdncd, EWGraphic, group10655 2 /a "SDK_03.bmp"}
Translated View
{ewc msdncd, EWGraphic, group10655 3 /a "SDK_04.bmp"}
Rotated View
{ewc msdncd, EWGraphic, group10655 4 /a "SDK_05.bmp"}
Sheared View
{ewc msdncd, EWGraphic, group10655 5 /a "SDK_06.bmp"}
Reflection
+$#A Transformation of Coordinate Spaces
A coordinate space is a planar space based on the Cartesian coordinate system. This system provides a means of specifying the location of each point on a plane. It requires two axes that are perpendicular and equal in length. The following illustration shows a coordinate space.
{ewc msdncd, EWGraphic, group10655 6 /a "SDK_07.bmp"}
The Microsoft® ( ) Win32® application programming interface (API) uses four coordinate spaces: world, page, device, and physical device. Applications use world space to rotate, shear, or reflect graphics output. World space measures 2^32 units high by 2^32 units wide. Page space (referred to as logical space in earlier versions of Windows) also measures 2^32 units high by 2^32 units wide. Device space measures 2^27 units high by 2^27 units wide. Page space works with device space to provide applications with device-independent units, such as millimeters and inches. The Win32 API refers to both world space and page space as logical space. The final coordinate space, physical device, usually refers to the client area of the application's window; however, it can also include the entire desktop, a complete window (including the frame, title bar, and menu bar), or a page of printer or plotter paper. Physical device dimensions vary according to the dimensions set by the display, printer, or plotter technology.
To depict output on a physical device, Windows copies (or maps) a rectangular region from one coordinate space into the next coordinate space until finally the output appears in its entirety on the physical device. Mapping begins in the application's world space if the application has called the SetWorldTransform_win32_SetWorldTransform function; otherwise, mapping occurs in page space. As Windows copies each point within the rectangular region from one space into another, it applies an algorithm called a transformation. A transformation alters (or transforms) the size, orientation, and shape of objects that are copied from one coordinate space into another. Although a transformation affects an object as a whole, it is applied to each point, or to each line, in the object.
The following illustration shows a typical transformation performed by using the SetWorldTransform function.
{ewc msdncd, EWGraphic, group10655 7 /a "SDK_08.bmp"}
+$#A World-Space to Page-Space Transformations
World-space to page-space transformations are new to Windows. They support translation and scaling that were available in previous versions of Windows. In addition, they support rotation, shear, and reflection capabilities. The following sections describe these transformations, illustrate their effects, and provide the algorithms used to achieve them.
+$#A Translation
Some applications translate (or shift) objects drawn in the client area. If your application features this capability, use the SetWorldTransform_win32_SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM_win32_XFORM_str structure containing the appropriate values. The eDx and eDy members of XFORM specify the horizontal and vertical translation components, respectively.
When translation occurs, each point in an object is shifted vertically, horizontally, or both, by a specified amount. The following illustration shows a 20- by 20-unit rectangle that was translated to the right by 10 units when copied from world coordinate space to page coordinate space.
{ewc msdncd, EWGraphic, group10655 8 /a "SDK_09.bmp"}
In the preceding illustration, the x-coordinate of each point in the rectangle is 10 units greater than the original x-coordinate.
Horizontal translation can be represented by the following algorithm.
x' = x + Dx

Where x' is the new x-coordinate, x is the original x-coordinate, and Dx is the horizontal distance moved.
Vertical translation can be represented by the following algorithm.
y' = y + Dy

Where y' is the new y-coordinate, y is the original y-coordinate, and Dy is the vertical distance moved.
The horizontal and vertical translation transformations can be combined into a single operation by using a 3-by-3 matrix.
|1 0 0|
|x' y' 1| = |x y 1| * |0 1 0|
|Dx Dy 1|

(The rules of matrix multiplication state that the number of rows in one matrix must equal the number of columns in the other. The integer 1 in the matrix |x y 1| is a placeholder that was added to meet this requirement.)
The 3-by-3 matrix that produced the illustrated translation transformation contains the following values.
|1 0 0|
|0 1 0|
|10 0 1|

+$#A Scaling
Most CAD and drawing applications provide features that scale output created by the user. If your application features scaling (or zoom) capabilities, use the SetWorldTransform_win32_SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM_win32_XFORM_str structure containing the appropriate values. The eM11 and eM22 members of XFORM specify the horizontal and vertical scaling components, respectively.
When scaling occurs, the vertical and horizontal lines (or vectors), that constitute an object, are stretched or compressed with respect to the x- or y-axis. The following illustration shows a 20- by 20-unit rectangle scaled vertically to twice its original height when copied from world coordinate space to page coordinate space.
{ewc msdncd, EWGraphic, group10655 9 /a "SDK_10.bmp"}
In the preceding illustration, the vertical lines that define the original rectangle's side measure 20 units, while the vertical lines that define the scaled rectangle's sides measure 40 units.
Vertical scaling can be represented by the following algorithm.
y' = y * Dy

Where y' is the new length, y is the original length, and Dy is the vertical scaling factor.
Horizontal scaling can be represented by the following algorithm.
x' = x * Dx

Where x' is the new length, x is the original length, and Dx is the horizontal scaling factor.
The vertical and horizontal scaling transformations can be combined into a single operation by using a 2-by-2 matrix.
|x' y'| = |Dx 0| * |x y|
|0 Dy|

The 2-by-2 matrix that produced the scaling transformation contains the following values.
|1 0|
|0 2|

+$#A Rotation
Many CAD applications provide features that rotate objects drawn in the client area. If your application features rotation capabilities, use the SetWorldTransform_win32_SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM_win32_XFORM_str structure containing the appropriate values. The eM11, eM12, eM21, and eM22 members of XFORM specify, respectively, the cosine, sine, negative sine, and cosine of the angle of rotation.
When rotation occurs, the points that constitute an object are rotated with respect to the coordinate-space origin. The following illustration shows a 20- by 20-unit rectangle rotated 30 degrees when copied from world coordinate space to page coordinate space.
{ewc msdncd, EWGraphic, group10655 10 /a "SDK_11.bmp"}
In the preceding illustration, each point in the rectangle was rotated 30 degrees with respect to the coordinate-space origin.
The following algorithm computes the new x-coordinate (x') for a point (x,y) that is rotated by angle A with respect to the coordinate-space origin.
x' = (x * cos A) - (y * sin A)

The following algorithm computes the y-coordinate (y') for a point (x,y) that is rotated by the angle A with respect to the origin.
y' = (x * sin A) + (y * cos A)

The two rotation transformations can be combined in a 2-by-2 matrix as follows.
|x' y'| == |x y| * | cos A sin A|
|-sin A cos A|

The 2-by-2 matrix that produced the rotation contains the following values.
| .8660 .5000|
|-.5000 .8660|

Rotation Algorithm Derivation
Rotation algorithms are based on trigonometry's addition theorem stating that the trigonometric function of a sum of two angles (A1 and A2) can be expressed in terms of the trigonometric functions of the two angles.
sin(A1 + A2) = (sin A1 * cos A2) + (cos A1 * sin A2)
cos(A1 + A2) = (cos A1 * cos A2) - (sin A1 * sin A2)

The following illustration shows a point p rotated counterclockwise to a new position p'. In addition, it shows two triangles formed by a line drawn from the coordinate-space origin to each point and a line drawn from each point through the x-axis.
{ewc msdncd, EWGraphic, group10655 11 /a "SDK_12.bmp"}
Using trigonometry, the x-coordinate of point p can be obtained by multiplying the length of the hypotenuse h by the cosine of A1.
x = h * cos A1

The y-coordinate of point p can be obtained by multiplying the length of the hypotenuse h by the sine of A1.
y = h * sin A1

Likewise, the x-coordinate of point p' can be obtained by multiplying the length of the hypotenuse h by the cosine of (A1 + A2).
x' = h * cos (A1 + A2)

Finally, the y-coordinate of point p' can be obtained by multiplying the length of the hypotenuse h by the sine of (A1 + A2).
y' = h * sin (A1 + A2)

Using the addition theorem, the preceding algorithms become the following.
x' = (h * cos A1 * cos A2) - (h * sin A1 * sin A2)
y' = (h * cos A1 * sin A2) + (h * sin A1 * cos A2)

The rotation algorithms for a given point rotated by angle A2 can be obtained by substituting x for each occurrence of (h * cos A1) and substituting y for each occurrence of (h * sin A1).
x' = (x * cos A2) - (y * sin A2)
y' = (x * sin A2) + (y * cos A2)

+$#A Shear
Some applications provide features that shear objects drawn in the client area. If your application features shear capabilities, use the SetWorldTransform_win32_SetWorldTransform function to set appropriate values in the world-space to page-space transformation. This function receives a pointer to an XFORM_win32_XFORM_str structure containing the appropriate values. The eM12 and eM21 members of XFORM specify the horizontal and vertical proportionality constants, respectively.
There are two components of the shear transformation. The first alters the vertical lines in an object; the second alters the horizontal lines. The following illustration shows a 20- by 20-unit rectangle sheared horizontally when copied from world space to page space.
{ewc msdncd, EWGraphic, group10655 12 /a "SDK_13.bmp"}
A horizontal shear can be represented by the following algorithm:
x' = x + (Sx * y)

where x is the original x-coordinate, Sx is the proportionality constant, and x' is the result of the shear transformation.
A vertical shear can be represented by the following algorithm:
y' = y + (Sy * x)

where y is the original y-coordinate, Sy is the proportionality constant, and y' is the result of the shear transformation.
The horizontal-shear and vertical-shear transformations can be combined into a single operation using a 2-by-2 matrix.
|x' y'| == |x y| * | 1 Sx|
| Sy 1|

The 2-by-2 matrix that produced the shear contains the following values.
|1 1|
|0 1|

+$#A Reflection
Some applications provide features that reflect (or mirror) objects drawn in the client area. If your application features reflection capabilities, use the SetWorldTransform_win32_SetWorldTransform function to set the appropriate values in the world-space to page-space transformation. This function receives a pointer to an XFORM_win32_XFORM_str structure containing the appropriate values. The eM11 and eM22 members of XFORM specify the horizontal and vertical reflection components, respectively.
The reflection transformation creates a "mirror" image of an object with respect to either the x- or y-axis. In short, reflection is just negative scaling. To produce a horizontal reflection, x-coordinates are multiplied by -1. To produce a vertical reflection, y-coordinates are multiplied by - 1.
Horizontal reflection can be represented by the following algorithm:
x' = -x

where x is the x-coordinate and x' is the result of the reflection.

The 2-by-2 matrix that produced horizontal reflection contains the following values:
|-1 0|
|0 1|

Vertical reflection can be represented by the following algorithm:
y' = -y

where y is the y-coordinate and y' is the result of the reflection.
The 2-by-2 matrix that produced vertical reflection contains the following values:
|1 0|
|0 -1|

The horizontal-reflection and vertical-reflection operations can be combined into a single operation by using the following 2-by-2 matrix:
|-1 0|
|0 -1|

+$#A Combined World-to-Page Space Transformations
You can combine the five world-to-page transformations into a single 3-by-3 matrix. The CombineTransform_win32_CombineTransform function can be used to combine two world-space to page-space transformations. You can use the combined transformations to alter output associated with a particular device context (DC) by calling the SetWorldTransform_win32_SetWorldTransform function and supplying the elements for this matrix. When an application calls SetWorldTransform, it stores the elements of the 3-by-3 matrix in an XFORM_win32_XFORM_str structure. The members of this structure correspond to the first two columns of a 3-by-3 matrix; the last column of the matrix is not required because its values are constant.
You can retrieve the elements of the current world transformation matrix by calling the GetWorldTransform_win32_GetWorldTransform function and supplying a pointer to an XFORM structure.
+$#A Page-Space to Device-Space Transformations
The page-space to device-space transformation was part of the original Windows interface. This transformation determines the mapping mode for all graphics output associated with a particular DC. A mapping mode is a scaling transformation that specifies the size of the units used for drawing operations. The mapping mode may also perform translation. In some cases, the mapping mode alters the orientation of the x- and y-axes in device space. The mapping modes are described in the following table.
Mapping mode Description
MM_ANISOTROPIC Each unit in page space is mapped to an application-specified unit in device space. The axis may or may not be equally scaled (for example, a circle drawn in world space may appear to be an ellipse when depicted on a given device). The orientation of the axis is also specified by the application.
MM_HIENGLISH Each unit in page space is mapped to 0.001 inch in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_HIMETRIC Each unit in page space is mapped to 0.01 millimeter in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_ISOTROPIC Each unit in page space is mapped to an application-defined unit in device space. The axes are always equally scaled. The orientation of the axes may be specified by the application.
MM_LOENGLISH Each unit in page space is mapped to 0.01 inch in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_LOMETRIC Each unit in page space is mapped to 0.1 millimeter in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_TEXT Each unit in page space is mapped to one pixel; that is, no scaling is performed at all. When no translation is in effect (this is the default), page space in the MM_TEXT mapping mode is equivalent to physical device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move down.
MM_TWIPS Each unit in page space is mapped to one twentieth of a printer's point (1/1440 inch). Increasing values of x occur as you move to the right; increasing values of y occur as you move up.

You set a mapping mode by calling the SetMapMode_win32_SetMapMode function. You retrieve the current mapping mode for a DC by calling the GetMapMode_win32_GetMapMode function.
The page-space to device-space transformations consist of values calculated from the points given by the window and viewport. The window and viewport each consist of a pair of points, with one point specifying an origin and the other a width and height (called the extents). The window points are in logical coordinates; the viewport in device coordinates (pixels). Windows combines the origins and extents from both the window and viewport to create the transformation. This means that the window and viewport each specify half of the factors needed to define the transformation used to map points in page space to device space. The effect of a transformation calculated in this way is that Windows maps the window origin to the viewport origin and the window extents to the viewport extents, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10655 13 /a "SDK_15.bmp"}
The window and viewport extents establish a ratio or scaling factor used in the page-space to device-space transformations. For the six predefined mapping modes (MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, MM_TEXT, and MM_TWIPS), the extents are set by Windows when you call SetMapMode. They cannot be changed. The other two mapping modes (MM_ISOTROPIC and MM_ANISOTROPIC) require that you specify the extents. This is done by calling SetMapMode to set the appropriate mode and then calling the SetWindowExtEx_win32_SetWindowExtEx and SetViewportExtEx_win32_SetViewportExtEx functions to specify the extents. In the MM_ISOTROPIC mapping mode, it is important to call SetWindowExtEx before calling SetViewportExtEx.
The window and viewport origins establish the translation used in the page-space to device-space transformations. You set the window and viewport origins by using the SetWindowOrgEx_win32_SetWindowOrgEx and SetViewportOrgEx_win32_SetViewportOrgEx functions. The origins are independent of the extents, and an application can set them regardless of the current mapping mode. Changing a mapping mode does not affect the currently set origins (although it can affect the extents). Origins are specified in absolute units that the current mapping mode does not affect. You can also alter the origins by using the OffsetWindowOrgEx_win32_OffsetWindowOrgEx and OffsetViewportOrgEx_win32_OffsetViewportOrgEx functions.
The following formula shows the math involved in converting a point from page space to device space.
Dx = ((Lx - WOx) * VEx / WEx) + VOx

The following variables are involved.
Dx x value in device units
Lx x value in logical units (also known as page space units)
WOx window x origin
VOx viewport x origin
WEx window x-extent
VEx viewport x-extent

The same equation with y replacing x transforms the y component of a point.
The formula first offsets the point from its coordinate origin. This value, no longer biased by the origin, is then scaled into the destination coordinate system by the ratio of the extents. Finally, the scaled value is offset by the destination origin to its final mapping.
The LPtoDP_win32_LPtoDP and DPtoLP_win32_DPtoLP functions may be used to convert from logical points to device points and from device points to logical points, respectively.
+$#A Predefined Mapping Modes
Of the six predefined mapping modes, one is device dependent (MM_TEXT) while the remaining five (MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS) are device independent.
The default mapping mode is MM_TEXT. One logical unit equals one pixel. Positive x is to the right, and positive y is down. This mode maps directly to the device's coordinate system. The logical-to-physical mapping involves only an offset in x and y that is defined by the application-controlled window and viewport origins. The viewport and window extents are all set to 1, creating a one-to-one mapping.
If your application displays geometric shapes (circles, squares, polygons, and so on), you may want to choose one of the device-independent mapping modes. For example, if you are writing code to provide charting capabilities for a spreadsheet application and want to guarantee that the diameter of each pie chart is 2 inches, you can choose the MM_LOENGLISH mapping mode and call the appropriate functions to draw and fill the chart. By specifying MM_LOENGLISH, you guarantee that the diameter of the chart is consistent on any display or printer. If you were to choose MM_TEXT instead of MM_LOENGLISH, a chart that appears circular on a VGA display would appear elliptical on an EGA display and would appear very small on a 300 dpi (dots per inch) laser printer.
+$#A Application-Defined Mapping Modes
The two application-defined mapping modes (MM_ISOTROPIC and MM_ANISOTROPIC) are provided for application-specific mapping modes. The MM_ISOTROPIC mode guarantees that logical units in the x-direction and in the y-direction are equal, while the MM_ANISOTROPIC mode allows the units to differ. A CAD or drawing application can benefit from the MM_ISOTROPIC mapping mode but may need to specify logical units that correspond to the increments on an engineer's scale (1/64 inch). These units would be difficult to obtain with the predefined mapping modes (MM_HIENGLISH or MM_HIMETRIC); however, they can easily be obtained by selecting the MM_ISOTROPIC (or MM_ANISOTROPIC) mode. The following example shows how to set logical units to 1/64 inch:
SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC, 64, 64, NULL);
SetViewportExtEx(hDC, GetDeviceCaps(hDC, LOGPIXELSX),
GetDeviceCaps(hDC, LOGPIXELSY), NULL);
+$#A Device-Space to Physical-Device Transformation
The device-space to physical-device transformation is unique in several respects. For example, it is limited to translation and is controlled by the window manager component of Windows. The sole purpose of this transformation is to ensure that the origin of device space is mapped to the proper point on the physical device. There are no functions to set this transformation, nor are there any functions to retrieve related data.
+$#A Default Transformations
Whenever an application creates a DC and immediately begins calling GDI drawing or output functions, it takes advantage of the default page-space to device-space, and device-space to client-area transformations. A world-to-page space transformation cannot happen until the application first calls the SetGraphicsMode_win32_SetGraphicsMode function to set the mode to GM_ADVANCED and then calls the SetWorldTransform_win32_SetWorldTransform function.
Use of MM_TEXT (the default page-space to device-space transformation) results in a one-to-one mapping; that is, a given point in page space maps to the same point in device space. As previously mentioned, this transformation is not specified by a matrix. Instead, it is obtained by dividing the width of the viewport by the width of the window and the height of the viewport by the height of the window. In the default case, the viewport dimensions are 1-pixel by 1-pixel and the window dimensions are 1-page unit by 1-page unit.
The device-space to physical-device (client area, desktop, or printer paper) transformation always results in a one-to-one mapping; that is, one unit in device space is always equivalent to one unit in the client area, on the desktop, or on a page. The sole purpose of this transformation is translation; it ensures that output appears correctly in an application's window no matter where that window is moved on the desktop.
The one unique aspect of MM_TEXT is the orientation of the y-axis in page space. In MM_TEXT, the positive y-axis extends downward and the negative y-axis extends upward.
+$#A Using Coordinate Spaces and Transformations
This section contains an example that demonstrates the following tasks:
· Drawing graphics with predefined units.
· Centering graphics in the application's client area.
· Scaling graphics output to half its original size.
· Translating graphics output 3/4 of an inch to the right.
· Rotating graphics 30 degrees.
· Shearing graphics output along the x-axis.
· Reflecting graphics output about an imaginary horizontal axis drawn through its midpoint.
The following example was used to create the illustrations that appear earlier in this topic.
void TransformAndDraw(int iTransform, HWND hWnd)
{

HDC hDC;
XFORM xForm;
RECT rect;

/* Retrieve a DC handle for the application's window. */

hDC = GetDC(hWnd);

/*
* Set the mapping mode to LOENGLISH. This moves the
* client area origin from the upper left corner of the
* window to the lower left corner (this also reorients
* the y-axis so that drawing operations occur in a true
* Cartesian space). It guarantees portability so that
* the object drawn retains its dimensions on any
* display running Windows.
*/

SetMapMode(hDC, MM_LOENGLISH);

/*
* Set the appropriate world transformation (based on the
* user's menu selection).
*/

switch (iTransform) {
case SCALE: /* Scale to 1/2 of the original size. */
xForm.eM11 = (FLOAT) 0.5;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 0.5;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

case TRANSLATE: /* Translate right by 3/4 inch. */
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 75.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

case ROTATE: /* Rotate 30 degrees counterclockwise. */
xForm.eM11 = (FLOAT) 0.8660;
xForm.eM12 = (FLOAT) 0.5000;
xForm.eM21 = (FLOAT) -0.5000;
xForm.eM22 = (FLOAT) 0.8660;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

case SHEAR: /* Shear along the x-axis with a */
/* proportionality constant of 1.0. */
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 1.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

case REFLECT: /* Reflect about a horizontal axis. */
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) -1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

case NORMAL: /* Set the unity transformation. */
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;

}

/* Find the midpoint of the client area. */

GetClientRect(hWnd, (LPRECT) &rect);
DPtoLP(hDC, (LPPOINT) &rect, 2);

/* Select a hollow brush. */

SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));

/* Draw the exterior circle. */

Ellipse(hDC, (rect.right / 2 - 100), (rect.bottom / 2 + 100),
(rect.right / 2 + 100), (rect.bottom / 2 - 100));

/* Draw the interior circle. */

Ellipse(hDC, (rect.right / 2 -94), (rect.bottom / 2 + 94),
(rect.right / 2 + 94), (rect.bottom / 2 - 94));

/* Draw the key. */

Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 113),
(rect.right / 2 + 13), (rect.bottom / 2 + 50));
Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 96),
(rect.right / 2 + 13), (rect.bottom / 2 + 50));

/* Draw the horizontal lines. */

MoveToEx(hDC, (rect.right / 2 - 150), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 - 16), (rect.bottom / 2 + 0));

MoveToEx(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 + 13), (rect.bottom / 2 + 0));

MoveToEx(hDC, (rect.right / 2 + 16), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 + 150), (rect.bottom / 2 + 0));

/* Draw the vertical lines. */

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 150), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 16));

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 13), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 13));

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 16), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 150));

ReleaseDC(hWnd, hDC);

}

+$#A Coordinate Space and Transformation Functions
Following are the functions used with coordinate spaces and transformations.
ClientToScreen_win32_ClientToScreenCombineTransform_win32_CombineTransformDPtoLP_win32_DPtoLPGetCurrentPositionEx_win32_GetCurrentPositionExGetGraphicsMode_win32_GetGraphicsModeGetMapMode_win32_GetMapModeGetViewportExtEx_win32_GetViewportExtExGetViewportOrgEx_win32_GetViewportOrgExGetWindowExtEx_win32_GetWindowExtExGetWindowOrgEx_win32_GetWindowOrgExGetWorldTransform_win32_GetWorldTransformLPtoDP_win32_LPtoDPMapWindowPoints_win32_MapWindowPointsModifyWorldTransform_win32_ModifyWorldTransformOffsetViewportOrgEx_win32_OffsetViewportOrgExOffsetWindowOrgEx_win32_OffsetWindowOrgExScaleViewportExtEx_win32_ScaleViewportExtExScaleWindowExtEx_win32_ScaleWindowExtExScreenToClient_win32_ScreenToClientSetGraphicsMode_win32_SetGraphicsModeSetMapMode_win32_SetMapModeSetViewportExtEx_win32_SetViewportExtExSetViewportOrgEx_win32_SetViewportOrgExSetWindowExtEx_win32_SetWindowExtExSetWindowOrgEx_win32_SetWindowOrgExSetWorldTransform_win32_SetWorldTransform

+$#AK Device Contexts
A device context is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output. The graphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations.
+$#A About Device Contexts
One of the chief features of the Microsoft® Win32® application programming interface (API) is device independence. Win32-based applications can draw and print output on a variety of devices. The software that supports this device independence is contained in two dynamic-link libraries. The first, GDI.DLL, is referred to as the graphics device interface (GDI); the second is referred to as a device driver. The name of the second depends on the device where the application draws output. For example, if the application draws output in the client area of its window on a VGA display, this library is VGA.DLL; if the application prints output on an Epson® FX-80 printer, this library is EPSON9.DLL.
An application must inform GDI to load a particular device driver and, once the driver is loaded, to prepare the device for drawing operations (such as selecting a line color and width, a brush pattern and color, a font typeface, a clipping region, and so on). These tasks are accomplished by creating and maintaining a device context. A device context is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output. The graphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations. Unlike most of the Win32 structures, an application never has direct access to the device context; instead, it operates on the structure indirectly by calling various functions.
+$#A Graphic Objects
The pen, brush, bitmap, palette, region, and path associated with a device context are referred to as the device context's graphic objects. Following are the attributes associated with each of these objects.
Graphic object Associated attributes
Bitmap Size, in bytes; dimensions, in pixels; color-format; compression scheme; and so on
Brush Style, color, pattern, and origin
Palette Colors and size (or number of colors)
Font Typeface name, width, height, weight, character set, and so on
Path Shape
Pen Style, width, and color
Region Location and dimensions

When an application creates a device context, Windows automatically stores a set of default objects in it. (There is no default bitmap or path.) An application can examine the attributes of the default objects by calling the GetCurrentObject_win32_GetCurrentObject and GetObject_win32_GetObject functions. The application can change these defaults by creating a new object and selecting it into the device context. An object is selected into a device context by calling the SelectObject_win32_SelectObject function.
+$#A Graphic Modes
Windows supports five graphic modes that allow an application to specify how colors are mixed, where output appears, how the output is scaled, and so on. These modes, which are stored in a device context, are described in the following table.
Mode Description
Background mode Defines how background colors are mixed with existing window or screen colors for bitmap and text operations.
Drawing mode Defines how foreground colors are mixed with existing window or screen colors for pen, brush, bitmap, and text operations.
Mapping mode Defines how graphics output is mapped from logical (or world) space onto the window, screen, or printer paper.
Polygon-fill mode Defines how the brush pattern is used to fill the interior of complex regions.
Stretching mode Defines how bitmap colors are mixed with existing window or screen colors when the bitmap is compressed (or scaled down).

As it does with graphic objects, Windows initializes a device context with default graphic modes. An application can retrieve and examine these default modes by calling the following functions.
Graphic mode Function
Background mode GetBkMode_win32_GetBkMode
Drawing mode GetROP2_win32_GetROP2
Mapping mode GetMapMode_win32_GetMapMode
Polygon-fill mode GetPolyFillMode_win32_GetPolyFillMode
Stretching mode GetStretchBltMode_win32_GetStretchBltMode

An application can change the default modes by calling one of the following functions.
Graphic mode Function
Background mode SetBkMode_win32_SetBkMode
Drawing mode SetROP2_win32_SetROP2
Mapping mode SetMapMode_win32_SetMapMode
Polygon-fill mode SetPolyFillMode_win32_SetPolyFillMode
Stretching mode SetStretchBltMode_win32_SetStretchBltMode

+$#A Device Context Types
The Win32 API provides four types of device contexts: display, printer, memory (or compatible), and information. Each type serves a specific purpose, as described in the following table.
Device context Purpose
Display Supports drawing operations on a video display.
Printer Supports drawing operations on a printer or plotter.
Memory Supports drawing operations on a bitmap.
Information Supports the retrieval of device data.

+$#A Display Device Contexts
Windows provides three types of device contexts for video displays: class, common and private. Class and private device contexts are used in applications that perform numerous drawing operations such as computer-aided design (CAD) applications, desktop-publishing applications, drawing and painting applications, and so on. Common device contexts are used in applications that perform infrequent drawing operations.
An application obtains a display device context by calling either the BeginPaint_win32_BeginPaint or GetDC_win32_GetDC function and identifying the window in which the corresponding output will appear. (The type of device context Windows returns is dependent on how the application registered its window class.) Typically, an application obtains a display device context only when it must draw in the client area. When the application is finished drawing, it must release the device context by calling the EndPaint_win32_EndPaint or ReleaseDC_win32_ReleaseDC function.
Class Device Contexts
Class device contexts are supported strictly for compatibility with previous versions of Windows. When writing a Win32-based application, avoid using the class device context. Use a private device context instead.
Common Device Contexts
Common device contexts are display device contexts maintained in a special cache by the window-management component of the Win32 API. An application obtains a handle identifying one of the available common device contexts by calling the GetDC_win32_GetDC, GetDCEx_win32_GetDCEx, or BeginPaint_win32_BeginPaint function. Before returning this device context handle, Windows initializes a common device context with default objects, attributes, and modes. Any drawing operations performed by the application use these defaults unless one of the GDI functions is called to select a new object, change the attributes of an existing object, or select a new mode.
Because only a limited number of common device contexts exist in the window manager's heap, an application should release these device contexts after it has finished drawing. An application releases a common device context by calling the ReleaseDC_win32_ReleaseDC or EndPaint_win32_EndPaint function. When the application releases a common device context, any changes to the default data are lost.
Private Device Contexts
Private device contexts are display device contexts that, unlike common device contexts, retain any changes to the default data ¾ even after an application calls the ReleaseDC_win32_ReleaseDC or EndPaint_win32_EndPaint function. Private device contexts are not part of the window manager's cache and therefore need not be released after use. The window manager automatically removes a private device context after the last window of that class has been destroyed.
An application creates a private device context by first specifying the CS_OWNDC window-class style when it initializes the style member of the WNDCLASS_win32_WNDCLASS_str structure and calls the RegisterClass_win32_RegisterClass function. (For more information about window classes, see Window Classes_win32_Window_Classes.) After creating a window with the CS_OWNDC style, an application can call the GetDC_win32_GetDC, GetDCEx_win32_GetDCEx, or BeginPaint_win32_BeginPaint function once to obtain a handle identifying a private device context. The application can continue using this handle (and the associated device context) until it deletes the window created with this class. Any changes to graphic objects and their attributes, or graphic modes are retained by Windows until the window is deleted.
+$#A Printer Device Contexts
Windows provides a single type of printer device context that can be used when printing on a dot-matrix printer, ink-jet printer, laser printer, or plotter. An application creates a printer device context by calling the CreateDC_win32_CreateDC function and supplying the appropriate arguments (the name of the printer driver, the name of the printer, the file or device name for the physical output medium, and other initialization data). When an application has finished printing, it deletes the printer device context by calling the DeleteDC_win32_DeleteDC function. An application must delete (rather than release) a printer device context; the ReleaseDC_win32_ReleaseDC function fails when an application attempts to use it to release a printer device context.
+$#A Memory Device Contexts
Windows supports a memory device context that stores bitmapped images for a particular device. An application creates a memory device context by calling the CreateCompatibleDC_win32_CreateCompatibleDC function and supplying a handle that identifies a device context for a particular device. When Windows processes this call, it creates a bitmap having a color format compatible with the original device. Because the bitmap is compatible with the given device, a memory device context is also sometimes referred to as a compatible device context.
The original bitmap in a memory device context is simply a placeholder. Its dimensions are one pixel by one pixel. Before an application can begin drawing, it must select a bitmap with the appropriate width and height into the device context by calling the SelectObject_win32_SelectObject function. Once the new bitmap is selected into the memory device context, an application can begin using the device context to store images. For more information about bitmaps and bitmap operations, see Bitmaps_win32_Bitmaps.
+$#A Information Device Contexts
Windows supports an information device context used to retrieve default device data. For example, an application can call the CreateIC_win32_CreateIC function to create an information device context for a particular model of printer and then call the GetCurrentObject_win32_GetCurrentObject and GetObject_win32_GetObject functions to retrieve the default pen or brush attributes. Because Windows can retrieve device information without creating the structures normally associated with the other types of device contexts, an information device context involves far less overhead and is created significantly faster than any of the other types. After an application finishes retrieving data by using an information device context, it must call the DeleteDC_win32_DeleteDC function.
+$#A Device Context Operations
An application can perform the following operations on a device context:
· Enumerate existing graphic objects.
· Select new graphic objects.
· Delete existing graphic objects.
· Save the current graphic objects, their attributes, and the graphic modes.
· Restore previously saved graphic objects, their attributes, and the graphic modes.
In addition, an application can use a device context to determine how graphics output is translated, cancel lengthy drawing operations (begun by a thread in a multithreaded application), or reset a printer to a particular state.
+$#A Operations on Graphic Objects
Once an application creates a display or printer device context, it can begin drawing on the associated device or, in the case of the memory device context, it can begin drawing on the bitmap stored in memory. However, before drawing begins and sometimes while drawing is in progress, it is often necessary to replace the default objects with new objects.
An application can examine a default object's attributes by calling the GetCurrentObject_win32_GetCurrentObject and GetObject_win32_GetObject functions. The GetCurrentObject function returns a handle identifying the current pen, brush, palette, bitmap, or font, and the GetObject function initializes a structure containing that object's attributes.
Some printers provide resident pens, brushes, and fonts that can be used to improve drawing speed in an application. Two functions can be used to enumerate these objects: EnumObjects_win32_EnumObjects and EnumFontFamilies_win32_EnumFontFamilies. If the application must enumerate resident pens or brushes, it can call the EnumObjects function to examine the corresponding attributes. If the application must enumerate resident fonts, it can call the EnumFontFamilies function (which can also enumerate GDI fonts).
Once an application determines that a default object needs replacing, it creates a new object by calling one of the following creation functions.
Graphic object Function
Bitmap CreateBitmap_win32_CreateBitmap, CreateBitmapIndirect_win32_CreateBitmapIndirect, CreateCompatibleBitmap_win32_CreateCompatibleBitmap, CreateDiscardableBitmap_win32_CreateDiscardableBitmap, CreateDIBitmap_win32_CreateDIBitmap
Brush CreateBrushIndirect_win32_CreateBrushIndirect, CreateDIBPatternBrush_win32_CreateDIBPatternBrush, CreateDIBPatternBrushPt_win32_CreateDIBPatternBrushPt, CreateHatchBrush_win32_CreateHatchBrush, CreatePatternBrush_win32_CreatePatternBrush, CreateSolidBrush_win32_CreateSolidBrush
Color Palette CreatePalette_win32_CreatePalette
Font CreateFont_win32_CreateFont, CreateFontIndirect_win32_CreateFontIndirect
Pen CreatePen_win32_CreatePen, CreatePenIndirect_win32_CreatePenIndirect, ExtCreatePen_win32_ExtCreatePen
Region CreateEllipticRgn_win32_CreateEllipticRgn, CreateEllipticRgnIndirect_win32_CreateEllipticRgnIndirect, CreatePolygonRgn_win32_CreatePolygonRgn, CreatePolyPolygonRgn_win32_CreatePolyPolygonRgn, CreateRectRgn_win32_CreateRectRgn, CreateRectRgnIndirect_win32_CreateRectRgnIndirect, CreateRoundRectRgn_win32_CreateRoundRectRgn

Each of these functions returns a handle identifying a new object. After an application retrieves a handle, it must call the SelectObject_win32_SelectObject function to replace the default object. However, the application should save the handle identifying the default object and use this handle to replace the new object when it is no longer needed. When the application finishes drawing with the new object, it must restore the default object by calling the SelectObject function and delete the new object by calling the DeleteObject_win32_DeleteObject function. Failing to delete objects causes serious performance problems.
+$#A Cancellation of Drawing Operations
When complex drawing applications perform lengthy graphics operations, they consume valuable system resources. By taking advantage of Win32 multitasking features, an application can use threads and the CancelDC_win32_CancelDC function to manage these operations. For example, if the graphics operation performed by thread A is consuming needed resources, thread B can call the CancelDC function to halt that operation.
+$#A Retrieving Device Data
Windows supports two functions, GetDeviceCaps_win32_GetDeviceCaps and DeviceCapabilities_win32_DeviceCapabilities, that applications can use to retrieve device data using a device context. GetDeviceCaps retrieves general device data for the following devices:
· Raster displays
· Dot-matrix printers
· Ink-jet printers
· Laser printers
· Vector plotters
· Raster cameras
The data includes the supported capabilities of the device, including device resolution (for video displays), color format (for video displays and color printers), number of graphic objects, raster capabilities, curve drawing, line drawing, polygon drawing, and text drawing. An application retrieves this data by supplying a handle identifying the appropriate device context as well as an index specifying the type of data the function is to retrieve.
The DeviceCapabilities_win32_DeviceCapabilities function retrieves data specific to printers, including the number of available paper bins, the duplex capabilities of the printer, the resolutions supported by the printer, the maximum and minimum supported paper size, and so on. An application retrieves this data by supplying strings specifying a printer device and port, as well as an index specifying the type of data that the function is to retrieve.
+$#A Saving, Restoring, and Resetting a Device Context
The Win32 API supports three functions that an application can use to save, restore, and reset a device context: SaveDC_win32_SaveDC, RestoreDC_win32_RestoreDC, and ResetDC_win32_ResetDC. The SaveDC function records on a special GDI stack the current device context's graphic objects and their attributes, and graphic modes. A drawing application can call this function before a user begins drawing and save the application's original state ¾ providing a "clean slate" for the user. To return to this original state, the application calls the RestoreDC function.
ResetDC is provided to reset printer device context data. An application calls this function to reset the paper orientation, paper size, output scaling factor, number of copies to be printed, paper source (or bin), duplex mode, and so on. Typically, an application calls this function after a user has changed one of the printer options and Windows has issued a WM_DEVMODECHANGE_win32_WM_DEVMODECHANGE message.
+$#A Using the Device Context Functions
· Obtaining a private display device context_win32_Obtaining_a_Private_Display_Device_Context
· Creating a printer device context_win32_Creating_a_Printer_Device_Context
· Retrieving the capabilities of a printer_win32_Retrieving_the_Capabilities_of_a_Printer
· Retrieving graphic-object attributes and selecting new graphic objects!AL(`_win32_Retrieving_Graphic_Object_Attributes_and_Selecting_New_Graphic_Objects',3,`No_SDK')

+$#A Obtaining a Private Display Device Context
An application performing numerous drawing operations in the client area of its window must use a private display device context. To create this type of device context, the application must specify the CS_OWNDC constant for the style member of the WNDCLASS_win32_WNDCLASS_str structure when registering the window class. After registering the window class, the application obtains a handle identifying a private display device context by calling the GetDC_win32_GetDC function.
The following example shows how to create a private display device context.
#include <windows.h> /* required for all Windows apps */
#include <stdio.h>
#include <string.h> /* strtok */
#include "dc.h" /* specific to this program */

/* Function prototypes */

BOOL InitApplication(HINSTANCE);
long FAR PASCAL MainWndProc(HWND, UINT, UINT, LONG);

/* Global variables */

HINSTANCE hinst; /* handle of current instance */
HDC hdc; /* display device context handle */
.
.
.

BOOL InitApplication(HINSTANCE hinstance)
{
WNDCLASS wc;

/*
* Fill in the window class structure with parameters
* describing the main window.
*/

wc.style = CS_OWNDC; /* Private-DC constant */
wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;

wc.hIcon = LoadIcon((HINSTANCE) NULL,
MAKEINTRESOURCE(IDI_APPLICATION));

wc.hCursor = LoadCursor((HINSTANCE) NULL,
MAKEINTRESOURCE(IDC_ARROW));

wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "GenericMenu";
wc.lpszClassName = "GenericWClass";

/* Register the window class and return the
* resultant code.
*/

return RegisterClass(&wc);
}

LRESULT APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
WPARAM wParam, /* additional information */
LPARAM lParam) /* additional information */
{

PAINTSTRUCT ps; /* paint structure */

/* Retrieve a handle identifying the private DC. */

hdc = GetDC(hwnd);

switch (message) {

case WM_PAINT:
BeginPaint(hwnd, &ps);

.
. /* Draw and paint using private DC. */
.

+$#A Creating a Printer Device Context
An application can create a printer device context in one of two ways:
· Calling the PrintDlg_win32_PrintDlg function to display a common dialog box that allows the user to specify printer options, then creating a printer device context by using those options.
· Creating a printer device context by using the default printer data from the WIN.INI file.
This section contains code demonstrating the latter method.
To use the default printer, retrieve and parse the appropriate string from the WIN.INI file. This string is retrieved by calling the GetProfileString_win32_GetProfileString function and specifying the appropriate section and entry names. In the case of the default printer, this data is stored in the [windows] section and identified by the entry named device. The seventh line in the following excerpt from a WIN.INI file shows an entry for a Kodak® Diconix™ printer.
[windows]
load=
run=
Beep=yes
Spooler=yes
NullPort=None
device=Diconix,winspool,LPT1:
CoolSwitch=1
BorderWidth=3
KeyboardSpeed=31
InitialKeyboardIndicators=2
CursorBlinkRate=530
DoubleClickSpeed=686
Programs=com exe bat pif cmd
Documents=
DeviceNotSelectedTimeout=15
TransmissionRetryTimeout=45
swapdisk=
NetWarn=1
fPromptOnVerbose=FALSE
fPromptOnWarning=FALSE
fPromptOnError=TRUE
fPrintVerbose=FALSE
fPrintFileLine=FALSE
shell=
ScreenSaveTimeOut=300
ScreenSaveActive=0
DebugOptions=2048

The following example shows how a printer device context was created by using the data from WIN.INI.
HDC hdcPrint; /* printer DC handle */
char szDevString[120]; /* array for WIN.INI data */
char *szPrinter, *szDriver; /* printer and driver names */
char *szPort; /* port name */
/*
* Retrieve the printer, printer-driver, and
* output-port names from WIN.INI.
*/

GetProfileString("windows", "device", ",,,",
szDevString, 120);

/*
* Parse the string of names, setting ptrs
* as required. If the string contains the
* required names, use them to create a
* device context.
*/

if ((szPrinter = strtok(szDevString,
(const char *) ","))
&& (szDriver = strtok ((char *) NULL,
(const char *) ", "))
&& (szPort = strtok ((char *) NULL,
(const char *) ", ")))
hdcPrint = CreateDC(szDriver, szPrinter,
szPort, NULL);
/*
* Print a test page that contains the string
* "PRINTER TEST" in the upper left corner.
*/

Escape(hdcPrint, STARTDOC, 8, "Test-Doc", NULL);
TextOut(hdcPrint, 50, 50, "PRINTER TEST", 12);
Escape(hdcPrint, NEWFRAME, 0, NULL, NULL);
Escape(hdcPrint, ENDDOC, 0, NULL, NULL);

/* Delete the printer DC. */

DeleteDC(hdcPrint);

+$#A Retrieving the Capabilities of a Printer
Not every output device supports the entire set of Win32 graphics functions. For example, because of hardware limitations, most vector plotters do not support bit-block transfers. An application can determine whether a device supports a particular Win32 graphics function by calling the GetDeviceCaps_win32_GetDeviceCaps function, specifying the appropriate index, and examining the return value.
The following example shows how an application tests a printer to determine whether it supports bit-block transfers.
/*
* Examine the raster capabilities of the device
* identified by hdcPrint to verify that it supports
* the BitBlt function.
*/

if ((GetDeviceCaps(hdcPrint, RASTERCAPS)
& RC_BITBLT) == 0) {
DeleteDC(hdcPrint);
break;
}
else

/* Print the bitmap using the printer DC. */

+$# Retrieving Graphic-Object Attributes and Selecting New Graphic Objects
An application can retrieve the attributes for a pen, brush, palette, font, or bitmap by calling the GetCurrentObject_win32_GetCurrentObject and GetObject_win32_GetObject functions. The GetCurrentObject function returns a handle identifying the object currently selected into the device context; the GetObject function returns a structure that describes the object's attributes.
The following example shows how an application can retrieve the current brush attributes and use the retrieved data to determine whether it is necessary to select a new brush.
HDC hdc; /* display DC handle */
HBRUSH hbrushNew, hbrushOld; /* brush handles */
HBRUSH hbrush; /* brush handle */
LOGBRUSH lb; /* logical-brush structure */

/*
* Retrieve a handle identifying the current brush.
*/

hbrush = GetCurrentObject(hdc, OBJ_BRUSH);

/*
* Retrieve a LOGBRUSH structure that contains the
* current brush attributes.
*/

GetObject(hbrush, sizeof(LOGBRUSH), &lb);

/*
* If the current brush is not a solid-black brush,
* replace it with the solid-black stock brush.
*/

if ((lb.lbStyle != BS_SOLID)
|| (lb.lbColor != 0x000000)) {
hbrushNew = GetStockObject(BLACK_BRUSH);
hbrushOld = SelectObject(hdc, hbrushNew);
}

/* Perform painting operations with the white brush. */

/*
* After completing the last painting operation
* with the new brush, the application should
* select the original brush back into the
* device context and delete the new brush.
* In this example, hbrushNew contains a handle
* to a stock object. It is not necessary (but it
* is not harmful) to call DeleteObject on a stock
* object. If hbrushNew contained a handle to a brush
* created by a function such as CreateBrushIndirect,
* it would be necessary to call DeleteObject.
*/

SelectObject(hdc, hbrushOld);
DeleteObject(hbrushNew);

Note that the application saved the original brush handle when calling the SelectObject_win32_SelectObject function the first time. This handle is saved so that the original brush can be selected back into the device context after the last painting operation has been completed with the new brush. After the original brush is selected back into the device context, the new brush is deleted, freeing memory in the GDI heap.
+$#AK Device Context Reference
The following functions and messages are associated with device contexts.
+$#A Device Context Functions
Following are the functions used with device contexts.
CancelDC_win32_CancelDCChangeDisplaySettings_win32_ChangeDisplaySettingsCreateCompatibleDC_win32_CreateCompatibleDCCreateDC_win32_CreateDCCreateIC_win32_CreateICDeleteDC_win32_DeleteDCDeleteObject_win32_DeleteObjectDeviceCapabilities_win32_DeviceCapabilitiesDrawEscape_win32_DrawEscapeEnumDisplaySettings_win32_EnumDisplaySettingsEnumObjects_win32_EnumObjectsEnumObjectsProc_win32_EnumObjectsProcGetCurrentObject_win32_GetCurrentObjectGetDC_win32_GetDCGetDCEx_win32_GetDCExGetDCOrgEx_win32_GetDCOrgExGetDeviceCaps_win32_GetDeviceCapsGetGraphicsMode_win32_GetGraphicsModeGetObject_win32_GetObjectGetObjectType_win32_GetObjectTypeGetStockObject_win32_GetStockObjectReleaseDC_win32_ReleaseDCResetDC_win32_ResetDCRestoreDC_win32_RestoreDCSaveDC_win32_SaveDCSelectObject_win32_SelectObject
+$#A Device Context Messages
Following is the message used with device contexts.
WM_DEVMODECHANGE_win32_WM_DEVMODECHANGE

+$#AK Enhanced Metafile Records
An enhanced metafile is an array of variable-length structures called enhanced metafile records. This overview contains definitions for enhanced metafile records.
+$#A About Enhanced Metafile Records
The first records in a metafile specify general information, such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of the metafile, identify the graphics device interface (GDI) functions used to create the picture.
+$#A Enhanced Metafile Record Structure
At the beginning of every enhanced metafile record is an EMR_win32_EMR_str structure, which contains two members. The first member, iType, identifies the record type ¾ that is, the GDI function whose parameters are contained in the record. Because the structures are variable in length, the other member, nSize, contains the size of the record. Immediately following the nSize member are the remaining parameters, if any, of the GDI function.
+$#A Playing Back Enhanced Metafiles
Applications can play back enhanced metafiles using the PlayEnhMetaFile_win32_PlayEnhMetaFile or EnumEnhMetaFile_win32_EnumEnhMetaFile function. If using PlayEnhMetaFile to draw the picture, you pass a handle to the enhanced metafile without being concerned with the format of the enhanced metafile records. However, it is sometimes desirable to enumerate the records in the enhanced metafile to search for a particular GDI function and modify the parameters of the function in some manner. To do this, you can use EnumEnhMetaFile and provide a callback function of the type ENHMFENUMPROC to process the enhanced metafile records. To modify the parameters for an enhanced metafile record, you must know the format of the parameters within the record.
+$#A Enhanced Metafile Reference
The following structures are used with enhanced metafile records. Note that the first structure, EMR_win32_EMR_str, is used as the first member of the remaining structures.
EMR_win32_EMR_strEMRANGLEARC_win32_EMRANGLEARC_strEMRARC_win32_EMRARC_str, EMRARCTO, EMRCHORD, EMRPIEEMRBITBLT_win32_EMRBITBLT_strEMRCREATEBRUSHINDIRECT_win32_EMRCREATEBRUSHINDIRECT_strEMRCREATECOLORSPACE_win32_EMRCREATECOLORSPACE_str EMRCREATEDIBPATTERNBRUSHPT_win32_EMRCREATEDIBPATTERNBRUSHPT_strEMRCREATEMONOBRUSH_win32_EMRCREATEMONOBRUSH_strEMRCREATEPALETTE_win32_EMRCREATEPALETTE_strEMRCREATEPEN_win32_EMRCREATEPEN_strEMRELLIPSE_win32_EMRELLIPSE_str, EMRRECTANGLEEMREOF_win32_EMREOF_strEMREXCLUDECLIPRECT_win32_EMREXCLUDECLIPRECT_str, EMRINTERSECTCLIPRECTEMREXTCREATEFONTINDIRECTW_win32_EMREXTCREATEFONTINDIRECTW_strEMREXTCREATEPEN_win32_EMREXTCREATEPEN_strEMREXTFLOODFILL_win32_EMREXTFLOODFILL_strEMREXTSELECTCLIPRGN_win32_EMREXTSELECTCLIPRGN_strEMREXTTEXTOUTA_win32_EMREXTTEXTOUTA_str, EMREXTTEXTOUTWEMRFILLRGN_win32_EMRFILLRGN_strEMRFRAMERGN_win32_EMRFRAMERGN_strEMRGDICOMMENT_win32_EMRGDICOMMENT_strEMRINVERTRGN_win32_EMRINVERTRGN_str, EMRPAINTRGNEMRLINETO_win32_EMRLINETO_str, EMRMOVETOEXEMRMASKBLT_win32_EMRMASKBLT_strEMRMODIFYWORLDTRANSFORM_win32_EMRMODIFYWORLDTRANSFORM_strEMROFFSETCLIPRGN_win32_EMROFFSETCLIPRGN_strEMRPLGBLT_win32_EMRPLGBLT_strEMRPOLYDRAW_win32_EMRPOLYDRAW_strEMRPOLYDRAW16_win32_EMRPOLYDRAW16_strEMRPOLYPOLYLINE_win32_EMRPOLYPOLYLINE_str, EMRPOLYPOLYGONEMRPOLYPOLYLINE16_win32_EMRPOLYPOLYLINE16_str, EMRPOLYPOLYGON16EMRPOLYTEXTOUTA_win32_EMRPOLYTEXTOUTA_str, EMRPOLYTEXTOUTWEMRRESIZEPALETTE_win32_EMRRESIZEPALETTE_strEMRRESTOREDC_win32_EMRRESTOREDC_strEMRROUNDRECT_win32_EMRROUNDRECT_strEMRSELECTOBJECT_win32_EMRSELECTOBJECT_str, EMRDELETEOBJECTEMRSELECTPALETTE_win32_EMRSELECTPALETTE_strEMRSETARCDIRECTION_win32_EMRSETARCDIRECTION_strEMRSETCOLORADJUSTMENT_win32_EMRSETCOLORADJUSTMENT_strEMRSETDIBITSTODEVICE_win32_EMRSETDIBITSTODEVICE_strEMRSETMAPPERFLAGS_win32_EMRSETMAPPERFLAGS_strEMRSETBKCOLOR_win32_EMRSETBKCOLOR_str, EMRSETTEXTCOLOREMRSETMITERLIMIT_win32_EMRSETMITERLIMIT_strEMRSETPALETTEENTRIES_win32_EMRSETPALETTEENTRIES_strEMRSETPIXELV_win32_EMRSETPIXELV_strEMRSETWORLDTRANSFORM_win32_EMRSETWORLDTRANSFORM_strEMRSTRETCHDIBITS_win32_EMRSTRETCHDIBITS_strEMRSTRETCHBLT_win32_EMRSTRETCHBLT_strEMRTEXT_win32_EMRTEXT_strEMRFILLPATH_win32_EMRFILLPATH_str, EMRSTROKEANDFILLPATH, EMRSTROKEPATHEMRPOLYLINE_win32_EMRPOLYLINE_str, EMRPOLYBEZIER, EMRPOLYGON, EMRPOLYBEZIERTO, EMRPOLYLINETOEMRPOLYLINE16_win32_EMRPOLYLINE16_str, EMRPOLYBEZIER16, EMRPOLYGON16, EMRPOLYBEZIERTO16, EMRPOLYLINETO16EMRSCALEVIEWPORTEXTEX_win32_EMRSCALEVIEWPORTEXTEX_str, EMRSCALEWINDOWEXTEXEMRSELECTCOLORSPACE_win32_EMRSELECTCOLORSPACE_str, EMRDELETECOLORSPACE EMRSETVIEWPORTORGEX_win32_EMRSETVIEWPORTORGEX_str, EMRSETWINDOWORGEX, EMRSETBRUSHORGEXEMRSETVIEWPORTEXTEX_win32_EMRSETVIEWPORTEXTEX_str, EMRSETWINDOWEXTEX
+$#A Enhanced Metafile Records with No Parameters
typedef struct tagABORTPATH
{
EMR emr;
} EMRABORTPATH, *PEMRABORTPATH, EMRBEGINPATH, *PEMRBEGINPATH, EMRENDPATH, *PEMRENDPATH, EMRCLOSEFIGURE, *PEMRCLOSEFIGURE, EMRFLATTENPATH, *PEMRFLATTENPATH, EMRWIDENPATH, *PEMRWIDENPATH, EMRSETMETARGN, *PEMRSETMETARGN, EMRSAVEDC, *PEMRSAVEDC, EMRREALIZEPALETTE, *PEMRREALIZEPALETTE;
Contains data for the AbortPath_win32_AbortPath, BeginPath_win32_BeginPath, EndPath_win32_EndPath, CloseFigure_win32_CloseFigure, FlattenPath_win32_FlattenPath, WidenPath_win32_WidenPath, SetMetaRgn_win32_SetMetaRgn, SaveDC_win32_SaveDC, and RealizePalette_win32_RealizePalette enhanced metafile records.

+$#A Enhanced Metafile Records with One Parameter
typedef struct tagEMRSELECTCLIPPATH{ EMR emr;
DWORD iMode;
} EMRSELECTCLIPPATH, *PEMRSELECTCLIPPATH, EMRSETBKMODE, *PEMRSETBKMODE, EMRSETMAPMODE, *PEMRSETMAPMODE, EMRSETPOLYFILLMODE, *PEMRSETPOLYFILLMODE, EMRSETROP2, *PEMRSETROP2, EMRSETSTRETCHBLTMODE, *PEMRSETSTRETCHBLTMODE, EMRSETTEXTALIGN, *PEMRSETTEXTALIGN, EMRENABLEICM, *PEMRENABLEICM

Contains parameters for the SelectClipPath_win32_SelectClipPath, SetBkMode_win32_SetBkMode, SetPolyFillMode_win32_SetPolyFillMode, SetROP2_win32_SetROP2, SetStretchBltMode_win32_SetStretchBltMode, SetTextAlign_win32_SetTextAlign, and EnableICM enhanced metafile records.
Members
emr
Base structure for all record types.
iMode
Value and meaning that varies depending on the function contained in the enhanced metafile record. For a description of this member, see the documentation of the functions contained in this record .

See Also
SelectClipPath_win32_SelectClipPath, SetBkMode_win32_SetBkMode, SetPolyFillMode_win32_SetPolyFillMode, SetROP2_win32_SetROP2, SetStretchBltMode_win32_SetStretchBltMode, SetTextAlign_win32_SetTextAlign

+$#AK Filled Shapes
Filled shapes are geometric shapes that are outlined by using the current pen and filled by using the current brush. There are five filled shapes: ellipse, chord, pie, polygon, and rectangle.
+$#A About Filled Shapes
An application written for Microsoft® Windows® uses filled shapes in a variety of ways. Spreadsheet applications, for example, use filled shapes to construct charts and graphs, and drawing and painting applications use filled shapes to allow the user to draw figures and illustrations.
+$#A About Ellipses
An ellipse is a closed curve defined by two fixed points (f1 and f2) such that the sum of the distances (d1 + d2) from any point on the curve to the two fixed points is constant. The following illustration shows an ellipse drawn by using the Ellipse_win32_Ellipse function.
{ewc msdncd, EWGraphic, group10658 0 /a "SDK_01.bmp"}
When calling Ellipse, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle. A bounding rectangle is the smallest rectangle completely surrounding the ellipse. When Windows draws the ellipse, it excludes the right and lower sides if no world transformations are set. Therefore, for any rectangle measuring x units wide by y units high, the associated ellipse measures x-1 units wide by y-1 units high. If the application sets a world transformation by calling the SetWorldTransform_win32_SetWorldTransform or ModifyWorldTransform_win32_ModifyWorldTransform function, Windows includes the right and lower sides.
+$#A About Chords
A chord is a region bounded by the intersection of an ellipse and a line segment called a secant. The following illustration shows a chord drawn by using the Chord_win32_Chord function.
{ewc msdncd, EWGraphic, group10658 1 /a "SDK_02.bmp"}
When calling Chord, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle, as well as the coordinates of two points defining two radials. A radial is a line drawn from the center of an ellipse's bounding rectangle to a point on the ellipse.
When Windows draws the curved part of the chord, it does so by using the current arc direction for the specified device context. The default arc direction is counterclockwise. You can have your application reset the arc direction by calling the SetArcDirection_win32_SetArcDirection function.
+$#A About Pies
A pie is a region bounded by the intersection of an ellipse curve and two radials. The following illustration shows a pie drawn by using the Pie_win32_Pie function.
{ewc msdncd, EWGraphic, group10658 2 /a "SDK_03.bmp"}
When calling Pie, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle, as well as the coordinates of two points defining two radials.
When Windows draws the curved part of the pie, it uses the current arc direction for the given device context. The default arc direction is counterclockwise. An application can reset the arc direction by calling the SetArcDirection_win32_SetArcDirection function.
+$#A About Polygons
A polygon is a filled shape with straight sides. The sides of a polygon are drawn by using the current pen. When Windows fills a polygon, it uses the current brush and the current polygon fill mode. The two fill modes ¾ alternate (the default) and winding ¾ determine whether regions within a complex polygon are filled or left unpainted. An application can select either mode by calling the SetPolyFillMode_win32_SetPolyFillMode function. For more information about polygon fill modes, see Regions_win32_Regions.
The following illustration shows a polygon drawn by using Polygon_win32_Polygon.
{ewc msdncd, EWGraphic, group10658 3 /a "SDK_04.bmp"}
In addition to drawing a single polygon by using Polygon, an application can draw multiple polygons by using the PolyPolygon_win32_PolyPolygon function.
+$#A Drawing Rectangles
A rectangle is a four-sided polygon whose opposing sides are parallel and equal in length. Although an application can draw a rectangle by calling the Polygon_win32_Polygon function, supplying the coordinates of each corner, Windows provides a simpler method, use of the Rectangle_win32_Rectangle function. This function requires only the coordinates for the upper left and the lower right corners. When an application calls the Rectangle function, Windows draws the rectangle, excluding the right and lower sides if no world transformation is set for the given device context.
If a world transformation has been set by using the SetWorldTransform_win32_SetWorldTransform or ModifyWorldTransform_win32_ModifyWorldTransform function, Windows includes the right and lower edges.
In addition to drawing a normal rectangle, the Microsoft® Win32® application programming interface (API) provides a function that applications can use to draw rectangles with rounded corners. This function, RoundRect_win32_RoundRect, requires that the application supply the coordinates of the lower left and upper right corners, as well as the width and height of the ellipse used to round each corner.
The Win32 API also provides three functions that applications can use to manipulate rectangles, described as follows.
Function Description
FillRect_win32_FillRect Repaints the interior of a rectangle.
FrameRect_win32_FrameRect Redraws the sides of a rectangle.
InvertRect_win32_InvertRect Inverts the colors that appear within the interior of a rectangle.

+$#A Using Filled Shapes
This section illustrates how to use filled shape functions. The example uses the main window procedure from an application that enables the user to draw ellipses, rectangles, and rectangles with rounded corners.
The user draws a filled shape by selecting a particular shape from the menu, positioning the cursor at the upper left corner of the shape (or the shape's bounding rectangle in the case of an ellipse), and then dragging the mouse until the desired dimensions are obtained.
The following illustration shows three filled shapes drawn using the sample code in this section.
{ewc msdncd, EWGraphic, group10658 4 /a "SDK_05.bmp"}
To enable the user to draw filled shapes, include the following code in your application.
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd; /* handle of window */
UINT uMsg; /* message */
WPARAM wParam; /* first message parameter */
LPARAM lParam; /* second message parameter */
{
HDC hdc; /* handle of device context (DC) */
PAINTSTRUCT ps; /* paint data for Begin/EndPaint */
POINT ptClientUL; /* client area upper left corner */
POINT ptClientLR; /* client area lower right corner */
static HDC hdcCompat;/* handle of DC for bitmap */
static POINT pt; /* x- and y-coordinates of cursor */
static RECT rcTarget; /* rect to receive filled shape */
static RECT rcClient; /* client area rectangle */
static BOOL fSizeEllipse; /* TRUE if ellipse is sized */
static BOOL fDrawEllipse; /* TRUE if ellipse is drawn */
static BOOL fDrawRectangle; /* TRUE if rectangle is drawn */
static BOOL fSizeRectangle; /* TRUE if rectangle is sized */
static BOOL fSizeRoundRect; /* TRUE if rounded rect is sized */
static BOOL fDrawRoundRect; /* TRUE if rounded rect is drawn */
static int nEllipseWidth; /* width for round corners */
static int nEllipseHeight; /* height for round corners */

switch (uMsg) {

case WM_COMMAND:
switch (wParam) {

/*
* Set the appropriate flag to indicate which
* filled shape the user is drawing.
*/

case IDM_ELLIPSE:
fSizeEllipse = TRUE;
break;

case IDM_RECTANGLE:
fSizeRectangle = TRUE;
break;

case IDM_ROUNDRECT:
fSizeRoundRect = TRUE;
break;

default:
return DefWindowProc(hwnd, uMsg, wParam,
lParam);
}
break;

case WM_CREATE:

nEllipseWidth = 20;
nEllipseHeight = 20;

return 0;

case WM_PAINT:

BeginPaint(hwnd, &ps);

/*
* Because the default brush is white, select
* a different brush into the device context
* to demonstrate the painting of filled shapes.
*/

SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH));

/*
* If one of the filled shape "draw" flags is TRUE,
* draw the corresponding shape.
*/

if (fDrawEllipse) { /* draws ellipse */
Ellipse(ps.hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);
fDrawEllipse = FALSE;
rcTarget.left = rcTarget.right = 0;
rcTarget.top = rcTarget.bottom = 0;
}

if (fDrawRectangle) { /* Draws rectangle */
Rectangle(ps.hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);
fDrawRectangle = FALSE;
rcTarget.left = rcTarget.right = 0;
rcTarget.top = rcTarget.bottom = 0;
}

if (fDrawRoundRect) { /* Draws rounded rectangle */
RoundRect(ps.hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom,
nEllipseWidth, nEllipseHeight);
fDrawRectangle = FALSE;
rcTarget.left = rcTarget.right = 0;
rcTarget.top = rcTarget.bottom = 0;
}

EndPaint(hwnd, &ps);
break;

case WM_SIZE:

/*
* Convert the client coordinates of the client area
* rectangle to screen coordinates and save them in a
* rectangle. The rectangle is passed to the ClipCursor
* function during WM_LBUTTONDOWN processing.
*/

GetClientRect(hwnd, &rcClient);
ptClientUL.x = rcClient.left;
ptClientUL.y = rcClient.top;
ptClientLR.x = rcClient.right;
ptClientLR.y = rcClient.bottom;
ClientToScreen(hwnd, &ptClientUL);
ClientToScreen(hwnd, &ptClientLR);
SetRect(&rcClient, ptClientUL.x, ptClientUL.y,
ptClientLR.x, ptClientLR.y);
return 0;

case WM_LBUTTONDOWN:

/*
* Restrict the cursor to the client area.
* This ensures that the window receives a matching
* WM_LBUTTONUP message.
*/

ClipCursor(&rcClient);

/* Save the coordinates of the cursor. */

pt.x = (LONG) LOWORD(lParam);
pt.y = (LONG) HIWORD(lParam);

/*
* If the user chooses one of the filled shapes,
* set the appropriate flag to indicate that the
* shape is being sized.
*/

if (fDrawEllipse)
fSizeEllipse = TRUE;

return 0;

case WM_MOUSEMOVE:

/*
* If one of the "size" flags is set, draw
* the target rectangle as the user drags
* the mouse.
*/

if ((wParam && MK_LBUTTON)
&& (fSizeEllipse || fSizeRectangle
|| fSizeRoundRect)) { /* draws target rect. */

/*
* Set the mixing mode so that the pen color is the
* inverse of the background color. The previous
* rectangle can then be erased by drawing
* another rectangle on top of it.
*/

hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOTXORPEN);

/*
* If a previous target rectangle exists, erase
* it by drawing another rectangle on top.
*/

if (!IsRectEmpty(&rcTarget))
Rectangle(hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);

/*
* Save the coordinates of the target rectangle.
* Avoid invalid rectangles by ensuring that the
* value of the left coordinate is greater than
* that of the right, and that the value of the
* bottom coordinate is greater than that of
* the top.
*/

if ((pt.x < (LONG) LOWORD(lParam)) &&
(pt.y > (LONG) HIWORD(lParam)))
SetRect(&rcTarget, pt.x, HIWORD(lParam),
LOWORD(lParam), pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&
(pt.y > (LONG) HIWORD(lParam)))
SetRect(&rcTarget, LOWORD(lParam),
HIWORD(lParam), pt.x, pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&
(pt.y < (LONG) HIWORD(lParam)))
SetRect(&rcTarget, LOWORD(lParam), pt.y,
pt.x, HIWORD(lParam));
else
SetRect(&rcTarget, pt.x, pt.y, LOWORD(lParam),
HIWORD(lParam));

/* Draw the new target rectangle. */

Rectangle(hdc, rcTarget.left, rcTarget.top,
rcTarget.right, rcTarget.bottom);
ReleaseDC(hwnd, hdc);
}
return 0;

case WM_LBUTTONUP:

/*
* If one of the "size" flags is TRUE, reset
* it to FALSE, and then set the corresponding
* "draw" flag.
* Invalidate the appropriate rectangle and issue
* a WM_PAINT message.
*/

if (fSizeEllipse) {
fSizeEllipse = FALSE;
fDrawEllipse = TRUE;
}

if (fSizeRectangle) {
fSizeRectangle = FALSE;
fDrawRectangle = TRUE;
}

if (fSizeRoundRect) {
fSizeRoundRect = FALSE;
fDrawRoundRect = TRUE;
}

if (fDrawEllipse || fDrawRectangle || fDrawRoundRect)
{
InvalidateRect(hwnd, &rcTarget, TRUE);
UpdateWindow(hwnd);
}

/* Release the cursor. */

ClipCursor((LPRECT) NULL);
return 0;

case WM_DESTROY:

/*
* Destroy the background brush, compatible bitmap,
* and bitmap.
*/

DeleteDC(hdcCompat);
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}

+$#A Filled Shape Functions
The following functions are used with filled shapes.
Chord_win32_ChordEllipse_win32_EllipseFillRect_win32_FillRectFrameRect_win32_FrameRectInvertRect_win32_InvertRectPie_win32_PiePolygon_win32_PolygonPolyPolygon_win32_PolyPolygonRectangle_win32_RectangleRoundRect_win32_RoundRect

+$#AK Fonts and Text
Fonts are used to draw text on video displays and other output devices. The Microsoft® Windows® operating system provides a set of functions that developers can use to install, select, and query different fonts.
+$#A About Fonts
To a person trained in the mechanics of manuscript composition or familiar with standard typography, some of the typographic terms used in Windows may be unusual. Most of the differences between standard typography and Windows reflect changes in technology. The original typographic terms were based on hot-metal composition, whereas the terms used in Windows, which appear as member names for the font and text output structures, reflect a new technology based on laser-printer output and composition performed on a personal computer using desktop publishing software.
+$#A Fonts Overview
In Windows, a font is a collection of characters and symbols that share a common design. The three major elements of this design are referred to as typeface, style, and size.
Typeface
The term typeface refers to specific characteristics of characters and symbols in the font, such as the width of the thick and thin strokes that compose the characters and the presence or absence of serifs. A serif is the short cross line at the ends of an unconnected stroke. A font or typeface without serifs is usually called a sans-serif font.
Style
The term style refers to the weight and slant of a font. Font weights can range from thin to black. The following list identifies possible weights for Windows fonts (beginning with the lightest and ending with the heaviest).
ThinExtralightLightNormalMediumSemiboldBoldExtraboldHeavy
Three terms categorize the slant of a Windows font: roman, oblique, and italic.
The characters in a roman font are upright. The characters in an oblique font are artificially slanted. The slant is achieved by performing a shear transformation on the characters from a roman font. The characters in an italic font are truly slanted and appear as they were designed. For more information on shearing, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.
Size
In Windows, the size of a font is an imprecise value. It can generally be determined by measuring the distance from the bottom of a lowercase "g" to the top of an adjacent uppercase "M," as shown in the following illustration.
{ewc msdncd, EWGraphic, group10659 0 /a "SDK_01.bmp"}
A font's size is specified in units called points. A point is .013837 of an inch. Following the point system devised by Pierre Simon Fournier, it is common practice to approximate a point as 1/72 inch.
+$#A Font Families
Windows organizes fonts by family; a family is a set of fonts having common stroke width and serif characteristics. Windows categorizes families with five family names. A sixth name ("Dontcare") allows an application to use the default font. The following table describes the font-family names.
Font-family name Description
Decorative Specifies a novelty font. An example is Old English.
Dontcare Specifies a generic family name. This name is used when information about a font does not exist or does not matter.
Modern Specifies a monospace font with or without serifs. Monospace fonts are usually modern; examples include Pica, Elite, and Courier New®.
Roman Specifies a proportional font with serifs. An example is Times New Roman.
Script Specifies a font that is designed to look like handwriting; examples include Script and Cursive.
Swiss Specifies a proportional font without serifs. An example is Arial.

These family names correspond to constants found in the WINGDI.H file: FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN, FF_SCRIPT, and FF_SWISS. An application uses these constants when it creates a font, selects a font, or retrieves information about a font.
Fonts within a family are distinguished by size (10 point, 24 point, etc.) and style (regular, italic, etc.).
+$#A Raster, Vector, and TrueType® Fonts
Windows-based applications can use three different kinds of font technologies to display and print text: raster, vector, and TrueType. The differences between these fonts reflect the way that the glyph for each character or symbol is stored in the respective font-resource file. In raster fonts, a glyph is a bitmap that Windows uses to draw a single character or symbol in the font. In vector fonts, a glyph is a collection of line endpoints that define the line segments Windows uses to draw a character or symbol in the font. In TrueType fonts, a glyph is a collection of line and curve commands as well as a collection of hints. Windows uses the line and curve commands to define the outline of the bitmap for a character or symbol in the TrueType font. Windows uses the hints to adjust the length of the lines and shapes of the curves used to draw the character or symbol. These hints and the respective adjustments are based on the amount of scaling used to reduce or increase the size of the bitmap.
Because the bitmaps for each glyph in a raster font are designed for a specific resolution of device, raster fonts are generally considered to be device dependent. Vector fonts, on the other hand, are not device dependent, because each glyph is stored as a collection of scalable lines. However, vector fonts are generally drawn more slowly than raster or TrueType fonts. TrueType fonts provide both relatively fast drawing speed and true device independence. By using the hints associated with a glyph, a developer can scale the characters from a TrueType font up or down and still maintain their original shape.
As previously mentioned, the glyphs for a font are stored in a font-resource file. A font-resource file is actually a Windows library that contains only data ¾ there is no code. For raster and vector fonts, this data is divided into two parts: a header describing the font's metrics and the glyph data. A font-resource file for a raster or vector font is identified by the .FON filename extension. For TrueType fonts, there are two files for each font: the first file contains a relatively short header and the second contains the actual font data. The first file is identified by a .FOT extension and the second is identified by a .TTF extension.
+$#A Character Sets Used by Fonts
All fonts use a character set. A character set contains punctuation marks, numerals, uppercase and lowercase letters, and all other printable characters. Each element of a character set is identified by a number.
Most character sets used in Windows are supersets of the U.S. ASCII character set, which defines characters for the 96 numeric values from 32 through 127. There are five major groups of character sets:
· Windows
· Unicode™
· OEM (original equipment manufacturer)
· Symbol
· Vendor-specific

Windows Character Set
The Windows character set is the most commonly used character set in Windows programming. It is essentially equivalent to the ANSI character set. The blank character is the first character in the Windows character set. It has a hexadecimal value of 0x20 (decimal 32). The last character in the Windows character set has a hexadecimal value of 0xFF (decimal 255).
Many fonts specify a default character. Whenever a request is made for a character that is not in the font, Windows provides this default character. Many fonts using the Windows character set specify the period (.) as the default character. TrueType fonts typically use an open box as the default character.
Fonts use a break character called a quad to separate words and justify text. Most fonts using the Windows character set specify that the blank character will serve as the break character.
Windows version 3.1 added 24 characters to the Windows code page, as shown in the following table.
Character Name Windows character code
‚ base line single quote 130
ƒ florin 131
„ base line double quote 132
… ellipsis 133
† dagger 134
‡ double dagger 135
ˆ circumflex 136
‰ permille 137
Š S Hacek 138
‹ left single guillemet 139
Œ OE ligature 140
' left single quote 145
' right single quote 146
" left double quote 147
" right double quote 148
• bullet 149
- en dash 150
¾ em dash 151
˜ tilde 152
™ trademark ligature 153
š s Hacek 154
› right single guillemet 155
œ oe ligature 156
Ÿ Y Dieresis 159

It should be noted that the characters for left and right single quote were first added to the character set for the release of Windows version 3.0.
Unicode™ Character Set
The Windows ANSI character uses 8 bits to represent each character; therefore, the maximum number of characters that can be expressed using 8 bits is 256 (2^8). This is usually sufficient for Western languages, including the diacritical marks used in French, German, Spanish, and other languages. However, Eastern languages employ thousands of separate characters, which cannot be encoded by using a single-byte coding scheme. With the proliferation of computer commerce, double-byte coding schemes were developed so that characters could be represented in 8-bit, 16-bit, 24-bit, or 32-bit sequences. This requires complicated passing algorithms; even so, using different code sets could yield entirely different results on two different computers.
To address the problem of multiple coding schemes, the Unicode standard for data representation was developed. A 16-bit character coding scheme, Unicode can represent 65,536 (2^16) characters, which is enough to include all languages in computer commerce today, as well as punctuation marks, mathematical symbols, and room for future expansion. Unicode establishes a unique code for every character to ensure that character translation is always accurate.
OEM Character Set
The OEM character set is typically used in full-screen MS-DOS® sessions for screen display. Characters 32 through 127 are usually the same in the OEM, U.S. ASCII, and Windows character sets. The other characters in the OEM character set (0 through 31 and 128 through 255) correspond to the characters that can be displayed in a full-screen MS-DOS session. These characters are generally different from the Windows characters.
Symbol Character Set
The Symbol character set contains special characters typically used to represent mathematical and scientific formulas.
Vendor-Specific Character Sets
Many printers and other output devices provide fonts based on character sets that differ from the Windows and OEM sets ¾ for example, the Extended Binary Coded Decimal Interchange Code (EBCDIC) character set. To use one of these character sets, the printer driver translates from the Windows character set to the vendor-specific character set.
+$#A Font Installation and Deletion
A font must already be resident on a given device or installed in the Windows font table in order for an application to draw text using glyphs from that font. The Windows font table is an internal array that identifies all nondevice fonts that are available to a Windows-based application. An application can retrieve the names of fonts currently installed on a device and stored in the internal font table by calling the EnumFontFamilies_win32_EnumFontFamilies or ChooseFont_win32_ChooseFont function.
An application can install a font by calling the font-installation function AddFontResource_win32_AddFontResource. The AddFontResource function loads a font that is stored in a font-resource file.
In the case of TrueType fonts, an additional step is sometimes necessary before the font can be installed in the font table. Some font manufacturers ship only the TrueType font-data files (identified by the .TTF extension). Before Windows can load these fonts, it requires a corresponding header file (identified by the .FOT extension). To create this header file, an application must call the CreateScalableFontResource_win32_CreateScalableFontResource function and pass the name of the font-data file as the third parameter. When this header file is created, an application can install the font by calling the AddFontResource function and passing the name of the new header file.
When an application finishes using an installed font, it must remove that font by calling the RemoveFontResource_win32_RemoveFontResource function.
Whenever an application calls the functions that add and delete font resources, it should also call the SendMessage_win32_SendMessage function and send a WM_FONTCHANGE_win32_WM_FONTCHANGE message to all top-level windows in the system. This message notifies other applications that the internal font table has been altered by an application that added or removed a font.
+$#A Font Creation and Selection
Previous versions of Windows required that the application developer provide source code and a dialog box template to support an interface that displayed the available fonts from which the user could choose. The new Font common dialog box simplifies the creation and selection process. By initializing the CHOOSEFONT_win32_CHOOSEFONT_str structure and calling the ChooseFont_win32_ChooseFont function, an application can support the same font-selection interface that previously required many lines of code. (For more information about the Font common dialog box, see Common Dialog Box Library_win32_Common_Dialog_Box_Library.)
Selection by the User
Most font creation and selection operations involve the user. For example, word processing applications let the user select unique fonts for headings, footnotes, and body text. After the user selects a font by using the Font dialog box and presses the OK button, the ChooseFont_win32_ChooseFont function initializes the members of a LOGFONT_win32_LOGFONT_str structure with the attributes of the requested font. To use this font for text-output operations, an application must first create a logical font and then select that font into its device context. A logical font is an application-supplied description of an ideal font. A developer can create a logical font by calling the CreateFont_win32_CreateFont or the CreateFontIndirect_win32_CreateFontIndirect functions. In this case, the application would call CreateFontIndirect and supply a pointer to the LOGFONT structure initialized by ChooseFont. In general, it is more efficient to call CreateFontIndirect because CreateFont requires several parameters while CreateFontIndirect requires only one ¾ a pointer to LOGFONT.
Before an application can actually begin drawing text with a logical font, it must find the closest match from the fonts stored internally on the device and the fonts whose resources have been loaded into the operating system. The fonts stored on the device or in the operating system are called physical fonts. The process of finding the physical font that most closely matches a specified logical font is called font mapping. This process occurs when an application calls the SelectObject_win32_SelectObject function and supplies a handle identifying a logical font. Font mapping is performed by using an internal algorithm that compares the attributes of the requested logical font against the attributes of available physical fonts. When the font mapper algorithm completes its search and determines the closest possible match, the SelectObject function returns and the application can begin drawing text with the new font.
The SetMapperFlags_win32_SetMapperFlags function specifies whether or not the font mapper algorithm searches only for physical fonts with aspect ratios that match the physical device. The aspect ratio for a device is the ratio formed by the width and the height of a pixel on that device.
+$#A Special Font Selection Considerations
Although most font selection operations involve the user, there are some instances where this is not true. For example, a developer may want to use a unique font in an application to draw text in a control window. To select an appropriate font, the application must be able to determine what fonts are available, create a logical font that describes one of these available fonts, and then select that font into the appropriate device context.
An application can enumerate the available fonts by using the EnumFontFamilies_win32_EnumFontFamilies function. This can be useful when an application must determine which fonts are available from a given family.
Once an application has enumerated the available fonts and located an appropriate match, it should use the values returned by the font enumeration function to initialize the members of a LOGFONT_win32_LOGFONT_str structure. Then it can call the CreateFontIndirect_win32_CreateFontIndirect function, passing to it a pointer to the initialized LOGFONT structure. If the CreateFontIndirect function is successful, the application can then select the logical font by calling the SelectObject_win32_SelectObject function.
When initializing the members of the LOGFONT structure, be sure to specify a specific character set in the lfCharSet member. This member is important in the font mapping process and the results will be inconsistent if this member is not initialized correctly. If you specify a typeface name in the lfFaceName member of the LOGFONT structure, make sure that the lfCharSet value matches the character set of the typeface specified in lfFaceName. For example, if you want to select a font such as "MS Mincho", lfCharSet must be set to the predefined value SHIFTJIS_CHARSET.
+$#A Embedded Fonts
Embedding a font is the technique of bundling a document and the fonts it contains into a file for transmission to another computer. Embedding a font guarantees that a font specified in a transmitted file will be present on the computer receiving the file. Not all fonts can be moved from computer to computer, however, since most fonts are licensed to only one computer at a time. In Windows, only TrueType fonts can be embedded.
Applications should embed a font in a document only when requested by a user. An application cannot be distributed along with documents that contain embedded fonts, nor can an application itself contain an embedded font. Whenever an application distributes a font, in any format, the proprietary rights of the owner of the font must be acknowledged.
It may be a violation of a font vendor's proprietary rights or user license agreement to embed any fonts where embedding is not permitted or to fail to observe the following guidelines on embedding fonts. A font's license may give only read-write permission for a font to be installed and used on the destination computer. Or the license may give read-only permission. Read-only permission allows a document to be viewed and printed (but not modified) by the destination computer; documents with read-only embedded fonts are themselves read-only. Read-only embedded fonts may not be unbundled from the document and installed on the destination computer.
An application can determine the license status by calling the GetOutlineTextMetrics_win32_GetOutlineTextMetrics function and examining the otmfsType member of the OUTLINETEXTMETRIC_win32_OUTLINETEXTMETRIC_str structure. If bit 1 of otmfsType is set, embedding is not permitted for the font. If bit 1 is clear, the font can be embedded. If bit 2 is set, the embedding is read-only.
To embed a font, an application can use the GetFontData_win32_GetFontData function to read the font file. Setting the dwTable and dwOffset parameters of GetFontData to 0L and the cbData parameter to -1L ensures that the application reads the entire font file from the beginning.
After an application retrieves the font data, it can store the data with the document by using any applicable format. Most applications build a font directory in the document, listing the embedded fonts and whether the embedding is read-write or read-only. An application can use the otmpStyleName and otmFamilyName members of the OUTLINETEXTMETRIC structure to identify the font.
If the read-only bit is set for the embedded font, applications must encrypt the font data before storing it with the document. The encryption method need not be complicated; for example, using the XOR operator to combine the font data with an application-defined constant is adequate and fast.
+$#A About Text Output
Text output is the most common type of graphic output found within the client area of Windows-based applications. It is used by Windows-based applications in different ways. Word processing and desktop publishing applications create documents with formatted text; spreadsheet applications use text, numbers, and symbols to specify formulas, label columns, and list values; database applications create records and display queries with text, and CAD applications use text to label objects and display dimensions.
The Win32® application programming interface (API) provides a complete set of functions to format and draw text in an application's client area and on a page of printer paper. These functions can be divided into two categories: those that format the text (or prepare it for output) and those that actually draw the text. The formatting functions align text, set the intercharacter spacing, set the text and text-background colors, and justify text. The drawing functions draw individual characters (or symbols) or entire strings of text.
+$#A Formatting Text
The formatting functions can be divided into three categories: those that retrieve or set the text-formatting attributes for a device context, those that retrieve character widths, and those that retrieve string widths and heights.
+$#A Text-Formatting Attributes
An application can use six functions to set the text-formatting attributes for a device context: SetBkColor_win32_SetBkColor, SetBkMode_win32_SetBkMode, SetTextAlign_win32_SetTextAlign, SetTextCharacterExtra_win32_SetTextCharacterExtra, SetTextColor_win32_SetTextColor, and SetTextJustification_win32_SetTextJustification. These functions affect the text alignment, the intercharacter spacing, the text justification, and text and background colors. In addition, six other functions can be used to retrieve the current text formatting attributes for any device context: GetBkColor_win32_GetBkColor, GetBkMode_win32_GetBkMode, GetTextAlign_win32_GetTextAlign, GetTextCharacterExtra_win32_GetTextCharacterExtra, GetTextColor_win32_GetTextColor, and GetTextExtentPoint32_win32_GetTextExtentPoint32.
Text Alignment
Applications can use the SetTextAlign_win32_SetTextAlign function to specify how Windows should position the characters in a string of text when they call one of the drawing functions. This function can be used to position headings, page numbers, callouts, and so on. Windows positions a string of text by aligning a reference point on an imaginary rectangle that surrounds the string, with the current cursor position or with a point passed as an argument to one of the text drawing functions. The SetTextAlign function lets the application specify the location of this reference point. The list below identifies the possible reference point locations.
Location Description
left/bottom The reference point is located at the bottom left corner of the rectangle.
left/base line The reference point is located at the intersection of the character-cell base line and the left edge of the rectangle.
left/top The reference point is located at the top left corner of the rectangle.
center/bottom The reference point is located at the center of the bottom of the rectangle.
center/base line The reference point is located at the intersection of the character-cell base line and the center of the rectangle.
center/top The reference point is located at the center of the top of the rectangle.
right/bottom The reference point is located at the bottom right corner of the rectangle.
right/base line The reference point is located at the intersection of the character-cell base line and the right edge of the rectangle.

right/top The reference point is located at the top right corner of the rectangle.

The following illustration shows a string of text drawn by calling the TextOut_win32_TextOut function. Before drawing the text, the SetTextAlign_win32_SetTextAlign function was called to relocate the reference point at each one of the nine possible locations.
{ewc msdncd, EWGraphic, group10659 1 /a "SDK_04.bmp"}
The default text alignment for a device context is the upper left corner of the imaginary rectangle that surrounds the text. An application can retrieve the current text-alignment setting for any device context by calling the GetTextAlign_win32_GetTextAlign function.
Intercharacter Spacing
Applications can use the SetTextCharacterExtra_win32_SetTextCharacterExtra function to alter the intercharacter spacing for all text output operations in a given device context. The following illustration shows a string of text drawn twice by calling the TextOut_win32_TextOut function. Before drawing the text the second time, the SetTextCharacterExtra function was called to increment the intercharacter spacing.
{ewc msdncd, EWGraphic, group10659 2 /a "SDK_06.bmp"}
The default intercharacter spacing value for any device context is zero. An application can retrieve the current intercharacter spacing value for a device context by calling the GetTextCharacterExtra_win32_GetTextCharacterExtra function.
Text Justification
Applications can use the GetTextExtentPoint32_win32_GetTextExtentPoint32 and SetTextJustification_win32_SetTextJustification functions to justify a line of text. Text justification is a common operation in any desktop publishing and in most word processing applications. The GetTextExtentPoint32 function computes the width and height of a string of text. After the width is computed, the application can call the SetTextJustification function to distribute extra spacing between each of the words in a line of text. The following illustration shows a paragraph of text printed twice: in the first paragraph, the text was not justified; in the second paragraph, the text was justified by calling the GetTextExtentPoint32 and SetTextJustification functions.
{ewc msdncd, EWGraphic, group10659 3 /a "SDK_05.bmp"}
Text and Background Color
Applications can use the SetTextColor_win32_SetTextColor function to set the color of text drawn in the client-area of their windows, as well as the color of text drawn on a color printer. An application can use the SetBkColor_win32_SetBkColor function to set the color that appears behind each character and the SetBkMode_win32_SetBkMode function to specify how Windows should blend the selected background color with the current color or colors on the video display.
The default text color for a display device context is black; the default background color is white; and the default background mode is OPAQUE. An application can retrieve the current text color for a device context by calling the GetTextColor_win32_GetTextColor function. An application can retrieve the current background color for a device context by calling the GetBkColor_win32_GetBkColor function and the current background mode by calling the GetBkMode_win32_GetBkMode function.
+$#A Character Widths
Applications need to retrieve character-width data when they perform such tasks as fitting strings of text to page or column widths. There are four functions that an application can use to retrieve character-width data. Two of these functions retrieve the character-advance width and two of these functions retrieve actual character-width data.
An application can use the GetCharWidth32_win32_GetCharWidth32 and GetCharWidthFloat_win32_GetCharWidthFloat functions to retrieve the advance width for individual characters or symbols in a string of text. The advance width is the distance that the cursor on a video display or the print-head on a printer must advance before printing the next character in a string of text. The GetCharWidth32 function returns the advance width as an integer value. If greater precision is required, an application can use the GetCharWidthFloat function to retrieve fractional advance-width values.
An application can retrieve actual character-width data by using the GetCharABCWidths_win32_GetCharABCWidths and GetCharABCWidthsFloat_win32_GetCharABCWidthsFloat functions. To retrieve character widths for characters in a string of text that will be printed using a TrueType font, an application can call the GetCharABCWidths function; however, for any other font, the application should call the GetCharABCWidthsFloat function. (For more information about TrueType fonts, see Raster, Vector, and TrueType® Fonts_win32_Raster.2c_.Vector.2c_.and_TrueType.ae_.Fonts). The following illustration shows the three components of the ABC width:
{ewc msdncd, EWGraphic, group10659 4 /a "SDK_02.bmp"}
The "A" spacing is the width to add to the current position before placing the character. The "B" spacing is the width of the character itself. The "C" spacing is the white space to the right of the character. The total advance width is determined by calculating the sum of A+B+C. The character cell is an imaginary rectangle that surrounds each character or symbol in a Windows font. Because characters can overhang or underhang the character cell, either or both of the A and C increments can be a negative number.
+$#A String Widths and Heights
In addition to retrieving character-width data for individual characters, applications also need to compute the width and height of entire strings. Two functions retrieve string-width and height measurements: GetTextExtentPoint32_win32_GetTextExtentPoint32, and GetTabbedTextExtent_win32_GetTabbedTextExtent. If the string does not contain tab characters, an application can use the GetTextExtentPoint32 function to retrieve the width and height of a specified string. If the string contains tab characters, an application should call the GetTabbedTextExtent function.
Windows also supports a special function, called GetTextExtentExPoint_win32_GetTextExtentExPoint, that applications can use for word-wrapping operations. This function returns the number of characters from a specified string that fit within a given space.
Font Ascenders and Descenders
Some applications determine the line spacing between text lines of different sizes by using a font's maximum ascender and descender. An application can retrieve these values by calling the GetTextMetrics_win32_GetTextMetrics function and then checking the tmAscent and tmDescent members of the TEXTMETRIC_win32_TEXTMETRIC_str.
The maximum ascent and descent are different from the typographic ascent and descent. In TrueType fonts, the typographic ascent and descent are typically the top of the "f" glyph and bottom of the "g" glyph. An application can retrieve the typographic ascender and descender for a TrueType font by calling the GetOutlineTextMetrics_win32_GetOutlineTextMetrics function and checking the values in the otmMacAscent and otmMacDescent members of the OUTLINETEXTMETRIC_win32_OUTLINETEXTMETRIC_str structure. It is important to note that TrueType font metrics do not correspond exactly to the metrics for Windows raster fonts, because TrueType font metrics have been designed by Apple Computer, Inc. for consistency across a variety of display and output devices.
The following figure shows the difference between the vertical text metric values returned in the NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str and OUTLINETEXTMETRIC structures. (The names beginning with otm are members of the OUTLINETEXTMETRIC structure.)
{ewc msdncd, EWGraphic, group10659 5 /a "SDK_03.bmp"}
Font Dimensions
An application can retrieve the physical dimensions of a TrueType font by calling the GetOutlineTextMetrics_win32_GetOutlineTextMetrics function. An application can retrieve the physical dimensions of any other font by calling the GetTextMetrics_win32_GetTextMetrics function. To determine the dimensions of an output device, an application can call the GetDeviceCaps_win32_GetDeviceCaps function. GetDeviceCaps returns both physical and logical dimensions.
A logical inch is a measure Windows uses to present legible fonts on the screen and is approximately 30 to 40 percent larger than a physical inch. The use of logical inches precludes an exact match between the output of the screen and printer. Developers should be aware that the text on a screen is not simply a scaled version of the text that will appear on the page, particularly if graphics are incorporated into the text.
+$#A Drawing Text
After an application selects the appropriate font, sets the required text-formatting options, and computes the necessary character width and height values for a string of text, it can begin drawing characters and symbols by calling any of the four text-output functions. Two of these functions, DrawText_win32_DrawText and TabbedTextOut_win32_TabbedTextOut, are part of Window Manager and found in the library USER.DLL; the remaining two functions are part of GDI and found in the library GDI.DLL. When an application calls one of these functions, the operating system passes the call to the graphics engine, which in turn passes the call to the appropriate device driver. At the device driver level, all of these calls are supported by one or more calls to the driver's own ExtTextOut_win32_ExtTextOut or TextOut_win32_TextOut function. An application will achieve the fastest execution by calling the ExtTextOut function, which is quickly converted into an ExtTextOut call for the device. However, there are instances when an application should call one of the other three functions; for example, to draw multiple lines of text within the borders of a specified rectangular region, it is more efficient to call the DrawText function. To create a multicolumn table with justified columns of text, it is more efficient to call the TabbedTextOut function.
+$#A Using the Font and Text-Output Functions
This section describes how you can use the font and text-output functions to draw normal text, draw text from different fonts on the same line, rotate lines of text, display the font-selection common dialog-box, enumerate fonts, and so on.
· Using a stock font to draw text_win32_Using_a_Stock_Font_to_Draw_Text
· Creating a logical font_win32_Creating_a_Logical_Font
· Enumerating the installed fonts_win32_Enumerating_the_Installed_Fonts
· Checking the text capabilities of a device_win32_Checking_the_Text_Capabilities_of_a_Device
· Setting the text alignment_win32_Setting_the_Text_Alignment
· Drawing text from different fonts on the same line_win32_Drawing_Text_From_Different_Fonts_on_the_Same_Line
· Rotating lines of text_win32_Rotating_Lines_of_Text
· Retrieving character outlines_win32_Retrieving_Character_Outlines
· Using portable truetype metrics_win32_Using_Portable_TrueType_Metrics
· Using PANOSE numbers_win32_Using_PANOSE_Numbers
· Creating customized fonts_win32_Creating_Customized_Fonts

+$#A Using a Stock Font to Draw Text
Windows provides six stock fonts. A stock font is a logical font that an application can obtain by calling the GetStockObject_win32_GetStockObject function and passing a value that identifies the requested font. The following list contains the six values that you can specify to obtain a stock font.
Value Meaning
ANSI_FIXED_FONT Specifies a monospace font based on the Windows character set. A Courier font is typically used.
ANSI_VAR_FONT Specifies a proportional font based on the Windows character set. MS Sans Serif is typically used.
DEVICE_DEFAULT_FONT Specifies the preferred font for the given device. This is typically the System font for display devices; however, for some dot-matrix printers this is a font that is resident on the device. (Printing with this font is usually faster than printing with a downloaded, bitmapped font).
OEM_FIXED_FONT Specifies a monospace font based on an OEM character set. For IBM® computers and compatibles, the OEM font is based on the IBM PC character set.
SYSTEM_FONT Specifies the System font. This is a proportional font based on the Windows character set, and is used by the operating system to display window titles, menu names, and text in dialog boxes. The System font is always available. Other fonts are available only if they have been installed.
SYSTEM_FIXED_FONT Specifies a monospace font compatible with the System font in Windows versions earlier than 3.0.

The following example retrieves a handle of the Windows variable stock font, selects it into a device context, and then writes a string using that font:
HFONT hfnt, hOldFont;

hfnt = GetStockObject(ANSI_VAR_FONT);
if (hOldFont = SelectObject(hdc, hfnt)) {
TextOut(hdc, 10, 50, "Sample ANSI_VAR_FONT text.", 26);
SelectObject(hdc, hOldFont);
}

If other stock fonts are not available, GetStockObject_win32_GetStockObject returns a handle to the System font (SYSTEM_FONT). You should use stock fonts only if the mapping mode for your application's device context is MM_TEXT.
+$#A Creating a Logical Font
You can use the Font common dialog box to display available fonts. The ChooseFont_win32_ChooseFont dialog box is displayed after an application initializes the members of a CHOOSEFONT_win32_CHOOSEFONT_str structure and calls the ChooseFont function. After the user chooses one of the available fonts and presses the OK button, the ChooseFont function initializes a LOGFONT_win32_LOGFONT_str structure with the relevant data. Your application can then call the CreateFontIndirect_win32_CreateFontIndirect function and create a logical font based on the user's request. The following example demonstrates how this is done.
HFONT FAR PASCAL MyCreateFont( void )
{

CHOOSEFONT cf;
LOGFONT lf;
HFONT hfont;

/* Initialize members of the CHOOSEFONT structure. */

cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = (HWND)NULL;
cf.hDC = (HDC)NULL;
cf.lpLogFont = &lf;
cf.iPointSize = 0;
cf.Flags = CF_SCREENFONTS;
cf.rgbColors = RGB(0,0,0);
cf.lCustData = 0L;
cf.lpfnHook = (LPCFHOOKPROC)NULL;
cf.lpTemplateName = (LPSTR)NULL;
cf.hInstance = (HINSTANCE) NULL;
cf.lpszStyle = (LPSTR)NULL;
cf.nFontType = SCREEN_FONTTYPE;
cf.nSizeMin = 0;
cf.nSizeMax = 0;

/* Display the CHOOSEFONT common-dialog box. */

ChooseFont(&cf);

/* Create a logical font based on the user's */
/* selection and return a handle identifying */
/* that font. */

hfont = CreateFontIndirect(cf.lpLogFont);
return (hfont);
}

+$#A Enumerating the Installed Fonts
In some instances, an application must be able to enumerate the available fonts and select the one most appropriate for a particular operation. An application can enumerate the available fonts by calling the EnumFonts_win32_EnumFonts or EnumFontFamilies_win32_EnumFontFamilies function. These functions send information about the available fonts to a callback function that the application supplies. The callback function receives information in LOGFONT_win32_LOGFONT_str and NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str structures. (The NEWTEXTMETRIC structure contains information about a TrueType font. When the callback function receives information about a non-TrueType font, the information is contained in a TEXTMETRIC_win32_TEXTMETRIC_str structure.) By using this information, an application can limit the user's choices to only those fonts that are available.
The EnumFontFamilies function is similar to the EnumFonts function but includes some extra functionality. EnumFontFamilies allows an application to take advantage of styles available with TrueType fonts. New and upgraded applications should use EnumFontFamilies instead of EnumFonts.
In previous versions of Windows, the only style attributes were weight and italic; any other styles were specified in the family name for the font. For example, when an application used the EnumFonts function to query the available Courier fonts, EnumFonts might return information for Courier, Courier Bold, Courier Bold Italic, and Courier Italic. It would not return information about any other Courier fonts that might be installed, because any other Courier fonts would typically have a different family name.
TrueType fonts are organized around a typeface name (for example, Courier New) and style names (for example, italic, bold, and extra-bold). The EnumFontFamilies function enumerates all the styles associated with a given family name, not simply the bold and italic attributes. For example, when the system includes a TrueType font called Courier New Extra-Bold, EnumFontFamilies lists it with the other Courier New fonts. The capabilities of EnumFontFamilies are helpful for fonts with many or unusual styles and for fonts that cross international borders.
If an application does not supply a typeface name, the EnumFonts_win32_EnumFonts and EnumFontFamilies_win32_EnumFontFamilies functions supply information about one font in each available family. To enumerate all the fonts in a device context, the application can specify NULL for the typeface name, compile a list of the available typefaces, and then enumerate each font in each typeface.
The following example uses the EnumFontFamilies function to retrieve the number of available raster, vector, and TrueType font families.
UINT uAlignPrev;
int aFontCount[] = { 0, 0, 0 };
char szCount[8];

EnumFontFamilies(hdc, (LPCTSTR) NULL,
(FONTENUMPROC) EnumFamCallBack, (LPARAM) aFontCount);

uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);

MoveToEx(hdc, 10, 50, (LPPOINT)NULL);
TextOut(hdc, 0, 0, "Number of raster fonts: ", 24);
itoa(aFontCount[0], szCount, 10);
TextOut(hdc, 0, 0, szCount, strlen(szCount));

MoveToEx(hdc, 10, 75, (LPPOINT)NULL);
TextOut(hdc, 0, 0, "Number of vector fonts: ", 24);
itoa(aFontCount[1], szCount, 10);
TextOut(hdc, 0, 0, szCount, strlen(szCount));

MoveToEx(hdc, 10, 100, (LPPOINT)NULL);
TextOut(hdc, 0, 0, "Number of TrueType fonts: ", 26);
itoa(aFontCount[2], szCount, 10);
TextOut(hdc, 0, 0, szCount, strlen(szCount));

SetTextAlign(hdc, uAlignPrev);
.
.
.

BOOL FAR PASCAL EnumFamCallBack(lplf, lpntm, FontType, aFontCount)
LPLOGFONT lplf;
LPNEWTEXTMETRIC lpntm;
DWORD FontType;
LPVOID aFontCount;
{
int far * aiFontCount = (int far *) aFontCount;

/*
* Record the number of raster, TrueType, and vector
* fonts in the font-count array.
*/

if (FontType & RASTER_FONTTYPE)
aiFontCount[0]++;
else if (FontType & TRUETYPE_FONTTYPE)
aiFontCount[2]++;
else
aiFontCount[1]++;

if (aiFontCount[0] || aiFontCount[1] || aiFontCount[2])
return TRUE;
else
return FALSE;

UNREFERENCED_PARAMETER( lplf );
UNREFERENCED_PARAMETER( lpntm );
}

This example uses two masks, RASTER_FONTTYPE and TRUETYPE_FONTTYPE, to determine the type of font being enumerated. If the RASTER_FONTTYPE bit is set, the font is a raster font. If the TRUETYPE_FONTTYPE bit is set, the font is a TrueType font. If neither bit is set, the font is a vector font. A third mask, DEVICE_FONTTYPE, is set when a device (for example, a laser printer) supports downloading TrueType fonts; it is zero if the device is a display adapter, dot-matrix printer, or other raster device. An application can also use the DEVICE_FONTTYPE mask to distinguish GDI-supplied raster fonts from device-supplied fonts. Windows can simulate bold, italic, underline, and strikeout attributes for GDI-supplied raster fonts, but not for device-supplied fonts.
An application can also check bits 1 and 2 in the tmPitchAndFamily member of the NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str structure to identify a TrueType font. If bit 1 is 0 and bit 2 is 1, the font is a TrueType font.
Vector fonts are categorized as OEM_CHARSET instead of ANSI_CHARSET. Some applications identify vector fonts by using this information, checking the tmCharSet member of the NEWTEXTMETRIC structure. This categorization usually prevents the font mapper from choosing vector fonts unless they are specifically requested. (Most applications no longer use vector fonts because their strokes are single lines and they take longer to draw than TrueType fonts, which offer many of the same scaling and rotation features that required vector fonts in earlier versions of Windows.)
+$#A Checking the Text Capabilities of a Device
You can use the EnumFonts_win32_EnumFonts and EnumFontFamilies_win32_EnumFontFamilies functions to enumerate the fonts that are available in a printer-compatible memory device context. You can also use the GetDeviceCaps_win32_GetDeviceCaps function to retrieve information about the text capabilities of a device. By calling the GetDeviceCaps function with the NUMFONTS index, you can determine the minimum number of fonts supported by a printer. (An individual printer may support more fonts than specified in the return value from GetDeviceCaps with the NUMFONTS index.) By using the TEXTCAPS index, you can identify many of the text capabilities of the specified device.
+$#A Setting the Text Alignment
You can query and set the text alignment for a device context by using the GetTextAlign_win32_GetTextAlign and SetTextAlign_win32_SetTextAlign functions. The text-alignment settings determine how text is positioned relative to a given location. Text can be aligned to the right or left of the position or centered over it; it can also be aligned above or below the point.
The following example shows a method for determining which horizontal alignment flag is set:
switch ((TA_LEFT | TA_RIGHT | TA_CENTER) & GetTextAlign(hdc))
{
case TA_LEFT:
.
.
.
case TA_RIGHT:
.
.
.
case TA_CENTER:
.
.
.
}

You can also use the SetTextAlign_win32_SetTextAlign function to update the current position when a text-output function is called. For instance, the following example uses the SetTextAlign function to update the current position when the TextOut_win32_TextOut function is called. In this example, the cArial parameter is an integer that specifies the number of Arial fonts.
UINT uAlignPrev;
char szCount[8];

uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);
MoveToEx(hdc, 10, 50, (LPPOINT) NULL);
TextOut(hdc, 0, 0, "Number of Arial fonts: ", 23);
itoa(cArial, szCount, 10);

TextOut(hdc, 0, 0, (LPSTR) szCount, strlen(szCount));
SetTextAlign(hdc, uAlignPrev);

+$#A Drawing Text From Different Fonts on the Same Line
Different type styles within a font family can have different widths. For example, bold and italic styles of a family are always wider than the roman style for a given point size. When you display or print several type styles on a single line, you must keep track of the width of the line to avoid having characters displayed or printed on top of one another.
You can use two functions to retrieve the width (or extent) of text in the current font. The GetTabbedTextExtent_win32_GetTabbedTextExtent function computes the width and height of a character string. If the string contains one or more tab characters, the width of the string is based upon a specified array of tab-stop positions. The GetTextExtentPoint32_win32_GetTextExtentPoint32 function computes the width and height of a line of text.
When necessary, Windows synthesizes a font by changing the character bitmaps. To synthesize a character in a bold font, Windows draws the character twice: once at the starting point, and again one pixel to the right of the starting point. To synthesize a character in an italic font, Windows draws two rows of pixels at the bottom of the character cell, moves the starting point one pixel to the right, draws the next two rows, and continues until the character has been drawn. By shifting pixels, each character appears to be sheared to the right. The amount of shear is a function of the height of the character.
One way to write a line of text that contains multiple fonts is to use the GetTextExtentPoint32 function after each call to TextOut_win32_TextOut and add the length to a current position. The following example writes the line "This is a sample string." using bold characters for "This is a", switches to italic characters for "sample", then returns to bold characters for "string." After printing all the strings, it restores the system default characters.
int XIncrement;
int YStart;
TEXTMETRIC tm;
HFONT hfntDefault, hfntItalic, hfntBold;
SIZE sz;
LPSTR lpszString1 = "This is a ";
LPSTR lpszString2 = "sample ";
LPSTR lpszString3 = "string.";

/* Create a bold and an italic logical font. */

hfntItalic = MyCreateFont();
hfntBold = MyCreateFont();

/* Select the bold font and draw the first string */
/* beginning at the specified point (XIncrement, YStart). */

XIncrement = 10;
YStart = 50;
hfntDefault = SelectObject(hdc, hfntBold);
TextOut(hdc, XIncrement, YStart, lpszString1,
lstrlen(lpszString1));

/*
* Compute the length of the first string and add
* this value to the x-increment that is used for the
* text-output operation.
*/

GetTextExtentPoint32(hdc, lpszString1,
lstrlen(lpszString1), &sz);
XIncrement += sz.cx;

/*
* Retrieve the overhang value from the TEXTMETRIC
* structure and subtract it from the x-increment.
* (This is only necessary for non-TrueType raster
* fonts.)
*/

GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;

/*
* Select an italic font and draw the second string
* beginning at the point (XIncrement, YStart).
*/

hfntBold = SelectObject(hdc, hfntItalic);
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
TextOut(hdc, XIncrement, YStart, lpszString2,
lstrlen(lpszString2));

/*
* Compute the length of the second string and add
* this value to the x-increment that is used for the
* text-output operation.
*/

GetTextExtentPoint32(hdc, lpszString2, lstrlen(lpszString2), &sz);
XIncrement += sz.cx;

/*
* Reselect the bold font and draw the third string
* beginning at the point (XIncrement, YStart).
*/

SelectObject(hdc, hfntBold);
TextOut(hdc, XIncrement - tm.tmOverhang, YStart, lpszString3,
lstrlen(lpszString3));

/* Reselect the original font. */

SelectObject(hdc, hfntDefault);

/* Delete the bold and italic fonts. */

DeleteObject(hfntItalic);
DeleteObject(hfntBold);

In this example, the GetTextExtentPoint32_win32_GetTextExtentPoint32 function initializes the members of a SIZE_win32_SIZE_str structure with the length and height of the specified string. The GetTextMetrics_win32_GetTextMetrics function retrieves the overhang for the current font. Because the overhang is zero if the font is a TrueType font, the overhang value does not change the string placement. For raster fonts, however, it is important to use the overhang value.
The overhang is subtracted from the bold string once, to bring subsequent characters closer to the end of the string if the font is a raster font. Because overhang affects both the beginning and end of the italic string in a raster font, the glyphs start at the right of the specified location and end at the left of the endpoint of the last character cell. (The GetTextExtentPoint32 function retrieves the extent of the character cells, not the extent of the glyphs.) To account for the overhang in the raster italic string, the example subtracts the overhang before placing the string and subtracts it again before placing subsequent characters.
The SetTextJustification_win32_SetTextJustification function adds extra space to the break characters in a line of text. You can use the GetTextExtentPoint_win32_GetTextExtentPoint function to determine the extent of a string, then subtract that extent from the total amount of space the line should occupy, and use the SetTextJustification function to distribute the extra space among the break characters in the string. The SetTextCharacterExtra_win32_SetTextCharacterExtra function adds extra space to every character cell in the selected font, including the break character. (You can use the GetTextCharacterExtra_win32_GetTextCharacterExtra function to determine the current amount of extra space being added to the character cells; the default setting is zero.)
You can place characters with greater precision by using the GetCharWidth32_win32_GetCharWidth32 or GetCharABCWidths_win32_GetCharABCWidths function to retrieve the widths of individual characters in a font. The GetCharABCWidths function is more accurate than the GetCharWidth32 function, but only when it is used with TrueType fonts; when you use GetCharABCWidths with non-TrueType fonts, it retrieves the same information as GetCharWidth32.
ABC spacing also allows an application to perform very accurate text alignment. For example, when the application right aligns a raster roman font without using ABC spacing, the advance width is calculated as the character width. This means the white space to the right of the glyph in the bitmap is aligned, not the glyph itself. By using ABC widths, applications have more flexibility in the placement and removal of white space when aligning text, because they have information that allows them to finely control intercharacter spacing.
+$#A Rotating Lines of Text
You can rotate TrueType fonts at any angle. This is useful for labeling charts and other illustrations. The following example rotates a string in 10-degree increments around the center of the client area by changing the value of the lfEscapement and lfOrientation members of the LOGFONT_win32_LOGFONT_str structure used to create the font.
RECT rc;
int angle;
HFONT hfnt, hfntPrev;
LPSTR lpszRotate = "String to be rotated.";

/* Allocate memory for a LOGFONT structure. */

PLOGFONT plf = (PLOGFONT) LocalAlloc(LPTR, sizeof(LOGFONT));

/* Specify a font typeface name and weight. */

lstrcpy(plf->lfFaceName, "Arial");
plf->lfWeight = FW_NORMAL;

/* Retrieve the client-rectangle dimensions. */

GetClientRect(hwnd, &rc);

/*
* Set the background mode to transparent for the
* text-output operation.
*/

SetBkMode(hdc, TRANSPARENT);

/*
* Draw the string 36 times, rotating 10 degrees
* counter-clockwise each time.
*/

for (angle = 0; angle < 3600; angle += 100) {
plf->lfEscapement = angle;
hfnt = CreateFontIndirect(plf);
hfntPrev = SelectObject(hdc, hfnt);
TextOut(hdc, rc.right / 2, rc.bottom / 2,
lpszRotate, lstrlen(lpszRotate));
SelectObject(hdc, hfntPrev);
DeleteObject(hfnt);
}

/* Reset the background mode to its default. */

SetBkMode(hdc, OPAQUE);

/* Free the memory allocated for the LOGFONT structure. */

LocalFree((LOCALHANDLE) plf);

+$#A Retrieving Character Outlines
You can use the GetGlyphOutline_win32_GetGlyphOutline function to retrieve the outline of a glyph from a TrueType font. The glyph outline returned by the GetGlyphOutline function is for a grid-fitted glyph. (A grid-fitted glyph has been modified so that its bitmap image conforms as closely as possible to the original design of the glyph.) If your application requires an unmodified glyph outline, request the glyph outline for a character in a font whose size is equal to the font's em units. (To create a font with this size, set the lfHeight member of the LOGFONT_win32_LOGFONT_str structure to the negative of the value of the ntmSizeEM member of the NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str structure.)
GetGlyphOutline returns the outline as a bitmap or as a series of polylines and splines. When an application retrieves a glyph outline as a series of polylines and splines, the information is returned in a TTPOLYGONHEADER_win32_TTPOLYGONHEADER_str structure followed by as many TTPOLYCURVE_win32_TTPOLYCURVE_str structures as required to describe the glyph. All points are returned as POINTFX_win32_POINTFX_str structures and represent absolute positions, not relative moves. The starting point given by the pfxStart member of the TTPOLYGONHEADER structure is the point where the outline for a contour begins. The TTPOLYCURVE structures that follow can be either polyline records or spline records.
To render a TrueType character outline in Windows, you must use both the polyline and the spline records. Windows can render both polylines and splines easily. Each polyline and spline record contains as many sequential points as possible, to minimize the number of records returned.
The starting point given in the TTPOLYGONHEADER structure is always on the outline of the glyph. The specified point serves as both the starting and ending points for the contour.
+$#A Polyline Records
Polyline_win32_Polyline records are a series of points; lines drawn between the points describe the outline of the character. A polyline record begins with the last point in the previous record (or, for the first record in the contour, the starting point). Each point in the record is on the glyph outline and can be connected simply by using straight lines.
+$#A Spline Records
Spline records represent the quadratic curves (that is, quadratic b-splines) used by TrueType. A spline record begins with the last point in the previous record (or for the first record in the contour, with the starting point). For the first spline record, the starting point and the last point in the record are on the glyph outline. For all other spline records, only the last point is on the glyph outline. All other points in the spline records are off the glyph outline and must be rendered as the control points of b-splines.
The last spline or polyline record in a contour always ends with the contour's starting point. This arrangement ensures that every contour is closed.
Because b-splines require three points (one point off the glyph outline between two points that are on the outline), you must perform some calculations when a spline record contains more than one off-curve point.
For example, if a spline record contains three points (A, B, and C) and it is not the first record, points A and B are off the glyph outline. To interpret point A, use the current position (which is always on the glyph outline) and the point on the glyph outline between points A and B. To find the midpoint (M) between A and B, you can perform the following calculation.
M = A + (B - A)/2
The midpoint between consecutive off-outline points in a spline record is a point on the glyph outline, according to the definition of the spline format used in TrueType fonts.
If the current position is designated by P, the two quadratic splines defined by this spline record are (P, A, M) and (M, B, C).
+$#A Using Portable TrueType Metrics
Applications that use the TrueType text metrics can achieve a high degree of printer and document portability; even applications that must maintain compatibility with earlier versions of Windows can use the TrueType metrics.
Design widths overcome most of the problems of device-dependent text introduced by physical devices. Design widths are a kind of logical width. Independent of any rasterization problems or scaling transformations, each glyph has a logical width and height. Composed to a logical page, each character in a string has a place that is independent of the physical device widths. Although a logical width implies that widths can be scaled linearly at all point sizes, this is not necessarily true for either nonportable or most TrueType fonts. At smaller point sizes, some glyphs are made wider relative to their height for better readability.
The characters in TrueType core fonts are designed against a 2048 by 2048 grid. The design width is the width of a character in these grid units. (TrueType supports any integer grid size up to 16,384 by 16,384; grid sizes that are integer powers of 2 scale faster than other grid sizes.)
The font outline is designed in notional units. The em square is the notional grid against which the font outline is fitted. (You can use the otmEMSquare member of OUTLINETEXTMETRIC_win32_OUTLINETEXTMETRIC_str and the ntmSizeEM member of NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str to retrieve the size of the em square in notional units.) When a font is created that has a point size (in device units) equal to the size of its em square, the ABC widths for this font are the desired design widths. For example, assume the size of an em square is 1000 and the ABC widths of a character in the font are 150, 400, and 150. A character in this font that is 10 device units high would have ABC widths of 1.5, 4, and 1.5, respectively. Since the MM_TEXT mapping mode is most commonly used with fonts (and MM_TEXT is equivalent to device units), this is a simple calculation.
Because of the high resolution of TrueType design widths, applications that use them must take into account the large numeric values that can be created.
+$#A Device vs. Design Units
An application can retrieve font metrics for a physical font only after the font has been selected into a device context. When a font is selected into a device context, it is scaled for the device. The font metrics specific to the device are known as device units.
Portable metrics in fonts are known as design units. To apply to a given device, design units must be converted to device units. Use the following formula to convert design units to device units.
DeviceUnits = (DesignUnits/unitsPerEm) * (PointSize/72) * DeviceResolution
The variables in this formula have the following meanings.
Variable Description
DeviceUnits Specifies the DesignUnits font metric converted to device units. This value is in the same units as the value given for DeviceResolution.
DesignUnits Specifies the font metric to be converted to device units. This value can be any font metric, including the width of a character or the ascender value for an entire font.
unitsPerEm Specifies the em square size for the font.
PointSize Specifies size of the font, in points. (One point equals 1/72 of an inch.)
DeviceResolution Specifies number of device units (pixels) per inch. Typical values might be 300 for a laser printer or 96 for a VGA screen.

This formula should not be used to convert device units back to design units. Device units are always rounded to the nearest pixel. The propagated round-off error can become very large, especially when an application is working with screen sizes.
To request design units, create a logical font whose height is specified as -unitsPerEm. Applications can retrieve the value for unitsPerEm by calling the EnumFontFamilies_win32_EnumFontFamilies function and checking the ntmSizeEM member of the NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str structure.
+$#A Metrics for Portable Documents
The following table specifies the most important font metrics for applications that require portable documents and the functions that allow an application to retrieve them.
Function Metric Use
EnumFontFamilies_win32_EnumFontFamilies ntmSizeEM Retrieval of design metrics; conversion to device metrics
GetCharABCWidths_win32_GetCharABCWidths ABCWidths Accurate placement of characters at the start and end of margins, picture boundaries, and other text breaks
GetCharWidth32_win32_GetCharWidth32 AdvanceWidths Placement of characters on a line
GetOutlineTextMetrics_win32_GetOutlineTextMetrics otmfsType Font-embedding bits
otmsCharSlopeRise Y-component for slope of cursor for italic fonts
otmsCharSlopeRun X-component for slope of cursor for italic fonts
otmAscent Line spacing
otmDescent Line spacing
otmLineGap Line spacing
otmpFamilyName Font identification
otmpStyleName Font identification
otmpFullName Font identification (typically, family and style name)

The otmsCharSlopeRise, otmsCharSlopeRun, otmAscent, otmDescent, and otmLineGap members of the OUTLINETEXTMETRIC_win32_OUTLINETEXTMETRIC_str structure are scaled or transformed to correspond to the current device mode and physical height (as given in the tmHeight member of the NEWTEXTMETRIC_win32_NEWTEXTMETRIC_str structure).
Font identification is important in those instances when an application must select the same font ¾ for example, when a document is reopened or moved to a different operating system. The font mapper always selects the correct font when an application requests a font by full name. The family and style names provide input to the standard font dialog box, which ensures that the selection bars are properly placed.
The otmsCharSlopeRise and otmsCharSlopeRun values are used to produce a close approximation of the main italic angle of the font. For typical roman fonts, otmsCharSlopeRise is 1 and otmsCharSlopeRun is 0. For italic fonts, the values attempt to approximate the sine and cosine of the main italic angle of the font (in counterclockwise degrees past vertical); note that the italic angle for upright fonts is 0. Because these values are not expressed in design u nits, they should not be converted into device units.
The character placement and line spacing metrics enable an application to compute device-independent line breaks that are portable across screens, printers, typesetters, and even platforms.
Device-independent page layout requires the following seven basic steps:
1. Normalize all design metrics to a common ultra-high resolution (UHR) value (for example, 65,536 DPI); this prevents round-off errors.
2. Compute line breaks based on UHR metrics and physical page width; this yields a starting point and an ending point of a line within the text stream.
3. Compute the device page width in device units (for example, pixels).
4. Fit each line of text into the device page width, using the line breaks computed in step 2.
5. Compute page breaks by using UHR metrics and the physical page length; this yields the number of lines per page.
6. Compute the line heights in device units.
7. Fit the lines of text onto the page, using the lines per page from step 5 and the line heights from step 6.

If all applications adopt these techniques, developers can virtually guarantee that documents moved from one application to another will retain their original appearance and format.
+$#A Using PANOSE Numbers
TrueType font files include PANOSE numbers, which applications can use to choose a font that closely matches their specifications. The PANOSE system classifies faces by 10 different attributes. For more information about these attributes, see the PANOSE_win32_PANOSE_str structure. A PANOSE structure is part of the OUTLINETEXTMETRIC_win32_OUTLINETEXTMETRIC_str structure (whose values are filled in by calling the GetOutlineTextMetrics_win32_GetOutlineTextMetrics function).
The PANOSE attributes are rated individually on a scale. The resulting values are concatenated to produce a number. Given this number for a font and a mathematical metric to measure distances in the PANOSE space, an application can determine the nearest neighbors.
+$#A Creating Customized Fonts
Windows keeps a font table containing all the fonts that applications can use. Windows chooses a font from this table when an application calls the CreateFont_win32_CreateFont or CreateFontIndirect_win32_CreateFontIndirect function. There can be up to 253 entries in the table.
A font resource is a group of individual fonts representing characters in a given character set that have various combinations of heights, widths, and pitches. You can load font resources and add the fonts in each resource to the operating system font table by using the AddFontResource_win32_AddFontResource function. To remove a font resource from the font table, you can use the RemoveFontResource_win32_RemoveFontResource function.
After you add a font resource to the font table, you can use the individual fonts in the resource. In other words, the CreateFont function takes the fonts into account when it tries to match a physical font to the specified logical font. (Fonts in the table are never directly accessible to an application. They are available only through the CreateFontIndirect and CreateFont functions, which return the handles of the fonts, not their memory addresses.)
Whenever your application adds or removes a font resource, it should inform all other applications of the change by sending a WM_FONTCHANGE_win32_WM_FONTCHANGE message to them. Use the following call to the SendMessage_win32_SendMessage function to send the message to all windows.
SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

You can use the GetProfileString_win32_GetProfileString function to search the [Fonts] section of the WIN.INI file for the list of fonts that the user has used Control Panel to install.
+$#A Creating Font Resources
A font resource file is an empty Windows dynamic-link library; it contains no code or data, but it does contain resources. You can create font resources by creating font files and adding them as resources to a font resource file. To create a font resource file, follow these steps:
1. Create the font files.
2. Create a resource-definition file for the font.
3. Create a dummy code module.
4. Create a module-definition file that describes the fonts and the devices that use the fonts.
5. Compile and link the source files.

You can add a font file to an empty library, along with such resources as icons, cursors, and menus, by using resource compiler.

Note A complete explanation of how to create a font resource file is beyond the scope of this book. For more information on this topic, see the documentation provided with your application development tools.

+$#A Installing and Using an Embedded Font
You must separate an embedded font from the containing document and install it in the user's operating system before Windows can use it. Although the exact procedure for separating the font from the document depends on the method used to embed it, your application should always perform these steps:
1. Resolve name conflicts before installing the font.
2. Write the font data to a file, decoding read-only fonts as necessary.
3. Use the CreateScalableFontResource_win32_CreateScalableFontResource function to create a font resource file for the unembedded font.

In Windows, only a TrueType font can be embedded.
Your application should avoid installing a font with the same name as a font that is already on the system. To determine whether there are duplicate style names, an application can compare the information returned by EnumFontFamilies_win32_EnumFontFamilies against the family name and style name stored with the embedded font.
Read-Write Permission
Embedded fonts that have read-write permission (and that therefore can be permanently installed on the user's system) should be written to a file that has the .TTF filename extension.
Most applications put the files for embedded fonts that have read-write permission into either the SYSTEM subdirectory of the user's Windows directory or into the application's working directory. Files for read-only embedded fonts are typically put into a temporary directory.
Before installing an embedded font, you must use the CreateScalableFontResource_win32_CreateScalableFontResource function to create a font resource file. Because Windows cannot directly interpret the native TrueType font file format, it requires a file that mimics the standard .FON file (called a .FOT file) to make internal bookkeeping and enumeration easier. The CreateScalableFontResource function produces a .FOT file that points to the TrueType font file. Once this .FOT file is produced, Windows applications can use TrueType fonts transparently by using the AddFontResource_win32_AddFontResource and RemoveFontResource_win32_RemoveFontResource functions. You can also use the CreateScalableFontResource function to install special fonts for logos, icons, and other graphics. Font resource files for read-only fonts should use a different extension (for example, .FOR) and should be hidden from other applications in the system by specifying 1 for the first parameter of CreateScalableFontResource.
Your application should offer users the option of permanently installing embedded fonts that have read-write permission. To permanently install a font, applications should concatenate the typeface and style names and then use the WriteProfileString_win32_WriteProfileString function to insert this string along with the .FOT file name in the [Fonts] section of the WIN.INI file. A typical font entry in the [Fonts] section looks like the following example.
Times New Roman Bold (TrueType)=TIMESBD.FOT

Read-Only Permission
Embedded fonts with read-only permission should not use the .TTF extension and should avoid the .FOT and .FON extensions. A typical filename extension for read-only embedded fonts is .TTR. Files for read-only embedded fonts must be removed from the operating system and from both physical and logical memory as soon as the containing document is closed, so their names do not need to be meaningful except to the application.
If a document contains one or more read-only embedded fonts, the user must not be permitted to edit the document. If the user is allowed to edit the document in any way, your application must first strip away and delete the read-only embedded fonts. As mentioned earlier, read-only embedded fonts must be removed from the operating system and memory immediately when the document in which they are bundled is closed.
To delete read-only embedded fonts, your application should follow these steps:
1. Call the RemoveFontResource_win32_RemoveFontResource function for each font to be deleted.
2. Delete the font resource file for each font.
3. Delete each TrueType font file for each font.

When an application creates a file for a read-only embedded font and specifies 1 for the first parameter of the CreateScalableFontResource_win32_CreateScalableFontResource function, the EnumFonts_win32_EnumFonts and EnumFontFamilies_win32_EnumFontFamilies functions will not enumerate this font. Hiding read-only embedded fonts in this manner makes it unlikely that another application can use them, even though Windows resources are theoretically available to all processes in Windows. The RemoveFontResource_win32_RemoveFontResource function does not delete a font currently in use. If your application uses a read-only embedded font installed by another application, it can be difficult for the installing application to delete the font. In this case, your application should delete the resource file and the TrueType font file when the user closes the document containing the read-only fonts.
It is very important that applications delete the TrueType font file for read-only embedded fonts. If the delete operation fails when the user closes the document, the application should periodically attempt to delete the file as the application runs, when it closes, and the next time it starts.
In some cases, an application is unable to delete a TrueType font file for a read-only embedded font because of external events (such as a system failure). There is no legal liability for events that are out of the control of the application.
+$#A Font and Text Functions
Following are the functions used with fonts.
AddFontResource_win32_AddFontResourceCreateFont_win32_CreateFontCreateFontIndirect_win32_CreateFontIndirectCreateScalableFontResource_win32_CreateScalableFontResourceDrawText_win32_DrawTextDrawTextEx_win32_DrawTextExEnumFontFamilies_win32_EnumFontFamiliesEnumFontFamProc_win32_EnumFontFamProcEnumFonts_win32_EnumFontsEnumFontsProc_win32_EnumFontsProcExtTextOut_win32_ExtTextOutGetAspectRatioFilterEx_win32_GetAspectRatioFilterExGetCharABCWidths_win32_GetCharABCWidthsGetCharABCWidthsFloat_win32_GetCharABCWidthsFloatGetCharacterPlacement_win32_GetCharacterPlacementGetCharWidth_win32_GetCharWidthGetCharWidth32_win32_GetCharWidth32GetCharWidthFloat_win32_GetCharWidthFloatGetFontData_win32_GetFontDataGetGlyphOutline_win32_GetGlyphOutlineGetKerningPairs_win32_GetKerningPairsGetOutlineTextMetrics_win32_GetOutlineTextMetricsGetRasterizerCaps_win32_GetRasterizerCapsGetTabbedTextExtent_win32_GetTabbedTextExtentGetTextAlign_win32_GetTextAlignGetTextCharacterExtra_win32_GetTextCharacterExtraGetTextColor_win32_GetTextColorGetTextExtentExPoint_win32_GetTextExtentExPointGetTextExtentPoint_win32_GetTextExtentPointGetTextExtentPoint32_win32_GetTextExtentPoint32GetTextFace_win32_GetTextFaceGetTextMetrics_win32_GetTextMetricsPolyTextOut_win32_PolyTextOutRemoveFontResource_win32_RemoveFontResourceSetMapperFlags_win32_SetMapperFlagsSetTextAlign_win32_SetTextAlignSetTextCharacterExtra_win32_SetTextCharacterExtraSetTextColor_win32_SetTextColorSetTextJustification_win32_SetTextJustificationTabbedTextOut_win32_TabbedTextOutTextOut_win32_TextOut
Obsolete or Deleted Functions
GetAspectRatioFilter_win32_GetAspectRatioFilterGetTextExtent_win32_GetTextExtent

+$#AK Lines and Curves
Lines and curves are used to draw graphics output on raster devices. As discussed in this overview, a line is a set of highlighted pixels on a raster display (or a set of dots on a printed page) identified by two points: a starting point and an ending point. A regular curve is a set of highlighted pixels on a raster display (or dots on a printed page) that defines the perimeter (or part of the perimeter) of a conic section. An irregular curve is a set of pixels that defines a curve that does not fit the perimeter of a conic section.
+$#A About Lines and Curves
All types of applications written for Microsoft® Windows® use lines and curves to draw graphics output on raster devices. Computer-aided design (CAD) and drawing applications use lines and curves to outline objects, specify the centers of objects, the dimensions of objects, and so on. Spreadsheet applications use lines and curves to draw grids, charts, and graphs. Word processing applications use lines to create rules and borders on a page of text.
+$#A Lines
A line is a set of highlighted pixels on a raster display (or a set of dots on a printed page) identified by two points: a starting point and an ending point. In Windows, the pixel located at the starting point is always included in the line, and the pixel located at the ending point is always excluded. (This kind of line is sometimes called inclusive-exclusive.)
When an application calls one of the Windows line-drawing functions, graphics device interface (GDI), or in some cases a device driver, determines which pixels should be highlighted. GDI is a dynamic-link library (DLL) that processes graphics function calls from a Windows-based application and passes those calls to a device driver. A device driver is a DLL that receives input from GDI, converts the input to device commands, and passes those commands to the appropriate device. GDI uses a digital differential analyzer (DDA) to determine the set of pixels that define a line. A DDA determines the set of pixels by examining each point on the line and identifying those pixels on the display surface (or dots on a printed page) that correspond to the points. The following illustration shows a line, its starting point, its ending point, and the pixels highlighted by using a simple DDA.
{ewc msdncd, EWGraphic, group10660 0 /a "SDK_01.bmp"}
The simplest and most common DDA is the Bresenham, or incremental, DDA. A modified version of this algorithm draws lines in Windows versions 3.x. The incremental DDA is noted for its simplicity, but it is also noted for its inaccuracy. Because it rounds off to the nearest integer value, it sometimes fails to represent the original line requested by the application. The Microsoft® Win32® DDA used by GDI does not round off to the nearest integer. As a result, this new DDA produces output that is sometimes much closer in appearance to the original line requested by the application.

Note If an application requires line output that cannot be achieved with the new DDA, it can draw its own lines by calling the LineDDA_win32_LineDDA function and supplying a private DDA. However, the LineDDA function draws lines much slower than the Windows line-drawing functions. Do not use this function within an application if speed is a primary concern.

An application can use the new DDA to draw single lines and multiple, connected line segments. An application can draw a single line by calling the LineTo_win32_LineTo function. This function draws a line from the current position up to, but not including, a specified ending point. An application can draw a series of connected line segments by calling the Polyline_win32_Polyline function, supplying an array of points that specify the ending point of each line segment. An application can draw multiple, disjointed series of connected line segments by calling the PolyPolyline_win32_PolyPolyline function, supplying the required ending points.
The following illustration shows line output created by calling the LineTo, Polyline, and PolyPolyline functions.
{ewc msdncd, EWGraphic, group10660 1 /a "SDK_02.bmp"}
+$#A Curves
A regular curve is a set of highlighted pixels on a raster display (or dots on a printed page) that define the perimeter (or part of the perimeter) of a conic section. An irregular curve is a set of pixels that define a curve that does not fit the perimeter of a conic section. In Windows, the ending point is excluded from a curve just as it is excluded from a line.
When an application calls one of the Windows curve-drawing functions, GDI breaks the curve into a number of extremely small, discrete line segments. After determining the endpoints (starting point and ending point) for each of these line segments, GDI determines which pixels (or dots) define each line by applying its DDA.
An application can draw an ellipse or part of an ellipse by calling the Arc_win32_Arc function. This function draws the curve within the perimeter of an invisible rectangle called a bounding rectangle. The size of the ellipse is specified by two invisible radials extending from the center of the rectangle to the sides of the rectangle. The following illustration shows an arc (part of an ellipse) drawn by using the Arc function.
{ewc msdncd, EWGraphic, group10660 2 /a "SDK_03.bmp"}
When calling the Arc function, an application specifies the coordinates of the bounding rectangle and radials. The preceding illustration shows the rectangle and radials with dashed lines while the actual arc was drawn using a solid line.
When drawing the arc of another object, the application can call the SetArcDirection_win32_SetArcDirection and GetArcDirection_win32_GetArcDirection functions to control the direction (clockwise or counterclockwise) in which the object is drawn. The default direction for drawing arcs and other objects is counterclockwise.
In addition to drawing ellipses or parts of ellipses, Windows-based applications can draw irregular curves called Bézier curves. A Bézier curve is an irregular curve whose curvature is defined by four control points (p1, p2, p3, and p4). The control points p1 and p4 define the starting and ending points of the curve, and the control points p2 and p3 define the shape of the curve by marking points where the curve reverses orientation.
{ewc msdncd, EWGraphic, group10660 3 /a "SDK_04.bmp"}
An application can draw irregular curves by calling the PolyBezier_win32_PolyBezier function, supplying the appropriate control points.
+$#A Combined Lines and Curves
In addition to drawing lines or curves, Windows-based applications can draw combinations of line and curve output by calling a single function. For example, an application can draw the outline of a pie chart by calling the AngleArc_win32_AngleArc function.
The AngleArc function draws an arc along a circle's perimeter and draws a line connecting the starting point of the arc to the circle's center. In addition to using the AngleArc function, a Windows-based application can also combine line and irregular curve output by using the PolyDraw_win32_PolyDraw function.
+$#A Line and Curve Attributes
A device context (DC) contains attributes that affect line and curve output. The line and curve attributes include the current position, brush style, brush color, pen style, pen color, transformation, and so on.
The default current position for any DC is located at the point (0,0) in logical (or world) space. You can set these coordinates to a new position by calling the MoveToEx_win32_MoveToEx function and passing a new set of coordinates.

Note Windows provides two sets of line- and curve-drawing functions. The first set retains the current position in a DC, and the second set alters the position. You can identify the functions that alter the current position by examining the function name. If the function name ends with the preposition "To", the function sets the current position to the ending point of the last line drawn (LineTo_win32_LineTo, ArcTo_win32_ArcTo, PolylineTo_win32_PolylineTo, or PolyBezierTo_win32_PolyBezierTo). If the function name does not end with this preposition, it leaves the current position intact (Arc_win32_Arc, Polyline_win32_Polyline, or PolyBezier_win32_PolyBezier).

The default brush is a solid white brush. An application can create a new brush by calling the CreateBrushIndirect_win32_CreateBrushIndirect function. After creating a brush, the application can select it into its DC by calling the SelectObject_win32_SelectObject function. Windows provides a complete set of functions to create, select, and alter the brush in an application's DC. For more information about these functions and about brushes in general, see Brushes_win32_Brushes.
The default pen is a cosmetic, solid black pen that is one pixel wide. An application can create a pen by using the ExtCreatePen_win32_ExtCreatePen function. After creating a pen, your application can select it into its DC by calling the SelectObject function. Windows provides a complete set of functions to create, select, and alter the pen in an application's DC. For more information about these functions and about pens in general, see Pens_win32_Pens.
The default transformation is the unity transformation (specified by the identity matrix). An application can specify a new transformation by calling the SetWorldTransform_win32_SetWorldTransform function. Windows provides a complete set of functions to transform lines and curves by altering their width, location, and general appearance. For more information about these functions, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.
+$#A Using Lines and Curves
You can use the line and curve functions to draw virtually any shape or object in the client area of an application window. This section illustrates how these functions can be used to draw markers or a pie chart.
· Drawing markers_win32_Drawing_Markers
· Drawing a pie chart_win32_Drawing_a_Pie_Chart

+$#A Drawing Markers
You can use the line functions to draw markers. A marker is a symbol centered over a point. Drawing applications use markers to designate starting points, ending points, and control points. Spreadsheet applications use markers to designate points of interest on a chart or graph.
In the following code sample, the application-defined Marker function creates a marker by using the MoveToEx_win32_MoveToEx and LineTo_win32_LineTo functions. These functions draw two intersecting lines, 20 pixels in length, centered over the cursor coordinates.
void Marker(LONG x, LONG y, HWND hwnd)
{
HDC hdc;

hdc = GetDC(hwnd);
MoveToEx(hdc, (int) x - 10, (int) y, (LPPOINT) NULL);
LineTo(hdc, (int) x + 10, (int) y);
MoveToEx(hdc, (int) x, (int) y - 10, (LPPOINT) NULL);
LineTo(hdc, (int) x, (int) y + 10);
ReleaseDC(hwnd, hdc);
}

Windows stores the coordinates of the cursor in the lParam parameter of the WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message when the user presses the left mouse button. The following code demonstrates how an application gets these coordinates, determines whether they lie within its client area, and passes them to the Marker function to draw the marker.
/* Line- and arc-drawing variables */

static BOOL bCollectPoints;
static POINT ptMouseDown[32];
static int index;
POINTS ptTmp;
RECT rc;

case WM_LBUTTONDOWN:

if (bCollectPoints && index < 32){

/* Create the region from the client area. */

GetClientRect(hwnd, &rc);
hrgn = CreateRectRgn(rc.left, rc.top,
rc.right, rc.bottom);

ptTmp = MAKEPOINTS((POINTS FAR *) lParam);
ptMouseDown[index].x = (LONG) ptTmp.x;
ptMouseDown[index].y = (LONG) ptTmp.y;

/* Test for a hit in the client rectangle. */

if (PtInRegion(hrgn, ptMouseDown[index].x,
ptMouseDown[index].y)) {

/* If a hit occurs, record the mouse coords. */

Marker(ptMouseDown[index].x, ptMouseDown[index].y,
hwnd);
index++;
}
}
break;

+$#A Drawing a Pie Chart
You can use the line and curve functions to draw a pie chart. The primary function used to draw pie charts is the AngleArc_win32_AngleArc function, which requires you to supply the coordinates of the center of the pie, the radius of the pie, a start angle, and a sweep angle. Following is a dialog box that the user can use to enter these values.
{ewc msdncd, EWGraphic, group10660 4 /a "SDK_05.bmp"}
The dialog box template found in the application's resource script (.RC) file specifies characteristics of the preceding dialog box (its height, the controls it contains, and its style), as follows.
AngleArc DIALOG 6, 18, 160, 100
STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Pie Chart"
FONT 8, "MS Sans Serif"
BEGIN
EDITTEXT IDD_X, 18, 22, 25, 12, ES_AUTOHSCROLL
LTEXT "X", 102, 4, 24, 9, 8
EDITTEXT IDD_Y, 18, 39, 25, 12, ES_AUTOHSCROLL
LTEXT "Y", 104, 5, 42, 12, 8
LTEXT "Center", 105, 19, 11, 23, 8
EDITTEXT IDD_RADIUS, 103, 9, 32, 12, ES_AUTOHSCROLL
EDITTEXT IDD_STARTANGLE, 103, 31, 32, 12, ES_AUTOHSCROLL
EDITTEXT IDD_SWEEPANGLE, 103, 53, 32, 12, ES_AUTOHSCROLL
LTEXT "Radius", 109, 73, 11, 25, 8
LTEXT "Start Angle", 110, 59, 33, 42, 8
LTEXT "Sweep Angle", 111, 55, 55, 43, 8
PUSHBUTTON "OK", IDD_OK, 9, 82, 40, 14
PUSHBUTTON "Cancel", IDD_CANCEL, 110, 82, 40, 14
END

The dialog box procedure, found in the application's source file, retrieves data (center coordinates, arc radius, and start and sweep angles) by following these steps:
1. The application-defined ClearBits function initializes the array that receives the user-input to zero.
2. The application-defined GetStrLngth function retrieves the length of the string entered by the user.
3. The application-defined RetrieveInput function retrieves the value entered by the user.

The following sample code shows the dialog box procedure.
BOOL CALLBACK ArcDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
CHAR chInput[4]; /* receives control-window input */
int cch; /* array-size and count variable */

switch (uMsg) {
case WM_INITDIALOG:
return FALSE;

case WM_COMMAND:
switch (wParam){

/*
* If the user pressed the OK button, retrieve the
* data that was entered in the various AngleArc
* controls.
*/

case IDD_OK:

/*
* Retrieve the x-coordinate of the arc's
* center.
*/

ClearBits(chInput, sizeof(chInput));
GetDlgItemText(hdlg, IDD_X, chInput,
sizeof(chInput));
cch = GetStrLngth(chInput);
nX = (int)RetrieveInput(chInput, cch);

/*
* Retrieve the y-coordinate of the arc's
* center.
*/

ClearBits(chInput, sizeof(chInput));
GetDlgItemText(hdlg, IDD_Y, chInput,
sizeof(chInput));
cch = GetStrLngth(chInput);
nY = (int)RetrieveInput(chInput, cch);

/* Retrieve the radius of the arc. */

ClearBits(chInput, sizeof(chInput));
GetDlgItemText(hdlg, IDD_RADIUS, chInput,
sizeof(chInput));
cch = GetStrLngth(chInput);
dwRadius = (DWORD) RetrieveInput(chInput, cch);

/* Retrieve the start angle. */

ClearBits(chInput, sizeof(chInput));
GetDlgItemText(hdlg, IDD_STARTANGLE, chInput,
sizeof(chInput));
cch = GetStrLngth(chInput);
xStartAngle = (float) RetrieveInput(chInput,
cch);

/* Retrieve the sweep angle. */

ClearBits(chInput, sizeof(chInput));
GetDlgItemText(hdlg, IDD_SWEEPANGLE, chInput,
sizeof(chInput));
cch = GetStrLngth(chInput);
xSweepAngle = (float) RetrieveInput(chInput,
cch);

EndDialog(hdlg, FALSE);
return TRUE;

/*
* If user presses the CANCEL button, close the
* dialog box.
*/

case IDD_CANCEL:
EndDialog(hdlg, FALSE);
return TRUE;
} /* end switch (wParam) */

break;

default:
return FALSE;
} /* end switch (message) */

UNREFERENCED_PARAMETER(lParam);
}

void ClearBits(LPTSTR cArray, int iLength)
{
int i;

for (i = 0; i < iLength; i++)
cArray[i] = 0;
}

int GetStrLngth(LPTSTR cArray)
{
int i = 0;

while (cArray[i++] != 0);
return i - 1;
}

DWORD RetrieveInput(LPTSTR cArray, int iLength)
{
int i, iTmp;
double dVal, dCount;

dVal = 0.0;
dCount = (double) (iLength - 1);

/* Convert ASCII input to a floating-point value. */

for (i = 0; i < iLength; i++) {
iTmp = cArray[i] - 0x30;
dVal = dVal + (((double)iTmp) * pow(10.0, dCount--));
}

return (DWORD) dVal;
}

To draw each section of the pie chart, pass the values entered by the user to the AngleArc_win32_AngleArc function. To fill the pie chart using the current brush, embed the call to AngleArc in a path bracket. The following code sample shows the defined path bracket and the call to AngleArc.
int nX;
int nY;
DWORD dwRadius;
float xStartAngle;
float xSweepAngle;

case (IDM_ANGLEARC):

DialogBox((HINSTANCE) GetModuleHandle (NULL),
(LPTSTR) "AngleArc",
hwnd, (DLGPROC) ArcDlgProc);

hdc = GetDC(hwnd);
BeginPath(hdc);
SelectObject(hdc,
GetStockObject(GRAY_BRUSH));
MoveToEx(hdc, nX, nY, (LPPOINT) NULL);
AngleArc(hdc, nX, nY, dwRadius,
xStartAngle, xSweepAngle);
LineTo(hdc, nX, nY);
EndPath(hdc);
StrokeAndFillPath(hdc);
ReleaseDC(hwnd, hdc);
break;

+$#A Line and Curve Functions
Following are the functions used with lines and curves.
AngleArc_win32_AngleArcArc_win32_ArcArcTo_win32_ArcToGetArcDirection_win32_GetArcDirectionLineDDA_win32_LineDDALineDDAProc_win32_LineDDAProcLineTo_win32_LineToMoveToEx_win32_MoveToExPolyBezier_win32_PolyBezierPolyBezierTo_win32_PolyBezierToPolyDraw_win32_PolyDrawPolyline_win32_PolylinePolylineTo_win32_PolylineToPolyPolyline_win32_PolyPolylineSetArcDirection_win32_SetArcDirection

+$#AK Metafiles
Applications written for the Microsoft® Windows® operating system use two graphics tools to store pictures: metafiles and bitmaps. For information about bitmaps, see Bitmaps_win32_Bitmaps. This overview describes metafiles.
+$#A About Metafiles
A metafile is a collection of structures that store a picture in a device-independent format. Device independence is the one feature that sets metafiles apart from bitmaps: unlike a bitmap, a metafile guarantees device independence. For example, when an application creates a picture measuring 2 by 2 inches on a VGA display and stores that picture in a metafile, the picture maintains its original dimensions when printed on a 300 dpi laser printer or copied over a network and displayed in another application that is running on an 8514/A video display. There is a drawback to metafiles, however; they are generally drawn more slowly than bitmaps. Therefore, if an application requires fast drawing and device independence is not an issue, it should use bitmaps instead of metafiles.
Internally, a metafile is an array of variable-length structures called metafile records. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw the picture. These records are stored in the metafile after a special metafile device context (DC) is created. This DC is then used for all drawing operations required to create the picture. When Windows processes a GDI function associated with a metafile DC, it converts the function into the appropriate data and stores this data in a record appended to the metafile.
After a picture is complete and the last record is stored in the metafile, the metafile can be passed to another application by means of the clipboard, embedded within another file, stored on disk, or played repeatedly. A metafile is played when its records are converted to device commands and processed by the appropriate device.
There are two types of metafiles: enhanced and Windows. An enhanced metafile is used in applications written using the Microsoft® Win32® application programming interface (API). The enhanced format consists of a header, a table of handles to GDI objects, a private palette, and an array of metafile records. Enhanced metafiles provide true device independence. (You can think of the picture stored in an enhanced metafile as a "snapshot" of the video display taken at a particular moment. This "snapshot" maintains its dimensions no matter where it appears: on a printer, a plotter, or the desktop, or in the client area of any Win32-based application.)
A Windows metafile is used for applications written using the Windows version 3.x API. The Windows format consists of a header and an array of metafile records. Windows-format metafiles are limited in their capabilities and should rarely be used ¾ the Windows-metafile API is supported to maintain backward compatibility with applications that were written to run with Windows version 3.x.
+$#A Enhanced-Format Metafiles
A developer can use enhanced metafiles to store a picture created by using the Win32 GDI functions (including new path and transformation functions). Because the enhanced metafile format is standardized for the Win32 API, pictures that are stored in this format can be copied from one Win32 application to another; and, because the pictures are truly device independent, they are guaranteed to maintain their shape and proportion on any output device.
+$#A Enhanced Metafiles
An enhanced metafile is an array of records. A metafile record is a variable-length ENHMETARECORD_win32_ENHMETARECORD_str structure. This structure identifies the record type, specifies the record length, and contains additional data that is dependent on the record type.
The first record in an enhanced metafile is always the enhanced-metafile header. The header specifies the following information.
· Size of the metafile, in bytes
· Dimensions of the picture frame, in device units
· Dimensions of the picture frame, in .01-millimeter units
· Number of records in the metafile
· Offset to an optional text description
· Size of the optional palette
· Resolution of the original device, in pixels
· Resolution of the original device, in millimeters

An optional text description can follow the header record. The text description describes the picture and the author's name. The optional palette specifies the colors used to create the enhanced metafile. The remaining records identify the GDI functions used to create the picture. The following hexadecimal output corresponds to a record generated for a call to the SetMapMode_win32_SetMapMode function.
00000011 0000000C 00000004

The value 0x00000011 specifies the record type (corresponds to the EMR_SETMAPMODE constant defined in the file WINGDI.H). The value 0x0000000C specifies the length of the record, in bytes. The value 0x00000004 identifies the mapping mode (corresponds to the MM_LOENGLISH constant defined in the file WINGDI.H).
+$#A Enhanced Metafile Operations
A developer creates an enhanced metafile by using the CreateEnhMetaFile_win32_CreateEnhMetaFile function and supplying the appropriate arguments. The Win32 API uses these arguments to maintain picture dimensions, to determine whether the metafile should be stored on a disk or in memory, and so on.
To maintain picture dimensions across output devices, the Win32 API requires the resolution of the reference device. This reference device is the device on which the picture first appeared, and the reference DC is the DC associated with the reference device. When calling the CreateEnhMetaFile function, the application must supply a handle that identifies this DC. The application can get this handle by calling the GetDC_win32_GetDC or CreateDC_win32_CreateDC function or specify NULL as the handle to use the current display device for the reference device.
Most applications store pictures permanently and therefore create an enhanced metafile that is stored on a disk; however, there are some instances when this is not necessary. For example, a word-processing application that provides chart-drawing capabilities could store a user-defined chart in memory as an enhanced metafile and then copy the enhanced metafile bits from memory into the user's document file. An application that requires a metafile that is stored permanently on a disk must supply the filename when it calls the CreateEnhMetaFile function. If the developer does not supply a filename, Windows automatically treats the metafile as a temporary file and stores it in memory.
The developer can add an optional text description to a metafile containing information about the picture and the author. An application can display these strings in the File Open dialog box to provide the user with information about metafile content that will help in selecting the appropriate file. If an application includes the text description, the application must supply a pointer to the string when it calls the CreateEnhMetaFile function.
When the CreateEnhMetaFile_win32_CreateEnhMetaFile function is successful, it returns a handle that identifies a special metafile DC. A metafile DC is unique in that it is associated with a file rather than with an output device. When Windows processes a GDI function that received a handle to a metafile DC, it converts the GDI function into an enhanced-metafile record and appends the record to the end of the enhanced metafile.
After a picture is complete and the last record is appended to the enhanced metafile, the application can close the file by calling the CloseEnhMetaFile_win32_CloseEnhMetaFile function. This function closes and deletes the special metafile DC and returns a handle identifying the enhanced metafile. The application can use this handle to accomplish the following tasks.
· Display the picture stored in the enhanced metafile.
· Create copies of the enhanced metafile.
· Enumerate, edit, or copy the individual records in the enhanced metafile.
· Retrieve the optional description that is stored in the enhanced metafile.
· Retrieve a copy of the enhanced-metafile header.
· Retrieve a binary version of the enhanced metafile.
· Enumerate the colors in the optional palette.
· Convert an enhanced-format metafile into a Windows-format metafile.

Some applications create temporary backup (or duplicate) copies of a file before enabling the user to alter the original. An application can create a backup copy of an enhanced metafile by calling the CopyEnhMetaFile_win32_CopyEnhMetaFile function, supplying a handle that identifies the enhanced metafile, and supplying a pointer to the name of the new file.
Most drawing, illustration, and computer-aided design (CAD) applications require a means of editing a picture stored in an enhanced metafile. Although editing an enhanced metafile is a complex task, a developer can use the EnumEnhMetaFile_win32_EnumEnhMetaFile function in combination with other Win32 functions to provide this capability in your application. The EnumEnhMetaFile function and its associated callback function enable the application to process individual records in an enhanced metafile.
Some applications display the text description of an enhanced metafile with the corresponding filename in the File Open dialog box. An application can determine whether this string exists in an enhanced metafile by retrieving the metafile header and examining one of its members. The GetEnhMetaFileHeader_win32_GetEnhMetaFileHeader function can retrieve the metafile header. If the string exists, the application retrieves it by calling the GetEnhMetaFileDescription_win32_GetEnhMetaFileDescription function.
Some applications retrieve the contents of a metafile by calling the GetEnhMetaFileBits_win32_GetEnhMetaFileBits function; however, before retrieving the contents, the application must specify the size of the file. To get the size, the application can use the GetEnhMetaFileHeader function and examine the appropriate member.
To achieve consistent colors when a picture is displayed on various output devices, an application can call the CreatePalette_win32_CreatePalette function and store a logical palette in an enhanced metafile. Other applications that display the picture stored in the enhanced metafile retrieve this palette and call the RealizePalette_win32_RealizePalette function before displaying the picture. To determine whether a palette is stored in an enhanced metafile, an application can retrieve the metafile header and examine the appropriate member. If a palette exists, the application can call the GetEnhMetaFilePaletteEntries_win32_GetEnhMetaFilePaletteEntries function to retrieve the logical palette.
+$#A Windows-Format Metafiles
The Win32 API supports the Windows metafile format to maintain compatibility with applications written for Windows version 3.x. Following are the limitations of this format.
· A Windows-format metafile is application and device dependent. Changes in the application's mapping modes or the device resolution affect the appearance of metafiles created in this format.
· A Windows-format metafile does not contain a comprehensive header that describes the original picture dimensions, the resolution of the device on which the picture was created, an optional text description, or an optional palette.
· A Windows-format metafile does not support the new curve, path, and transformation functions.
· Some Windows-format metafile records cannot be scaled.
· The metafile DC associated with a Windows-format metafile cannot be queried (that is, an application cannot retrieve device-resolution data, font metrics, and so on).

To convert a Windows-format metafile to an enhanced-format metafile, an application can call the GetMetaFileBitsEx_win32_GetMetaFileBitsEx function to retrieve the data from the Windows-format metafile and then call the SetWinMetaFileBits_win32_SetWinMetaFileBits function to convert this data into an enhanced-format metafile.
A developer writing a Win32-based application should avoid using the Windows-format functions and use the enhanced-format functions instead.
+$#A Using Metafiles
· Creating an enhanced metafile_win32_Creating_an_Enhanced_Metafile
· Displaying a picture and storing it in an enhanced metafile_win32_Displaying_a_Picture_and_Storing_It_in_an_Enhanced_Metafile
· Opening an enhanced metafile and displaying its contents_win32_Opening_an_Enhanced_Metafile_and_Displaying_Its_Contents
· Editing an enhanced metafile_win32_Editing_an_Enhanced_Metafile

+$#A Creating an Enhanced Metafile
This section contains an example that demonstrates the creation of an enhanced metafile that is stored on a disk, using a filename specified by the user.
The example uses a DC for the application window as the reference DC. (Windows stores the resolution data for this device in the enhanced-metafile's header.) The application retrieves a handle identifying this DC by calling the GetDC_win32_GetDC function.
The example uses the dimensions of the application's client area to define the dimensions of the picture frame. Using the rectangle dimensions returned by the GetClientRect_win32_GetClientRect function, the application converts the device units to .01-millimeter units and passes the converted values to the CreateEnhMetaFile_win32_CreateEnhMetaFile function.
The example displays a Save As common dialog box that enables the user to specify the filename of the new enhanced metafile. The system appends the three-character .EMF extension to this filename and passes the name to the CreateEnhMetaFile function.
The example also embeds a text description of the picture in the enhanced-metafile header. This description is specified as a resource in the string table of the application's resource file. However, in a working application, this string would be retrieved from a custom control in a common dialog box or from a separate dialog box displayed solely for this purpose.
/* Obtain a handle to a reference DC. */

hdcRef = GetDC(hWnd);

/*
* Determine the picture frame dimensions.
* iWidthMM is the display width in millimeters.
* iHeightMM is the display height in millimeters.
* iWidthPels is the display width in pixels.
* iHeightPels is the display height in pixels
*/

iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);
iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);
iWidthPels = GetDeviceCaps(hdcRef, HORZRES);
iHeightPels = GetDeviceCaps(hdcRef, VERTRES);

/*
* Retrieve the coordinates of the client
* rectangle, in pixels.
*/

GetClientRect(hWnd, &rect);

/*
* Convert client coordinates to .01-mm units.
* Use iWidthMM, iWidthPels, iHeightMM, and
* iHeightPels to determine the number of
* .01-millimeter units per pixel in the x-
* and y-directions.
*/

rect.left = (rect.left * iWidthMM * 100)/iWidthPels;
rect.top = (rect.top * iHeightMM * 100)/iHeightPels;
rect.right = (rect.right * iiWidthMM * 100)/iWidthPels;
rect.bottom = (rect.bottom * iHeightMM * 100)/iHeightPels;

/* Load the filename filter from the string table. */

LoadString(hInst, IDS_FILTERSTRING,
(LPSTR)szFilter, sizeof(szFilter));

/*
* Replace the '%' separators that are embedded
* between the strings in the string-table entry
* with '\0'.
*/

for (i=0; szFilter[i]!='\0'; i++)
if (szFilter[i] == '%')
szFilter[i] = '\0';

/* Load the dialog title string from the table. */

LoadString(hInst, IDS_TITLESTRING,
(LPSTR)szTitle, sizeof(szTitle));

/* Initialize the OPENFILENAME members. */

szFile[0] = '\0';

Ofn.lStructSize = sizeof(OPENFILENAME);
Ofn.hwndOwner = hWnd;
Ofn.lpstrFilter = szFilter;
Ofn.lpstrFile= szFile;
Ofn.nMaxFile = sizeof(szFile);
Ofn.lpstrFileTitle = szFileTitle;
Ofn.nMaxFileTitle = sizeof(szFileTitle);
Ofn.lpstrInitialDir = (LPSTR)NULL;
Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
Ofn.lpstrTitle = szTitle;

/*
* Display the Filename common dialog box. The
* filename specified by the user is passed
* to the CreateEnhMetaFile function and used to
* store the metafile on disk.
*/

GetSaveFileName(&Ofn);

/* Load the description from the string table. */

LoadString(hInst, IDS_DESCRIPTIONSTRING,
(LPSTR)szDescription, sizeof(szDescription));

/*
* Replace the '%' string separators that are
* embedded between strings in the string-table
* entry with '\0'.
*/

for (i=0; szDescription[i]!='\0'; i++)
if (szDescription[i] == '%')
szDescription[i] = '\0';

/* Create the metafile DC. */

hdcMeta = CreateEnhMetaFile(hdcRef,
(LPTSTR) Ofn.lpstrFile,
&rect, (LPSTR)szDescription);

if (!hdcMeta)
errhandler("CreateEnhMetaFile", hWnd);

/* Release the reference DC. */

ReleaseDC(hWnd, hdcRef);

+$#A Displaying a Picture and Storing It in an Enhanced Metafile
This section contains an example demonstrating the creation of a picture and the process of storing the corresponding records in a metafile. The example draws a picture to the display or stores it in a metafile. If a display DC handle is given, it draws a picture to the screen using various GDI functions. If an enhanced metafile DC is given, it stores the same picture in the enhanced metafile.
void DrawOrStore(HWND hwnd, HDC hdcMeta, HDC hdcDisplay)
{

RECT rect;
HDC hDC;
int fnMapModeOld;
HBRUSH hbrOld;

/* Draw it to the display DC or store it in the metafile DC. */

if (hdcMeta)
hDC = hdcMeta;
else
hDC = hdcDisplay;

/* Set the mapping mode in the DC. */

fnMapModeOld = SetMapMode(hDC, MM_LOENGLISH);

/* Find the midpoint of the client area. */

GetClientRect(hwnd, (LPRECT)&rect);
DPtoLP(hDC, (LPPOINT)&rect, 2);

/* Select a gray brush. */

hbrOld = SelectObject(hDC, GetStockObject(GRAY_BRUSH));

/* Draw a circle with a one inch raduis. */

Ellipse(hDC, (rect.right/2 - 100), (rect.bottom/2 + 100),
(rect.right/2 + 100), (rect.bottom/2 - 100));

/* Perform additional drawing here. */

/* Set the device context back to its original state. */

SetMapMode(hDC, fnMapModeOld);
SelectObject(hDC, hbrOld);
}

+$#A Opening an Enhanced Metafile and Displaying Its Contents
This section contains an example demonstrating how an application opens an enhanced metafile stored on disk and displays the associated picture in the client area.
The example uses the Open common dialog box to enable the user to select an enhanced metafile from a list of existing files. It then passes the name of the selected file to the GetEnhMetaFile_win32_GetEnhMetaFile function, which returns a handle identifying the file. This handle is passed to the PlayEnhMetaFile_win32_PlayEnhMetaFile function in order to display the picture.
LoadString(hInst, IDS_FILTERSTRING,
(LPSTR)szFilter, sizeof(szFilter));

/*
* Replace occurrences of '%' string separator
* with '\0'.
*/

for (i=0; szFilter[i]!='\0'; i++)
if (szFilter[i] == '%')
szFilter[i] = '\0';

LoadString(hInst, IDS_DEFEXTSTRING,
(LPSTR)szDefExt, sizeof(szFilter));

/*
* Use the OpenFilename common dialog box
* to obtain the desired filename.
*/

szFile[0] = '\0';
Ofn.lStructSize = sizeof(OPENFILENAME);
Ofn.hwndOwner = hWnd;
Ofn.lpstrFilter = szFilter;
Ofn.lpstrCustomFilter = (LPSTR)NULL;
Ofn.nMaxCustFilter = 0L;
Ofn.nFilterIndex = 1L;
Ofn.lpstrFile = szFile;
Ofn.nMaxFile = sizeof(szFile);
Ofn.lpstrFileTitle = szFileTitle;
Ofn.nMaxFileTitle = sizeof(szFileTitle);
Ofn.lpstrInitialDir = (LPSTR) NULL;
Ofn.lpstrTitle = (LPSTR)NULL;
Ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
Ofn.nFileOffset = 0;
Ofn.nFileExtension = 0;
Ofn.lpstrDefExt = szDefExt;

GetOpenFileName(&Ofn);

/* Open the metafile. */

hemf = GetEnhMetaFile(Ofn.lpstrFile);

/* Retrieve a handle to a window DC. */

hDC = GetDC(hWnd);

/* Retrieve the client rectangle dimensions. */

GetClientRect(hWnd, &rct);

/* Draw the picture. */

PlayEnhMetaFile(hDC, hemf, &rct);

/* Release the metafile handle. */

DeleteEnhMetaFile(hemf);

/* Release the window DC. */

ReleaseDC(hWnd, hDC);

+$#A Editing an Enhanced Metafile
To edit a picture stored in an enhanced metafile, an application must perform the following tasks.
1. Use hit-testing to capture the cursor coordinates and retrieve the position of the object (line, arc, rectangle, ellipse, polygon, or irregular shape) that the user wants to alter.
2. Convert these coordinates to logical (or world) units.
3. Call the EnumEnhMetaFile_win32_EnumEnhMetaFile function and examine each metafile record.
4. Determine whether a given record corresponds to a GDI drawing function.
5. If it does, determine whether the coordinates stored in the record correspond to the line, arc, ellipse, or other graphics element that intersects the coordinates specified by the user.
6. Upon finding the record that corresponds to the output that the user wants to alter, erase the object on the screen that corresponds to the original record.
7. Delete the corresponding record from the metafile, saving a pointer to its location.
8. Permit the user to redraw or replace the object.
9. Convert the GDI functions used to draw the new object into one or more enhanced-metafile records.
10. Store these records in the enhanced metafile.

+$#A Metafile Functions
Following are the functions used with enhanced format metafiles.
CloseEnhMetaFile_win32_CloseEnhMetaFileCopyEnhMetaFile_win32_CopyEnhMetaFileCreateEnhMetaFile_win32_CreateEnhMetaFileDeleteEnhMetaFile_win32_DeleteEnhMetaFileEnhMetaFileProc_win32_EnhMetaFileProcEnumEnhMetaFile_win32_EnumEnhMetaFileGdiComment_win32_GdiCommentGetEnhMetaFile_win32_GetEnhMetaFileGetEnhMetaFileBits_win32_GetEnhMetaFileBitsGetEnhMetaFileDescription_win32_GetEnhMetaFileDescriptionGetEnhMetaFileHeader_win32_GetEnhMetaFileHeaderGetEnhMetaFilePaletteEntries_win32_GetEnhMetaFilePaletteEntriesGetWinMetaFileBits_win32_GetWinMetaFileBitsPlayEnhMetaFile_win32_PlayEnhMetaFilePlayEnhMetaFileRecord_win32_PlayEnhMetaFileRecordSetEnhMetaFileBits_win32_SetEnhMetaFileBitsSetWinMetaFileBits_win32_SetWinMetaFileBits
The following functions are provided for compatibility with Windows-format metafiles:
CloseMetaFile_win32_CloseMetaFileCopyMetaFile_win32_CopyMetaFileCreateMetaFile_win32_CreateMetaFileDeleteMetaFile_win32_DeleteMetaFileEnumMetaFile_win32_EnumMetaFileEnumMetaFileProc_win32_EnumMetaFileProcGetMetaFile_win32_GetMetaFileGetMetaFileBitsEx_win32_GetMetaFileBitsExPlayMetaFile_win32_PlayMetaFilePlayMetaFileRecord_win32_PlayMetaFileRecordSetMetaFileBitsEx_win32_SetMetaFileBitsEx

+$#AK Paths
A path is one or more figures (or shapes) that are filled, outlined, or both filled and outlined. Applications written for Microsoft® Windows® use paths in many ways. Paths are used in drawing and painting applications. Computer-aided design (CAD) applications use paths to create unique clipping regions, to draw outlines of irregular shapes, and to fill the interiors of irregular shapes. An irregular shape is a shape composed of Bézier curves and straight lines. (A regular shape is an ellipse, a circle, a rectangle, or a polygon.)
+$#A About Paths
A path is one of the objects associated with a device context (DC). However, unlike the default objects (the pen, the brush, and the font) that are part of any new DC, there is no default path. For more information about DCs, see Device Contexts_win32_Device_Contexts.
To create a path and select it into a DC, it is first necessary to define the points that describe it. This is done by calling the BeginPath_win32_BeginPath function, specifying the appropriate drawing functions, and then by calling the EndPath_win32_EndPath function. This combination of functions (BeginPath, drawing functions, and EndPath) constitute a path bracket. The following functions can be used in a path bracket.
AngleArc_win32_AngleArc LineTo_win32_LineTo Polyline_win32_Polyline
Arc_win32_Arc MoveToEx_win32_MoveToEx PolylineTo_win32_PolylineTo
ArcTo_win32_ArcTo Pie_win32_Pie PolyPolygon_win32_PolyPolygon
Chord_win32_Chord PolyBezier_win32_PolyBezier PolyPolyline_win32_PolyPolyline
CloseFigure_win32_CloseFigure PolyBezierTo_win32_PolyBezierTo Rectangle_win32_Rectangle
Ellipse_win32_Ellipse PolyDraw_win32_PolyDraw RoundRect_win32_RoundRect
ExtTextOut_win32_ExtTextOut Polygon_win32_Polygon TextOut_win32_TextOut

When an application calls EndPath_win32_EndPath, Windows selects the associated path into the specified DC. (If another path had previously been selected into the DC, Windows deletes that path without saving it.) After Windows selects the path into the DC, an application can operate on the path in one of the following ways:
· Draw the outline of the path (using the current pen).
· Paint the interior of the path (using the current brush).
· Draw the outline and fill the interior of the path.
· Modify the path (converting curves to line segments).
· Convert the path into a clip path.
· Convert the path into a region.
· Flatten the path by converting each curve in the path into a series of line segments.
· Retrieve the coordinates of the lines and curves that compose a path.

+$#A Outlined and Filled Paths
An application can draw the outline of a path by calling the StrokePath_win32_StrokePath function, it can fill the interior of a path by calling the FillPath_win32_FillPath function, and it can both outline and fill the path by calling the StrokeAndFillPath_win32_StrokeAndFillPath function.
Whenever an application fills a path, Windows uses the DC's current fill mode. An application can retrieve this mode by calling the GetPolyFillMode_win32_GetPolyFillMode function, and it can set a new fill mode by calling the SetPolyFillMode_win32_SetPolyFillMode function. For a description of the two fill modes, see Regions_win32_Regions.
The following illustration shows the cross-section of an object created by a computer-aided design (CAD) application using paths that were both outlined and filled.
{ewc msdncd, EWGraphic, group10662 0 /a "SDK_01.bmp"}
+$#A Transformations of Paths
Paths are defined using logical units and current transformations. (If the SetWorldTransform_win32_SetWorldTransform function has been called, the logical units are world units; otherwise, the logical units are page units.) An application can use world transformations to scale, rotate, shear, translate, and reflect the lines and Bézier curves that define a path.

Note A world transformation within a path bracket only affects those lines and curves drawn after the transformation was set. It will have no affect on those lines and curves that were drawn before it was set. For a description of the world transformation, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.

An application can also use SetWorldTransform_win32_SetWorldTransform to outline the shape of the pen used to outline a path if the pen is a geometric pen. For a description of geometric pens, see Pens_win32_Pens.
+$#A Clip Paths and Graphic Effects
An application can use clipping and paths to create special graphic effects. The following illustration shows a string of text drawn with a large Arial font.
{ewc msdncd, EWGraphic, group10662 1 /a "SDK_02.bmp"}
The next illustration shows the result of selecting the text as a clip path and drawing radial lines for a circle whose center is located above and left of the string.
{ewc msdncd, EWGraphic, group10662 2 /a "SDK_03.bmp"}

Note Before graphics device interface (GDI) adds text created with a bitmapped font to a path, it converts the font to an outline or vector font.

An application creates a clip path by generating a path bracket and then calling the SelectClipPath_win32_SelectClipPath function. After a clip path is selected into a DC, output only appears within the borders of the path.
In addition to creating special graphics effects, clip paths are also useful in applications that save images as enhanced metafiles. By using a clip path, an application is able to ensure device independence because the units used to specify a path are logical units (as opposed to device units that are used to specify a region).
+$#A Conversion of Paths to Regions
An application can convert a path into a region by calling the PathToRegion_win32_PathToRegion function. Like SelectClipPath_win32_SelectClipPath, PathToRegion is useful in the creation of special graphics effects. For example, there are no functions that allow an application to offset a path; however, there is a function that enables an application to offset a region (OffsetRgn_win32_OffsetRgn). Using PathToRegion, an application can create the effect of animating a complex shape by creating a path that defines the shape, converting the path into a region (by calling PathToRegion), and then repeatedly painting, moving, and erasing the region (by calling functions in a sequence, such as FillRgn_win32_FillRgn, OffsetRgn, and FillRgn).
+$#A Curved Paths
An application can flatten the curves in a path by calling the FlattenPath_win32_FlattenPath function. This function is especially useful for applications that fit text onto the contour of a path which contains curves. To fit the text, the application must perform the following steps:
1. Create the path where the text appears.
2. Call the FlattenPath function to convert the curves in a path into line segments.
3. Call the GetPath_win32_GetPath function to retrieve those line segments.
4. Calculate the length of each line and the width of each character in the string.
5. Use line-width and character-width data to position each character along the curve.
+$#A Using Paths
This section contains a code sample that enables the user to select a font of a particular point size (by using the Choose Font dialog box), select a clip path (by using text drawn with this font), and then view the result of clipping to the text.
This code sample was used to create the illustration that appears in Clip Paths_win32_Clip_Paths.
CHOOSEFONT cf; /* common dialog box font structure */
LOGFONT lf; /* logical font structure */
HFONT hfont; /* new logical font handle */
HFONT hfontOld; /* original logical font handle */
HDC hdc; /* display DC handle */
int nXStart, nYStart; /* drawing coordinates */
RECT rc; /* rect structure for painting window */
SIZE sz; /* structure that receives text extents */
double aflSin[90]; /* sine of 0-90 degrees */
double aflCos[90]; /* cosine of 0-90 degrees */
double flRadius,a; /* radius of circle */
int iMode; /* clipping mode */
HRGN hrgn; /* clip region handle */

LRESULT APIENTRY MainWndProc(
HWND hwnd, /* window handle */
UINT message, /* type of message */
WPARAM wParam, /* additional information */
LPARAM lParam) /* additional information */

{

PAINTSTRUCT ps;

switch (message) {

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
break;

case WM_COMMAND: /* command from app's menu */
switch (wParam) {

case IDM_VANISH: /* erases client area */
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rc);
FillRect(hdc, &rc, GetStockObject(WHITE_BRUSH));
ReleaseDC(hwnd, hdc);
break;

case IDM_AND: /* sets clip mode to RGN_AND */
iMode = RGN_AND;
break;

case IDM_COPY: /* sets clip mode to RGN_COPY */
iMode = RGN_COPY;
break;

case IDM_DIFF: /* sets clip mode to RGN_DIFF */
iMode = RGN_DIFF;
break;

case IDM_OR: /* sets clip mode to RGN_OR */
iMode = RGN_OR;
break;

case IDM_XOR: /* sets clip mode to RGN_XOR */
iMode = RGN_XOR;
break;

case IDM_CLIP_PATH:

/* Retrieve a cached DC for the window. */

hdc = GetDC(hwnd);

/*
* Use the font requested by the user in the
* Choose Font dialog box to create a logical font,
* then select that font into the device context.
*/

hfont = CreateFontIndirect(cf.lpLogFont);
hfontOld = SelectObject(hdc, hfont);

/*
* Retrieve the dimensions of the rectangle
* that surrounds the text.
*/

GetTextExtentPoint32(hdc, "Clip Path", 9, &sz);

/*
* Set a clipping region using the rect that
* surrounds the text.
*/

hrgn = CreateRectRgn(nXStart, nYStart,
nXStart + sz.cx,
nYStart + sz.cy);

SelectClipRgn(hdc, hrgn);

/*
* Create a clip path using text drawn with
* the user's requested font.
*/

BeginPath(hdc);
TextOut(hdc, nXStart, nYStart, "Clip Path", 9);
EndPath(hdc);
SelectClipPath(hdc, iMode);

/* Compute the sine of 0, 1, ... 90 degrees. */
for (i = 0; i < 90; i++) {
aflSin[i] = sin( (((double)i) / 180.0)
* 3.14159);
}

/* Compute the cosine of 0, 1,... 90 degrees. */
for (i = 0; i < 90; i++) {
aflCos[i] = cos( (((double)i) / 180.0)
* 3.14159);
}

/* Set the radius value. */

flRadius = (double)(2 * sz.cx);

/*
* Draw the 90 rays extending from the
* radius to the edge of the circle.
*/

for (i = 0; i < 90; i++) {
MoveToEx(hdc, nXStart, nYStart,
(LPPOINT) NULL);
LineTo(hdc, nXStart + ((int) (flRadius
* aflCos[i])),
nYStart + ((int) (flRadius
* aflSin[i])));
}

/* Reselect the original font into the DC. */

SelectObject(hdc, hfontOld);

/* Delete the user's font. */

DeleteObject(hfont);

/* Release the DC. */

ReleaseDC(hwnd, hdc);

break;

case IDM_FONT:

/* Initialize necessary members. */

cf.lStructSize = sizeof (CHOOSEFONT);
cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
cf.rgbColors = RGB(0, 255, 255);
cf.nFontType = SCREEN_FONTTYPE;

/*
* Display the Font dialog box, allow the user
* to choose a font, and render text in the
* window with that selection.
*/

if (ChooseFont(&cf)) {
hdc = GetDC(hwnd);
hfont = CreateFontIndirect(cf.lpLogFont);
hfontOld = SelectObject(hdc, hfont);
crOld = SetTextColor(hdc, cf.rgbColors);
TextOut(hdc, nXStart, nYStart,
"Clip Path", 9);
SetTextColor(hdc, crOld);
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
ReleaseDC(hwnd, hdc);
}

break;

default:
return DefWindowProc(hwnd, message, wParam,
lParam);
}
break;

case WM_DESTROY: /* window is being destroyed */
PostQuitMessage(0);
break;

default: /* if unprocessed... */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}

+$#A Path Functions
Use the following functions to create, alter, or draw paths.
AbortPath_win32_AbortPathBeginPath_win32_BeginPathCloseFigure_win32_CloseFigureEndPath_win32_EndPathFillPath_win32_FillPathFlattenPath_win32_FlattenPathGetMiterLimit_win32_GetMiterLimitGetPath_win32_GetPathPathToRegion_win32_PathToRegionSetMiterLimit_win32_SetMiterLimitStrokeAndFillPath_win32_StrokeAndFillPathStrokePath_win32_StrokePathWidenPath_win32_WidenPath

+$#AK Pens
A pen is a graphics tool that an application for Microsoft® Windows® uses to draw lines and curves. Drawing applications use pens to draw freehand lines, straight lines, and curves. Computer-aided design (CAD) applications use pens to draw visible lines, hidden lines, section lines, center lines, and so on. Word processing and desktop publishing applications use pens to draw borders and rules. Spreadsheet applications use pens to designate trends in graphs and to outline bar graphs and pie charts.
+$#A About Pens
There are two types of pen: cosmetic and geometric. A cosmetic pen_win32_Cosmetic_Pens is used with applications requiring lines of fixed width and lines that are quickly drawn. A CAD application, for example, uses a cosmetic pen to generate hidden, section, center, and dimension lines that are between .015 and .022 inches wide ¾ regardless of the scale factor. A geometric pen_win32_Geometric_Pens is used with applications requiring scalable lines, lines with unique end or join styles, and lines that are wider than a single pixel. A spreadsheet application, for example, uses a geometric pen to define each of the bars in a bar graph as a wide line.
+$#A Cosmetic Pens
The dimensions of a cosmetic pen are specified in device units. Therefore, lines drawn with a cosmetic pen always have a fixed width. Lines drawn with a cosmetic pen are generally drawn three to ten times faster than lines drawn with a geometric pen. Cosmetic pens have three attributes: width, style, and color. For more information about these attributes, see Pen Attributes_win32_Pen_Attributes.
To create a cosmetic pen, an application uses the CreatePen_win32_CreatePen, CreatePenIndirect_win32_CreatePenIndirect, or ExtCreatePen_win32_ExtCreatePen function. To retrieve one of the three stock cosmetic pens managed by window manager, the application uses the GetStockObject_win32_GetStockObject function.
After a pen is created (or a handle identifying one of the stock pens is obtained), the pen is selected into the application's device context (DC) by calling the SelectObject_win32_SelectObject function. From this point on, the application uses this pen for any line-drawing operations in its client area.
+$#A Geometric Pens
The dimensions of a geometric pen are specified in logical units. Therefore, lines drawn with a geometric pen can be scaled ¾ that is, they may appear wider or narrower, depending on the current world transformation. For more information about the world transformation, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.
In addition to the three attributes shared with cosmetic pens (width, style, and color), geometric pens possess the following four attributes: pattern, optional hatch, end style, and join style. For more information about these attributes, see Pen Attributes_win32_Pen_Attributes.
To create a geometric pen, an application uses the ExtCreatePen_win32_ExtCreatePen function. As with cosmetic pens, the SelectObject_win32_SelectObject function selects a geometric pen into the application's DC.
+$#A Pen Attributes
There are seven pen attributes that define the type of pen and its characteristics: width, style, color, pattern, hatch, end style, and join style. Both cosmetic and geometric pens have the width, style, and color attributes. Only geometric pens have the pattern, hatch, end style, and join style attributes. The pattern and optional hatch attribute are usually associated with a brush, but can also be used with geometric pens.
+$#A Width
The width attribute specifies a cosmetic pen width in device units. When used with a geometric pen, however, it specifies the pen's width in logical units. For more information about device units, see Coordinate Spaces and Transformations_win32_Coordinate_Spaces_and_Transformations.
Currently, Windows limits the width of cosmetic pens to a single pixel; however, future versions may remove this limitation.
+$#A Style
The style attribute specifies the line pattern that appears when a particular cosmetic or geometric pen is used. There are eight predefined pen styles. The following illustration shows the seven of these styles that are defined by Windows.
{ewc msdncd, EWGraphic, group10663 0 /a "SDK_01.bmp"}
The inside-frame style is identical to the solid style for cosmetic pens. However, it operates differently when used with a geometric pen. If the geometric pen is wider than a single pixel and a drawing function uses the pen to draw a border around a filled object, Windows draws the border inside the object's frame. By using the inside-frame style, an application can ensure that an object appears entirely within the specified dimensions, regardless of the geometric pen width.
In addition to the seven styles defined by Windows, there is an eighth style that is user (or application) defined. A user-defined style generates lines with a customized series of dashes and dots.
Use the CreatePen_win32_CreatePen, CreatePenIndirect_win32_CreatePenIndirect, or ExtCreatePen_win32_ExtCreatePen function to create a pen that has the Windows-defined styles. Use the ExtCreatePen function to create a pen that has a user-defined style.
+$#A Color
The color attribute specifies the pen's color. An application can create a cosmetic pen with a unique color by storing the red, green, blue (RGB_win32_RGB) triplet that specifies the desired color in a COLORREF_win32_COLORREF_str structure and passing this structure's address to the CreatePen_win32_CreatePen, CreatePenIndirect_win32_CreatePenIndirect, or ExtCreatePen_win32_ExtCreatePen function. (The stock pens are limited to black, white, and invisible.) For more information about RGB triplets and color, see Colors_win32_Colors.
+$#A Pattern
The pattern attribute specifies the pattern of a geometric pen.
The following illustration shows lines drawn with different geometric pens. Each pen was created using a different pattern attribute.
{ewc msdncd, EWGraphic, group10663 1 /a "SDK_02.bmp"}
The first line in the previous illustration is drawn using one of the six available hatch patterns; for more information about hatch patterns, see Hatch_win32_Hatch. The next line is drawn using the hollow pattern, identical to the null pattern. The third line is drawn using a custom pattern created from an 8- by 8-pixel bitmap. (For more information about bitmaps and their creation, see Bitmaps_win32_Bitmaps.) The last line is drawn using a solid pattern. Creating a brush and passing its handle to the ExtCreatePen_win32_ExtCreatePen function creates a pattern.
+$#A Hatch
The hatch attribute specifies the hatch type of a geometric pen with the hatch pattern attribute. There are six patterns available. The following illustration shows lines drawn using different hatch patterns.
{ewc msdncd, EWGraphic, group10663 2 /a "SDK_03.bmp"}
+$#A End Cap
The end cap attribute specifies the shape of a geometric pen: round, square, or flat. The following illustration shows parallel lines drawn using each type of end cap.
{ewc msdncd, EWGraphic, group10663 3 /a "SDK_04.bmp"}
The round and square end caps extend past the starting and ending points of a line drawn with a geometric pen; the flat end cap does not.
+$#A Join
The join attribute specifies how the ends of two geometric lines are joined: beveled, mitered, or round. The following illustration shows pairs of connected lines drawn using each type of join.
{ewc msdncd, EWGraphic, group10663 4 /a "SDK_05.bmp"}
+$#A Using Pens
This section contains sample code that demonstrates the appearance of lines drawn using various pen styles and attributes.
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
PAINTSTRUCT ps;
LOGBRUSH lb;
RECT rc;
HDC hdc;
int i;
HPEN hPen, hPenOld;
DWORD dwPenStyle[] = {
PS_DASH,
PS_DASHDOT,
PS_DOT,
PS_INSIDEFRAME,
PS_NULL,
PS_SOLID
};
UINT uHatch[] = {
HS_BDIAGONAL,
HS_CROSS,
HS_DIAGCROSS,
HS_FDIAGONAL,
HS_HORIZONTAL,
HS_VERTICAL
};

switch (uMsg)
{
case WM_PAINT:
{
GetClientRect(hWnd, &rc);
rc.left += 10;
rc.top += 10;
rc.bottom -= 10;

/* Initialize the pen's "brush." */
lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(255,0,0);
lb.lbHatch = 0;

hdc = BeginPaint(hWnd, &ps);
for (i = 0; i < 6; i++)
{
hPen = ExtCreatePen(PS_COSMETIC | dwPenStyle[i],
1, &lb, 0, NULL);
hPenOld = SelectObject(hdc, hPen);
MoveToEx(hdc, rc.left + (i * 20), rc.top, NULL);
LineTo(hdc, rc.left + (i * 20), rc.bottom);
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
}
rc.left += 150;
for (i = 0; i < 6; i++)
{
lb.lbStyle = BS_HATCHED;
lb.lbColor = RGB(0,0,255);
lb.lbHatch = uHatch[i];
hPen = ExtCreatePen(PS_GEOMETRIC,
5, &lb, 0, NULL);
hPenOld = SelectObject(hdc, hPen);
MoveToEx(hdc, rc.left + (i * 20), rc.top, NULL);
LineTo(hdc, rc.left + (i * 20), rc.bottom);
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
}
EndPaint(hWnd, &ps);

}
break;

case WM_DESTROY:
DeleteObject(hPen);
PostQuitMessage(0);
break;

default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

return FALSE;
}

+$#A Pen Functions
Following are the functions that support the pen interface.
CreatePen_win32_CreatePenCreatePenIndirect_win32_CreatePenIndirectExtCreatePen_win32_ExtCreatePen

+$#AK Printing and Print Spooler
Microsoft® Windows® provides a complete set of functions that allow applications to print on a variety of devices: laser printers, vector plotters, raster printers, and fax machines.
+$#A About Printing
One of the chief features of Windows printing functions is their support of device independence; instead of issuing device-specific commands to draw output on a particular printer or plotter, an application calls high-level functions from graphics device interface (GDI). For example, to print a bitmapped image, an application can call the BitBlt_win32_BitBlt function, supplying the coordinates for the bitmap as well as handles identifying the source and destination device contexts (DCs). The call to BitBlt is then converted to raw device commands by a printer device driver. A device driver is a Windows dynamic-link library (DLL) that supports the Windows device driver interface (DDI). A device driver generates raw device commands when it processes calls to DDI functions made by the graphics engine. The commands are processed by the printer when it prints the image. The syntax, number, and type of these commands vary from device to device.
+$#A Default Printing Interface
In addition to GDI and the device driver, the default printing interface consists of several other components that process output before it arrives at the printer. These components are the print spooler, the print processor, the graphics engine, and the monitor.
+$#A Print Spooler
The primary component of the printing interface is the print spooler. The print spooler is a Windows executable file that manages the printing process. Management of printing involves retrieving the location of the correct printer driver, loading that driver, converting high-level function calls to journal records, storing those journal records on disk as a print job, and so on.
The spooler is loaded at startup and continues to run until the operating system is shut down. Windows Print Manager provides a graphical interface that the user or system administrator can use to access and configure the spooler; however, if Print Manager is disabled or shut down, the spooler continues to run.
Applications that print create a printer DC. When an application creates a printer DC, the spooler performs necessary tasks such as determining the location of the required printer driver and then loading the appropriate printer driver. It also determines the data type used to record the print job. The supported data types include journal records, ASCII text, and PostScript®. A print job is a document stored internally (by using one of the supported data types) that may contain one or more pages of output. It may consist of multiple forms; for example, a job may consist of one envelope and three pages of A4 paper. A print job is defined (or bracketed) by the StartDoc_win32_StartDoc and EndDoc_win32_EndDoc functions.
The default data type for a print job is the journal record. A journal record is a compact structure used to store text output commands, raster graphics commands, and so on. When an application calls StartDoc, the spooler creates a journal file and a data file and begins storing journal records in the journal file. Each time the application calls one of the GDI drawing functions, one or more new journal records are created and stored in the journal file. The journal and data files are created in an operating system directory. The spooler uses the journal file to store journal records and uses the data file to record the type of form, the data type for the print job, the target printer, and so on. These files are deleted by the spooler when the job has successfully printed.
+$#A Print Processor
The spooler monitors the current print jobs and the target printer to determine an appropriate time to print a job. Once the spooler determines that a job should be printed, it calls the print processor. The print processor is a Windows DLL that reads and converts journal records into DDI calls.
+$#A Graphics Engine
The graphics engine is a Windows DLL that converts print processor output into calls to device driver functions. The device driver, in turn, processes these calls and converts them into raw device commands that the device can process.
+$#A Monitor
Once a device driver has converted an entire journal file into raw device commands, the file of converted commands is passed back to the spooler. The spooler sends these low-level commands to a monitor. A monitor is a Windows DLL that passes the raw device commands over the network, through a parallel port, or through a serial port to the device.
+$#A Printer Device Contexts Overview
Just as an application requires a display DC before it can begin drawing in the client area of a window, it needs a special printer DC before it can begin sending output to a printer. A printer DC is similar to a display DC in that it is an internal data structure that defines a set of graphic objects and their associated attributes and specifies the graphic modes that affect output. The graphic objects include a pen (for line drawing), a brush (for painting and filling), and a font (for text output).
Unlike a display DC, printer DCs are not owned by the window management component, and they cannot be obtained by calling the GetDC_win32_GetDC function. Instead, an application must call the CreateDC_win32_CreateDC or PrintDlg_win32_PrintDlg function. When an application calls CreateDC, it must supply a driver and port name. This data is stored in one of the operating system initialization (.INI) files. It can be retrieved by calling the EnumPrinters_win32_EnumPrinters function.
When an application calls PrintDlg and specifies the PD_RETURNDC value in the Flags member of the PRINTDLG_win32_PRINTDLG_str structure, Windows automatically returns a handle identifying the user's selected printer DC. For an example that demonstrates this task, see Using a Print Dialog Box and Retrieving a Printer Device Context!AL(`_win32_Using_a_Print_Dialog_Box_and_Retrieving_a_Printer_Device_Context',3,`No_SDK').
+$#A Printer Escapes
Windows version 3.x supported as many as 64 special functions called printer escapes that applications used to access special device features. An application called these functions by specifying one of 64 predefined values as the second parameter to the Escape function. For example, one of these applications printing on a PostScript printer could draw a PostScript graphic object called a path by calling the Escape function and supplying the BEGIN_PATH and END_PATH values. Most of these escapes are obsolete in the Microsoft® Win32® application programming interface (API) but are provided to simplify the porting of 16-bit Windows-based applications. The Win32 version of GDI supports a complete set of path functions that applications can use instead of the escapes to draw paths on any device. For a list of the Win32 functions that replace some of the escapes, see the Escape_win32_Escape_v3 function.
Of the 64 original printer escapes, only the QUERYESCSUPPORT and the PASSTHROUGH escapes can be used by Win32-based applications:
Applications written for Windows 3.x can also use QUERYESCSUPPORT and PASSTHROUGH as well as the following 10 escapes. Note that the following escapes are only supported for backwards compatibility with Windows 3.x-based applications. The Escape function may fail if a Win32-based application uses any of these.
· ABORTDOC
· ENDDOC
· GETPHYSPAGESIZE
· GETPRINTINGOFFSET
· GETSCALINGFACTOR
· NEWFRAME
· NEXTBAND
· SETABORTPROC
· SETCOPYCOUNT
· STARTDOC
In addition to supporting Escape_win32_Escape_v3, the Win32 API provides a new extended escape function called ExtEscape_win32_ExtEscape. This function allows applications to access capabilities of a particular device not directly available through GDI.
+$#A WYSIWYG Display and Output
Most applications attempt to support WYSIWYG (what you see is what you get) output. This means that text drawn with a 10-point Helvetica bold font in the application window should have a similar appearance when it is printed. Obtaining true WYSIWYG output is virtually impossible and even undesirable in most cases. This is due, in part, to the differences in video and printer technologies; a pixel on a screen is generally larger than a dot on a common laser printer. Viewing distances are different as well; a computer user typically sits about two feet away from the screen, but a reader's eyes are usually one foot or less from the printed page.
To compensate for legibility differences between screens and the printed page, Windows supports a unit called the logical inch that is always specified in pixels. For a video display, the logical inch is always greater than the physical inch to compensate for the longer viewing distance and the (generally) coarser resolution. For printers, the logical inch is always equal to the physical inch.
To obtain a WYSIWYG effect when drawing text, an application can use the CreateFont_win32_CreateFont function to specify the typeface name and point size of an ideal (or logical) font and then call the SelectObject_win32_SelectObject function to identify the display or printer DC. When the application calls SelectObject, Windows selects a physical font that is the closest possible match to the specified logical font. When Windows selects the display font, it chooses a physical font that is larger than the actual point size. From the user's perspective, however, it appears to be very close to the correct height. When Windows selects the font for the printer, it chooses a physical font that is actually the requested point size. For more information about fonts and text output, see Fonts and Text_win32_Fonts_and_Text.
To obtain a WYSIWYG effect when drawing bitmapped graphics, an application can retrieve the width and height, in logical inches, of the screen and the printed page. Using these values, the application can create horizontal and vertical scaling factors to maintain the proportion of bitmapped images when they are drawn on a printer. For an example that demonstrates this process, see WYSIWYG Display and Output_win32_WYSIWYG_Display_and_Output. For more information about bitmaps and bitmap output, see Bitmaps_win32_Bitmaps.
+$#A Using the Printing Functions
This section contains examples that demonstrate how to print text and graphics. The code was taken from an application that allows a user to open and display a 16-color bitmapped image that is stored as a .BMP file.
In addition to displaying the bitmapped image, the application allows the user to configure the printer and then print the image and a string of text specifying the location of the file that contained the image. The application prints the text at the top of the page and the image in the center of the page.
· Opening and displaying a .BMP file_win32_Opening_and_Displaying_a_BMP_File
· Using a print dialog box and retrieving a printer device context!AL(`_win32_Using_a_Print_Dialog_Box_and_Retrieving_a_Printer_Device_Context',3,`No_SDK')
· Preparing to print_win32_Preparing_to_Print
· Printing a document_win32_Printing_a_Document

+$#A Opening and Displaying a .BMP File
In the sample application, a user is able to open a .BMP file that contains a bitmapped image and display that image in the client area of the application's window. The user selects the file to be opened when the application displays the Open dialog box. (For more information about the Open dialog box, see Common Dialog Box Library_win32_Common_Dialog_Box_Library.)
After the user selects a file and closes the dialog box, the file and path names are stored in members of the OPENFILENAME_win32_OPENFILENAME_str structure. The application uses this data to open the appropriate file and retrieve the bitmap header and data. The following code sample can be used to retrieve this data.
/* Retrieve a handle identifying the file. */

hfbm = CreateFile(ofn.lpstrFile, GENERIC_READ,
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
(HANDLE) NULL);

/* Retrieve the BITMAPFILEHEADER structure. */

ReadFile(hfbm, &bmfh, sizeof(BITMAPFILEHEADER),
&dwRead, (LPOVERLAPPED)NULL);

/* Retrieve the BITMAPFILEHEADER structure. */

ReadFile(hfbm, &bmih, sizeof(BITMAPINFOHEADER),
&dwRead, (LPOVERLAPPED)NULL);

/* Allocate memory for the BITMAPINFO structure. */

hmem1 = GlobalAlloc(GHND,
sizeof(BITMAPINFOHEADER) +
((1<<bmih.biBitCount) * sizeof(RGBQUAD)));

lpbmi = GlobalLock(hmem1);

/*
* Load BITMAPINFOHEADER into the BITMAPINFO
* structure.
*/

lpbmi->bmiHeader.biSize = bmih.biSize;
lpbmi->bmiHeader.biWidth = bmih.biWidth;
lpbmi->bmiHeader.biHeight = bmih.biHeight;
lpbmi->bmiHeader.biPlanes = bmih.biPlanes;
lpbmi->bmiHeader.biBitCount = bmih.biBitCount;
lpbmi->bmiHeader.biCompression = bmih.biCompression;
lpbmi->bmiHeader.biSizeImage = bmih.biSizeImage;
lpbmi->bmiHeader.biXPelsPerMeter = bmih.biXPelsPerMeter;
lpbmi->bmiHeader.biYPelsPerMeter = bmih.biYPelsPerMeter;
lpbmi->bmiHeader.biClrUsed = bmih.biClrUsed;
lpbmi->bmiHeader.biClrImportant = bmih.biClrImportant;

/*
* Retrieve the color table.
* 1 << bmih.biBitCount == 2 ^ bmih.biBitCount
*/

ReadFile(hfbm, lpbmi->bmiColors,
((1<<bmih.biBitCount) * sizeof(RGBQUAD)),
&dwRead, (LPOVERLAPPED) NULL);

/*
* Allocate memory for the required number of
* bytes.
*/

hmem2 = GlobalAlloc(GHND,
(bmfh.bfSize - bmfh.bfOffBits));

lpvBits = GlobalLock(hmem2);

/* Retrieve the bitmap data. */

ReadFile(hfbm, lpvBits,
(bmfh.bfSize - bmfh.bfOffBits),
&dwRead, (LPOVERLAPPED) NULL);

/*
* Create a bitmap from the data stored in the
* .BMP file.
*/

hbm = CreateDIBitmap(hdc, &bmih,
CBM_INIT, lpvBits,
lpbmi, DIB_RGB_COLORS);

/*
* Unlock the global memory objects and
* close the .BMP file.
*/

GlobalUnlock(hmem1);
GlobalUnlock(hmem2);
CloseHandle(hfbm);

/* Set the fDisplayBitmap flag. */

if (hbm)
fDisplayBitmap = TRUE;
else
TextOut(hdc, 100, 100, "LoadBitmap Failed", 17);

/* Paint the window (and draw the bitmap). */

GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &rect, TRUE);
UpdateWindow(hwnd);

Once the bitmap data is retrieved, the bitmapped image can be drawn in the application's client area. The following code sample is used to draw the bitmap.
case WM_PAINT:
BeginPaint(hwnd, &ps);
if (fDisplayBitmap) {
hdcMem = CreateCompatibleDC(ps.hdc);
SelectObject(hdcMem, hbm);
GetObject(hbm, sizeof(BITMAP), (LPSTR) &bm);
BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight,
hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
}
EndPaint(hwnd, &ps);
break;

+$# Using a Print Dialog Box and Retrieving a Printer Device Context
The first step in printing involves setting up the printer and obtaining a printer DC. In the sample application, the File menu contains two options, Print and Print Setup. By selecting either option, the user can configure the printer. When the user selects the Print Setup option, the Print Setup dialog box is displayed and the user can select a printer, a page orientation, a paper size, and so on. When the user selects the Print option, the Print dialog box is displayed and the user can select a range of pages, a print quality, a number of copies, and so on. The user can also display the Print Setup dialog box by clicking the Setup push button.
The Print and Print Setup dialog boxes are both displayed by initializing the members of a PRINTDLG_win32_PRINTDLG_str structure and calling the PrintDlg_win32_PrintDlg function. (For more information about displaying the Print Setup dialog box, see Common Dialog Box Library_win32_Common_Dialog_Box_Library). In addition to retrieving user-specified data, PrintDlg can be used to obtain a printer DC by specifying the PD_RETURNDC value in the Flags member of the PRINTDLG structure. The following code sample shows how to intialize the members of the structure and to display the Print dialog box.
/* Initialize the PRINTDLG members. */

pd.lStructSize = sizeof(PRINTDLG);
pd.hDevMode = (HANDLE) NULL;
pd.hDevNames = (HANDLE) NULL;
pd.Flags = PD_RETURNDC;
pd.hwndOwner = hwnd;
pd.hDC = (HDC) NULL;
pd.nFromPage = 1;
pd.nToPage = 1;
pd.nMinPage = 0;
pd.nMaxPage = 0;
pd.nCopies = 1;
pd.hInstance = (HANDLE) NULL;
pd.lCustData = 0L;
pd.lpfnPrintHook = (LPPRINTHOOKPROC) NULL;
pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL;
pd.lpPrintTemplateName = (LPSTR) NULL;
pd.lpSetupTemplateName = (LPSTR) NULL;
pd.hPrintTemplate = (HANDLE) NULL;
pd.hSetupTemplate = (HANDLE) NULL;

/* Display the PRINT dialog box. */

PrintDlg(&pd);

+$#A Preparing to Print
The following code sample determines whether the selected printer is capable of printing bitmaps by calling the GetDeviceCaps_win32_GetDeviceCaps function, supplying the RASTERCAPS value. By examining this function's return value, the application can determine whether it should print a document or inform the user that the device does not support raster output.
/*
* Examine the raster capabilities of the device
* identified by pd.hDC to verify that it supports
* the BitBlt function.
*/

if (!(GetDeviceCaps(pd.hDC, RASTERCAPS)
& RC_BITBLT)) {
DeleteDC(pd.hDC);
MessageBox(hwnd,
"Printer cannot display bitmaps.",
"Device Error",
MB_OK);
break;
}

After the sample application determines that the selected printer is capable of printing bitmaps, it follows these steps:
1. Sets a Boolean flag that the application's abort procedure can examine to determine whether to allow printing to continue.
2. Registers the application's AbortProc_win32_AbortProc function, which is used to cancel a print job.
3. Displays a modeless Cancel dialog box.
4. Disables the application's window while the dialog box is displayed.

The steps are illustrated in the following code sample.
/*
* Set the flag used by the AbortPrintJob
* dialog procedure.
*/

bPrint = TRUE;

/*
* Register the application's AbortProc
* function with GDI.
*/

SetAbortProc(pd.hDC, AbortProc);

/* Display the modeless Cancel dialog box. */

hdlgCancel = CreateDialog(hinst, (LPTSTR) "AbortDlg",
hwnd, (DLGPROC) AbortPrintJob);

/* Disable the application's window. */

EnableWindow(hwnd, FALSE);

Once the application registers AbortProc_win32_AbortProc with Windows, GDI calls the function repeatedly during the printing process to determine whether to cancel a job. In the current version of the Win32 API, GDI calls this function approximately every two seconds until the entire job has been spooled.
If the user chooses to cancel the job, GDI notifies the spooler that it should delete the corresponding journal file from the print queue and reset the printer to its default state.
+$#A Abort Procedure
Any application written for Windows that supports printing should provide an abort procedure and a modeless dialog box that allow a user to cancel a print job. The abort procedure for the sample application contains a message loop that retrieves messages for the modeless dialog box.
BOOL CALLBACK AbortProc(HDC hdc, int nCode)
{
MSG msg;

/*
* Retrieve and remove messages from the thread's message
* queue.
*/

while (PeekMessage((LPMSG) &msg, (HWND) NULL,
0, 0, PM_REMOVE)) {

/* Process any messages for the Cancel dialog box. */

if (!IsDialogMessage(hdlgCancel, (LPMSG) &msg)) {
TranslateMessage((LPMSG) &msg);
DispatchMessage((LPMSG) &msg);
}
}

/*
* Return the global bPrint flag (which is set to FALSE
* if the user presses the Cancel button).
*/

return bPrint;

}

For Win16-based applications, this procedure must be exported in the application's module-definition (.DEF) file.
+$#A Cancel Dialog Box
The Cancel dialog box typically contains a single push button that allows the user to cancel a print job. The following template for the Cancel dialog box was taken from the sample application's resource (.RES) file.
AbortDlg DIALOG LOADONCALL MOVEABLE DISCARDABLE 33, 32, 160, 70

CAPTION "Sample Printing App"

STYLE WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_VISIBLE |
WS_POPUP | WS_SYSMENU

BEGIN

CONTROL "Now Printing: ", IDD_TEXT, "static",
SS_CENTER | WS_CHILD, 0, 10, 160, 8

CONTROL "", IDD_FILE, "static",
SS_CENTER | WS_CHILD, 0, 25, 160, 8

CONTROL "Cancel", IDD_CANCEL, "button",
BS_DEFPUSHBUTTON | WS_TABSTOP | WS_CHILD,
60, 45, 45, 15

END

The code sample that follows shows the dialog box procedure for the application.
LRESULT CALLBACK AbortPrintJob(
HWND hwndDlg, /* window handle of dialog box */
UINT message, /* type of message */
WPARAM wParam, /* message-specific information */
LPARAM lParam) /* message-specific information */
{
switch (message) {
case WM_INITDIALOG: /* message: initialize dialog box */

/* Initialize the static text control. */

SetDlgItemText(hwndDlg, IDD_FILE, ofn.lpstrFile);

return TRUE;

case WM_COMMAND: /* message: received a command */

/* User pressed "Cancel" button--stop print job. */

MessageBox(hwndDlg, "Incoming", "WM_COMMAND", MB_OK);

bPrint = FALSE;

return TRUE;

default:
return FALSE; /* didn't process a message */

}
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(message);
}

+$#A Printing a Document
Once the application initializes the necessary variables, registers its AbortProc_win32_AbortProc function, and displays its modeless Cancel dialog box, it can start the print job by calling the StartDoc_win32_StartDoc function.
After the application begins a print job, it can define individual pages in the document by calling the StartPage_win32_StartPage and EndPage_win32_EndPage functions and embedding the appropriate calls to GDI drawing functions within this bracket. After the application has defined the last page, it can close the document and end the print job by calling the EndDoc_win32_EndDoc function.
The following example shows the code required to print a string of text and a bitmapped image. The string of text, centered at the top of the page, identifies the path and filename for the file that contains the bitmapped image. The bitmapped image, centered vertically and horizontally on the page, is drawn so that the same proportions used to draw the image in the application's window are maintained.
/*
* Initialize the members of a DOCINFO
* structure.
*/

di.cbSize = sizeof(DOCINFO);
di.lpszDocName = "Bitmap Printing Test";
di.lpszOutput = (LPTSTR) NULL;
di.lpszDataType = (LPTSTR) NULL;
di.fwType = 0;

/*
* Begin a print job by calling the StartDoc
* function.
*/

nError = StartDoc(pd.hDC, &di);
if (nError == SP_ERROR) {
errhandler("StartDoc", hwnd);
goto Error;
}

/*
* Inform the driver that the application is
* about to begin sending data.
*/

nError = StartPage(pd.hDC);
if (nError <= 0) {
errhandler("StartPage", hwnd);
goto Error;
}

/*
* Retrieve the number of pixels-per-logical-inch
* in the horizontal and vertical directions
* for the display upon which the bitmap
* was created.
*/

fLogPelsX1 = (float) GetDeviceCaps(pd.hDC, LOGPIXELSX);
fLogPelsY1 = (float) GetDeviceCaps(pd.hDC, LOGPIXELSY);

/*
* Retrieve the number of pixels-per-logical-inch
* in the horizontal and vertical directions
* for the printer upon which the bitmap
* will be printed.
*/

fLogPelsX2 = (float) GetDeviceCaps(pd.hDC,
LOGPIXELSX);
fLogPelsY2 = (float) GetDeviceCaps(pd.hDC,
LOGPIXELSY);

/*
* Determine the scaling factors required to
* print the bitmap and retain its original
* proportions.
*/

if (fLogPelsX1 > fLogPelsX2)
fScaleX = (fLogPelsX1 / fLogPelsX2);
else
fScaleX = (fLogPelsX2 / fLogPelsX1);

if (fLogPelsY1 > fLogPelsY2)
fScaleY = (fLogPelsY1 / fLogPelsY2);
else
fScaleY = (fLogPelsY2 / fLogPelsY1);

/*
* Compute the coordinate of the upper left
* corner of the centered bitmap.
*/

cWidthPels = GetDeviceCaps(pd.hDC, HORZRES);
xLeft = ((cWidthPels / 2) -
((int) (((float) bmih.biWidth)
* fScaleX)) / 2);
cHeightPels = GetDeviceCaps(pd.hDC, VERTRES);
yTop = ((cHeightPels / 2) -
((int) (((float) bmih.biHeight)
* fScaleY)) / 2);

/*
* Create a memory DC that is compatible with
* the printer and select the bitmap (which
* the user requested) into this DC.
*/

hdcMem = CreateCompatibleDC(pd.hDC);

if (!SelectObject(hdcMem, hbm))
errhandler("SelectObject Failed", hwnd);

/*
* Use the StretchBlt function to scale the
* bitmap and maintain its original proportions
* (that is, if the bitmap was square when it
* appeared in the application's client area,
* it should also appear square on the page).
*/

if (!StretchBlt(pd.hDC, xLeft, yTop,
(int) ((float) bmih.biWidth * fScaleX),
(int) ((float) bmih.biHeight * fScaleY),
hdcMem, 0, 0,
bmih.biWidth, bmih.biHeight,
SRCCOPY))
errhandler("StretchBlt Failed", hwnd);

/* Delete the memory DC. */

DeleteDC(hdcMem);

/*
* Retrieve the width of the string that
* specifies the full path and filename for the
* file that contains the bitmap.
*/

GetTextExtentPoint32(pd.hDC, ofn.lpstrFile,
ofn.nFileExtension + 3,
&szMetric);

/*
* Compute the starting point for the
* text-output operation. The string will
* be centered horizontally and positioned
* three-lines down from the top of the page.
*/

xLeft = ((cWidthPels / 2) - (szMetric.cx / 2));
yTop = (szMetric.cy * 3);

/*
* Print the path and filename for the bitmap,
* centered at the top of the page.
*/

TextOut(pd.hDC, xLeft, yTop, ofn.lpstrFile,
ofn.nFileExtension + 3);

/*
* Determine whether the user has pressed
* the Cancel button in the AbortPrintJob
* dialog box; if the button has been pressed,
* call the AbortDoc function. Otherwise, inform
* the spooler that the page is complete.
*/

nError = EndPage(pd.hDC);

if (nError <= 0) {
errhandler("EndPage", hwnd);
goto Error;
}

/* Inform the driver that document has ended. */

nError = EndDoc(pd.hDC);
if (nError <= 0)
errhandler("EndDoc", hwnd);

Error:
/* Enable the application's window. */

EnableWindow(hwnd, TRUE);

/* Remove the AbortPrintJob dialog box. */

DestroyWindow(hdlgCancel);

/* Delete the printer DC. */

DeleteDC(pd.hDC);

Because the pixels on a screen typically have different dimensions than the dots on a printer, it is necessary to scale bitmapped images to obtain a WYSIWYG effect. This is done by obtaining horizontal and vertical scaling factors and then applying those factors to the width and height values passed to the StretchBlt_win32_StretchBlt function. In the sample application, the scaling factors were obtained by retrieving the horizontal and vertical logical-pixel count for the two devices. Once the scaling factors were obtained, they were used to adjust the bitmap width and height.
To center the bitmap on the page, the application first computed the width and height of the scaled bitmap. (The bitmap was scaled to maintain the original proportions of the image.) These values were divided by two and then subtracted from half of the width and height of the page. The result defines the coordinates of the upper left corner of the bitmap.
To center the text at the top of the page, the application called the GetTextExtentPoint32_win32_GetTextExtentPoint32 function to retrieve the width and height of the string specifying the path names and filenames. Once these values were obtained, the application used the height to position the string three lines down the page and the width to position the string horizontally centered on the page.
The following illustration shows a representation of the page that appeared when the application printed the bitmapped image in the WINLOGO.BMP file. This illustration also depicts the variables used to position the text and to position and scale the bitmap.
{ewc msdncd, EWGraphic, group10664 0 /a "SDK_01.bmp"}
+$#AK Printing and Print Spooler Reference
The following functions, structures, and messages are associated with printing and the print spooler.
+$#A Printing and Print Spooler Functions
Following are the functions used to print.
AbortDoc_win32_AbortDocDeviceCapabilities_win32_DeviceCapabilitiesEndDoc_win32_EndDocEndPage_win32_EndPageEscape_win32_Escape_v3ExtEscape_win32_ExtEscapeSetAbortProc_win32_SetAbortProcStartDoc_win32_StartDocStartPage_win32_StartPage
Following are the functions used to access the print spooler.
AbortPrinter_win32_AbortPrinterAbortProc_win32_AbortProcAddForm_win32_AddFormAddJob_win32_AddJobAddMonitor_win32_AddMonitorAddPort_win32_AddPortAddPrinter_win32_AddPrinterAddPrinterConnection_win32_AddPrinterConnectionAddPrinterDriver_win32_AddPrinterDriverAddPrintProcessor_win32_AddPrintProcessorAddPrintProvidor_win32_AddPrintProvidorAdvancedDocumentProperties_win32_AdvancedDocumentPropertiesClosePrinter_win32_ClosePrinterConfigurePort_win32_ConfigurePortConnectToPrinterDlg_win32_ConnectToPrinterDlgDeleteForm_win32_DeleteFormDeleteMonitor_win32_DeleteMonitorDeletePort_win32_DeletePortDeletePrinter_win32_DeletePrinterDeletePrinterConnection_win32_DeletePrinterConnectionDeletePrinterDriver_win32_DeletePrinterDriverDeletePrintProcessor_win32_DeletePrintProcessorDeletePrintProvidor_win32_DeletePrintProvidorDocumentProperties_win32_DocumentPropertiesEndDocPrinter_win32_EndDocPrinterEndPagePrinter_win32_EndPagePrinterEnumForms_win32_EnumFormsEnumJobs_win32_EnumJobsEnumMonitors_win32_EnumMonitorsEnumPorts_win32_EnumPortsEnumPrinterDrivers_win32_EnumPrinterDriversEnumPrinters_win32_EnumPrintersEnumPrintProcessorDataTypes_win32_EnumPrintProcessorDatatypesEnumPrintProcessors_win32_EnumPrintProcessorsFindClosePrinterChangeNotification_win32_FindClosePrinterChangeNotificationFindFirstPrinterChangeNotification_win32_FindFirstPrinterChangeNotificationFindNextPrinterChangeNotification_win32_FindNextPrinterChangeNotificationFreePrinterNotifyInfo_win32_FreePrinterNotifyInfoGetForm_win32_GetFormGetJob_win32_GetJobGetPrinter_win32_GetPrinterGetPrinterData_win32_GetPrinterDataGetPrinterDriver_win32_GetPrinterDriverGetPrinterDriverDirectory_win32_GetPrinterDriverDirectoryGetPrintProcessorDirectory_win32_GetPrintProcessorDirectoryOpenPrinter_win32_OpenPrinterPrinterMessageBox_win32_PrinterMessageBoxPrinterProperties_win32_PrinterPropertiesReadPrinter_win32_ReadPrinterResetPrinter_win32_ResetPrinterScheduleJob_win32_ScheduleJobSetForm_win32_SetFormSetJob_win32_SetJobSetPrinter_win32_SetPrinterSetPrinterData_win32_SetPrinterDataStartDocPrinter_win32_StartDocPrinterStartPagePrinter_win32_StartPagePrinterWaitForPrinterChange_win32_WaitForPrinterChangeWritePrinter_win32_WritePrinter
+$#A Printing and Print Spooler Structures
The following structures are used with the print spooler:
DATATYPES_INFO_1_win32_DATATYPES_INFO_1_str PRINTER_NOTIFY_OPTIONS_win32_PRINTER_NOTIFY_OPTIONS_strPRINTER_NOTIFY_OPTIONS_TYPE_win32_PRINTER_NOTIFY_OPTIONS_TYPE_strPRINTER_NOTIFY_INFO_win32_PRINTER_NOTIFY_INFO_strPRINTER_NOTIFY_INFO_DATA_win32_PRINTER_NOTIFY_INFO_DATA_str
+$#A Printing and Print Spooler Messages
Following is the message used with the print spooler.
WM_SPOOLERSTATUS_win32_WM_SPOOLERSTATUS

+$#AK Regions
In Microsoft® Windows®, a region is a rectangle, polygon, or ellipse (or a combination of two or more of these shapes) that can be filled, painted, inverted, framed, and used to perform hit testing (testing for the cursor location).
+$#A About Regions
Following are three types of regions that have been filled and framed.
{ewc msdncd, EWGraphic, group10665 0 /a "SDK_01.bmp"}
+$#A Region Creation and Selection
An application creates a region by calling a function associated with a specific shape. The following table shows the function(s) associated with each of the standard shapes.
Shape Function
Rectangular region CreateRectRgn_win32_CreateRectRgn, CreateRectRgnIndirect_win32_CreateRectRgnIndirect
Rectangular region with rounded corners CreateRoundRectRgn_win32_CreateRoundRectRgn
Elliptical region CreateEllipticRgn_win32_CreateEllipticRgn, CreateEllipticRgnIndirect_win32_CreateEllipticRgnIndirect
Polygonal region CreatePolygonRgn_win32_CreatePolygonRgn, CreatePolyPolygonRgn_win32_CreatePolyPolygonRgn

Each region creation function returns a handle that identifies the new region. An application can use this handle to select the region into a device context by calling the SelectObject_win32_SelectObject function and supplying this handle as the second argument. After a region is selected into a device context, the application can perform various operations on it.
+$#A Region Operations
Applications can combine regions, compare them, paint or invert their interiors, draw a frame around them, retrieve their dimensions, and test whether the cursor lies within their boundaries.
+$#A Combining
An application combines two regions by calling the CombineRgn_win32_CombineRgn function. Using this function, an application can combine the intersecting parts of two regions, all but the intersecting parts of two regions, the two original regions in their entirety, and so on. Following are five values that define the region combinations.
Value Meaning
RGN_AND The intersecting parts of two original regions define a new region.
RGN_COPY A copy of the first (of the two original regions) defines a new region.
RGN_DIFF The part of the first region that does not intersect the second defines a new region.
RGN_OR The two original regions define a new region.
RGN_XOR Those parts of the two original regions that do not overlap define a new region.

The following illustration shows the five possible combinations of a square and a circular region resulting from a call to CombineRgn_win32_CombineRgn.
{ewc msdncd, EWGraphic, group10665 1 /a "SDK_02.bmp"}
+$#A Comparing
An application compares two regions to determine whether or not they are identical by calling the EqualRgn_win32_EqualRgn function. EqualRgn considers two regions identical if they are equal in size and shape.
+$#A Filling
An application fills the interior of a region by calling the FillRgn_win32_FillRgn function and supplying a handle that identifies a specific brush. When an application calls FillRgn, Windows fills the region with the brush by using the current fill mode for the specified device context. There are two fill modes: alternate and winding. The application can set the fill mode for a device context by calling the SetPolyFillMode_win32_SetPolyFillMode function. The application can retrieve the current fill mode for a device context by calling the GetPolyFillMode_win32_GetPolyFillMode function.
The following illustration shows two identical regions: one filled using alternate mode and the other filled using winding mode.
{ewc msdncd, EWGraphic, group10665 2 /a "SDK_03.bmp"}
Alternate Mode
To determine which pixels Windows highlights when alternate mode is specified, perform the following test:
1. Select a pixel within the region's interior.
2. Draw an imaginary ray, in the positive x-direction, from that pixel towards infinity.
3. Each time the ray intersects a boundary line, increment a count value.
Windows highlights the pixel if the count value is an odd number.
Winding Mode
To determine which pixels Windows highlights when winding mode is specified, perform the following test:
1. Determine the direction in which each boundary line is drawn.
2. Select a pixel within the region's interior.
3. Draw an imaginary ray, in the positive x-direction, from the pixel toward infinity.
4. Each time the ray intersects a boundary line with a positive y-component, increment a count value. Each time the ray intersects a boundary line with a negative y-component, decrement the count value.
Windows highlights the pixel if the count value is nonzero.
+$#A Painting
An application fills the interior of a region by using the brush currently selected into a device context by the PaintRgn_win32_PaintRgn function. This function uses the current polygon fill modes (alternate and winding).
+$#A Inverting
An application inverts the colors that appear within a region by calling the InvertRgn_win32_InvertRgn function. On monochrome displays, InvertRgn makes white pixels black and black pixels white. On color screens, this inversion is dependent on the type of technology used to generate the colors for the screen.
+$#A Framing
An application draws a border around a region by calling the FrameRgn_win32_FrameRgn function and specifying the border width and brush pattern that Windows uses when drawing the frame.
+$#A Retrieving a Bounding Rectangle
An application retrieves the dimensions of a region's bounding rectangle by calling the GetRgnBox_win32_GetRgnBox function. If the region is rectangular, GetRgnBox returns the dimensions of the region. If the region is elliptical, the function returns the dimensions of the smallest rectangle that can be drawn around the ellipse. The long sides of the rectangle are the same length as the ellipse's major axis, and the short sides of the rectangle are the same length as the ellipse's minor axis. If the region is polygonal, GetRgnBox returns the dimensions of the smallest rectangle that can be drawn around the entire polygon.
+$#A Moving
An application moves a region by calling the OffsetRgn_win32_OffsetRgn function. The given offsets along the x-axis and y-axis determine the number of logical units to move left or right and up or down.
+$#A Hit Testing
An application performs hit testing on regions to determine the coordinates of the current cursor position. Then it passes these coordinates ¾ as well as a handle identifying the region ¾ to the PtInRegion_win32_PtInRegion function. The cursor coordinates can be retrieved by processing the various mouse messages, such as WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN, WM_LBUTTONUP_win32_WM_LBUTTONUP, WM_RBUTTONDOWN_win32_WM_RBUTTONDOWN, and WM_RBUTTONUP_win32_WM_RBUTTONUP. The return value for PtInRegion indicates whether the cursor position is within the given region.
+$#A Using Regions
· Using regions to clip output_win32_Using_Regions_to_Clip_Output
· Using regions to perform hit testing_win32_Using_Regions_to_Perform_Hit_Testing

+$#A Using Regions to Clip Output
This section contains a single example that demonstrates how you can use regions to enable the user to define how a part of client area output can appear. Regions used for this purpose are called clipping regions.
The example for this section is taken from a Windows-based application that enables a user to capture the entire desktop as a bitmap and then isolate and save a part of this image as a .BMP file.
By selecting the Define Clip Region option from the application's menu, the user is able to select a clipping region by clicking the left mouse button and dragging the mouse. As the user drags the mouse, the application draws a rectangle that corresponds to the new clipping region.
By selecting the Clip option, the user is able to redraw the isolated part of the image within the boundaries of the specified rectangle.
+$#A Defining the Clipping Region
When the user selects the Define Clip Region option, Windows issues a WM_COMMAND_win32_WM_COMMAND message. The wParam parameter of this message contains an application-defined constant, IDM_DEFINE, that indicates that the user selected this option from the menu. The application processes this input by setting a Boolean flag, fDefineRegion, as shown in the following code sample.
case WM_COMMAND:
switch (wParam) {

case IDM_DEFINE:
fDefineRegion = TRUE;
break;

After selecting the Define Clipping Region option, the user can begin drawing the rectangle by clicking and dragging the mouse while the cursor is in the application's client area.
When the user presses the left button, Windows issues a WM_LBUTTONDOWN_win32_WM_LBUTTONDOWN message. The lParam parameter of this message contains the cursor coordinates, which correspond to the upper left corner of a rectangle used to define the clipping region. The application processes the WM_LBUTTONDOWN message, as follows.
/* These variables are required for clipping. */

static POINT ptUpperLeft;
static POINT ptLowerRight;
static POINT aptRect[5];
static POINT ptTmp;
static POINTS ptsTmp;
static BOOL fDefineRegion;
static BOOL fRegionExists;
static HRGN hrgn;
static RECT rctTmp;
int i;

switch (message) {

case WM_LBUTTONDOWN:
if (fDefineRegion) {

/* Retrieve the new upper left corner. */

ptsTmp = MAKEPOINTS(lParam);
ptUpperLeft.x = (LONG) ptsTmp.x;
ptUpperLeft.y = (LONG) ptsTmp.y;
}

if (fRegionExists) {

/* Erase the previous rectangle. */

hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOTXORPEN);

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))
errhandler("Polyline Failed", hwnd);
ReleaseDC(hwnd, hdc);

/* Clear the rectangle coordinates. */

for (i = 0; i < 4; i++) {
aptRect[i].x = 0;
aptRect[i].y = 0;
}

/* Clear the temporary point structure. */

ptTmp.x = 0;
ptTmp.y = 0;

/* Clear the lower right coordinates. */

ptLowerRight.x = 0;
ptLowerRight.y = 0;

/* Reset the flag. */

fRegionExists = FALSE;
fDefineRegion = TRUE;

/* Retrieve the new upper left corner. */

ptsTmp = MAKEPOINTS(lParam);
ptUpperLeft.x = (LONG) ptsTmp.x;
ptUpperLeft.y = (LONG) ptsTmp.y;
}
break;

As the user drags the mouse, Windows issues WM_MOUSEMOVE_win32_WM_MOUSEMOVE messages and stores the new cursor coordinates in the lParam parameter. Each time the application receives a new WM_MOUSEMOVE message, it erases the previous rectangle (if one exists) and draws the new rectangle by calling the Polyline_win32_Polyline function, passing it the coordinates of the four corners of the rectangle. The application performs the following tasks.
/* These variables are required for clipping. */

static POINT ptUpperLeft;
static POINT ptLowerRight;
static POINT aptRect[5];
static POINT ptTmp;
static POINTS ptsTmp;
static BOOL fDefineRegion;
static BOOL fRegionExists;
static HRGN hrgn;
static RECT rctTmp;
int i;

switch (message) {

case WM_MOUSEMOVE:

if (wParam & MK_LBUTTON && fDefineRegion) {

/* Get a window DC. */

hdc = GetDC(hwnd);

if (!SetROP2(hdc, R2_NOTXORPEN))
errhandler("SetROP2 Failed", hwnd);

/*
* If previous mouse movement occurred, store the original
* lower right corner coordinates in a temporary structure.
*/

if (ptLowerRight.x) {
ptTmp.x = ptLowerRight.x;
ptTmp.y = ptLowerRight.y;
}

/*
* Get the new coordinates of the clipping region's lower
* right corner.
*/

ptsTmp = MAKEPOINTS(lParam);
ptLowerRight.x = (LONG) ptsTmp.x;
ptLowerRight.y = (LONG) ptsTmp.y;

/*
* If previous mouse movement occurred, erase the original
* rectangle.
*/

if (ptTmp.x) {
aptRect[0].x = ptUpperLeft.x;
aptRect[0].y = ptUpperLeft.y;
aptRect[1].x = ptTmp.x;
aptRect[1].y = ptUpperLeft.y;
aptRect[2].x = ptTmp.x;
aptRect[2].y = ptTmp.y;
aptRect[3].x = ptUpperLeft.x;
aptRect[3].y = ptTmp.y;
aptRect[4].x = aptRect[0].x;
aptRect[4].y = aptRect[0].y;

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))
errhandler("Polyline Failed", hwnd);
}

aptRect[0].x = ptUpperLeft.x;
aptRect[0].y = ptUpperLeft.y;
aptRect[1].x = ptLowerRight.x;
aptRect[1].y = ptUpperLeft.y;
aptRect[2].x = ptLowerRight.x;
aptRect[2].y = ptLowerRight.y;
aptRect[3].x = ptUpperLeft.x;
aptRect[3].y = ptLowerRight.y;
aptRect[4].x = aptRect[0].x;
aptRect[4].y = aptRect[0].y;

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))
errhandler("Polyline Failed", hwnd);

ReleaseDC(hwnd, hdc);
}
break;

+$#A Clipping Output
After the user chooses the Clip option from the menu, the application uses the coordinates of the rectangle the user created to define a clipping region. After defining the clipping region and selecting it into the application's device context, the application redraws the bitmapped image. The application performs these tasks, as follows.
/* These variables are required for clipping. */

static POINT ptUpperLeft;
static POINT ptLowerRight;
static POINT aptRect[5];
static POINT ptTmp;
static POINTS ptsTmp;
static BOOL fDefineRegion;
static BOOL fRegionExists;
static HRGN hrgn;
static RECT rctTmp;
int i;

case WM_COMMAND:
switch (wParam) {

case IDM_CLIP:

hdc = GetDC(hwnd);

/*
* Retrieve the application's client rectangle and paint
* with the default (white) brush.
*/

GetClientRect(hwnd, &rctTmp);
FillRect(hdc, &rctTmp, GetStockObject(WHITE_BRUSH));

/* Use the rect coordinates to define a clipping region. */

hrgn = CreateRectRgn(aptRect[0].x, aptRect[0].y,
aptRect[2].x, aptRect[2].y);
SelectClipRgn(hdc, hrgn);

/* Transfer (draw) the bitmap into the clipped rectangle. */

BitBlt(hdc,
0, 0,
bmp.bmWidth, bmp.bmHeight,
hdcCompatible,
0, 0,
SRCCOPY);

ReleaseDC(hwnd, hdc);
break;

+$#A Using Regions to Perform Hit Testing
The example in Brushes_win32_Brushes uses regions to simulate a "zoomed" view of an 8- by 8-pixel monochrome bitmap. By clicking on the pixels in this bitmap, the user creates a custom brush suitable for drawing operations. The example shows how to use the PtInRegion_win32_PtInRegion function to perform hit testing and the InvertRgn_win32_InvertRgn function to invert the colors in a region.
+$#A Region Functions
Following are the functions used with regions.
CombineRgn_win32_CombineRgnCreateEllipticRgn_win32_CreateEllipticRgnCreateEllipticRgnIndirect_win32_CreateEllipticRgnIndirectCreatePolygonRgn_win32_CreatePolygonRgnCreatePolyPolygonRgn_win32_CreatePolyPolygonRgnCreateRectRgn_win32_CreateRectRgnCreateRectRgnIndirect_win32_CreateRectRgnIndirectCreateRoundRectRgn_win32_CreateRoundRectRgnEqualRgn_win32_EqualRgnExtCreateRegion_win32_ExtCreateRegionFillRgn_win32_FillRgnFrameRgn_win32_FrameRgnGetPolyFillMode_win32_GetPolyFillModeGetRegionData_win32_GetRegionDataGetRgnBox_win32_GetRgnBoxInvertRgn_win32_InvertRgnOffsetRgn_win32_OffsetRgnPaintRgn_win32_PaintRgnPtInRegion_win32_PtInRegionRectInRegion_win32_RectInRegionSetPolyFillMode_win32_SetPolyFillMode

+$#AK Accessibility
The Microsoft® Win32® API provides a set of accessibility features that make it easier for persons with disabilities to use computers. This overview describes the provided accessibility features and the Win32 functions and structures for controlling these features.
+$#A About Accessibility
There are two categories of accessibility features:
· Accessibility parameters_win32_Accessibility_Parameters. These are system parameters that can be set by the user. Applications can check the state of an accessibility parameter to determine whether the user wants special behavior that can be provided in an application-specific manner. For example, the ShowSounds parameter indicates that an application that typically uses sound to convey important information should also provide the information visually.
· Built-in accessibility features_win32_Built_in_Accessibility_Features. These features are built into the system or provided as an extension to the system. They affect how the user provides keyboard and mouse input to the computer. When enabled, their functionality is available regardless of which applications are running. An example is a keyboard filter that makes it easier for users with movement impairments to type key combinations such as CTRL+ALT+DEL.

+$#A Accessibility Parameters
The system maintains a set of accessibility parameters that indicate whether the user has special needs or preferences. The user controls the state of these parameters, typically using a Control Panel application. Control Panel applications or other programs that allow the user to customize the Windows environment can use the SystemParametersInfo_win32_SystemParametersInfo function to set the accessibility parameters.
Applications should use the GetSystemMetrics_win32_GetSystemMetrics function to determine the state of the accessibility parameters. When an accessibility parameter is set to TRUE, the application should modify its user interface, if necessary, to accomodate the user's preferences.
Win32 supports the following accessibility parameter:
Parameter Description
ShowSounds Indicates that an application that typically uses sound to convey important information should provide the information visually.

+$#A Built-in Accessibility Features
The built-in accessibility features are extensions to the operating system. These features affect the behavior of the system regardless of which applications are running.
Win32 supports the following built-in accessibility features:
Feature Description
AccessTimeout Enables a user to specify a timeout interval after which system-wide accessibility features are automatically disabled. The AccessTimeout feature is intended for computers that are shared by several users with different preferences. Each individual can use hot keys or the Control Panel to enable preferred features. After a user leaves, the features he or she enabled will be automatically disabled by the timeout. The accessibility features affected by the timeout are FilterKeys, MouseKeys, StickyKeys, and ToggleKeys. The ACCESSTIMEOUT_win32_ACCESSTIMEOUT_str structure defines the parameters for this feature.
FilterKeys Enables control of keyboard properties, such as the amount of time before a keystroke is accepted as input and the amount of time before a keystroke begins to repeat. The FilterKeys feature also provides sound feedback when a key is pressed and when it is accepted as input. The FILTERKEYS_win32_FILTERKEYS_str structure defines the parameters for this feature.
MouseKeys Enables the user to control the mouse pointer using the numeric keypad. The MOUSEKEYS_win32_MOUSEKEYS_str structure defines the parameters for this feature.
SoundSentry Displays a visual signal when a sound is generated by a Windows-based application or an MS-DOS application running in a window. The SOUNDSENTRY_win32_SOUNDSENTRY_str structure defines the parameters for this feature.
StickyKeys Enables the user to type key combinations, such as CTRL+ALT+DEL, in sequence rather than at the same time. The STICKYKEYS_win32_STICKYKEYS_str structure defines the parameters for this feature.
ToggleKeys Provides sound feedback when the user turns on or off the CAPS LOCK, NUM LOCK, or SCROLL LOCK keys. The TOGGLEKEYS_win32_TOGGLEKEYS_str structure defines the parameters for this feature.

The SystemParametersInfo_win32_SystemParametersInfo function provides the programming interface to the built-in accessibility features. This function is useful in Control Panel applications or other programs that allow the user to customize the Windows environment. The SystemParametersInfo function can perform the following tasks:
· Query the system to determine the availability and current state of a specified built-in accessibility feature
· Enable a feature and specify parameters that control its behavior
· Disable a feature
· Enable a hot key that allows the user to enable or disable a feature

For each of the built-in features, Win32 defines a structure that applications use with SystemParametersInfo_win32_SystemParametersInfo to query or set the parameters of the feature.
An application can use SystemParametersInfo to enable or disable a feature directly. For some of the features, SystemParametersInfo can enable a hot key or key combination so the user can toggle the state of a feature. There are no hot keys for the SoundSentry feature. The following accessibility features have hot keys:
Feature Description
FilterKeys Hold down the LEFT SHIFT key for eight seconds to toggle the FilterKeys feature on or off. Holding down the LEFT SHIFT key for 12 seconds enables FilterKeys using the most conservative values for FILTERKEYS.iBounceMSec. Holding down the LEFT SHIFT key for 16 seconds enables FilterKeys using the most conservative values for FILTERKEYS.iWaitMSec.
MouseKeys LEFT ALT+LEFT SHIFT+NUM LOCK
StickyKeys Press the SHIFT key 5 times
ToggleKeys Hold down the NUM LOCK key for 5 seconds

+$#A Using Accessibility
· Checking the state of an accessibility parameter_win32_Checking_the_State_of_an_Accessibility_Parameter.
· Enabling a built-in accessibility feature_win32_Enabling_a_Built_in_Accessibility_Feature.

+$#A Checking the State of an Accessibility Parameter
The following code fragment uses the GetSystemMetrics_win32_GetSystemMetrics function to check the ShowSounds parameter. If GetSystemMetrics returns TRUE, the application should present all important information in visual form.
BOOL fShowSounds;
fShowSounds = GetSystemMetrics(SM_SHOWSOUNDS);

+$#A Enabling a Built-in Accessibility Feature
The following code fragment uses the SystemParametersInfo_win32_SystemParametersInfo function to enable the FilterKeys feature:
FILTERKEYS fk;
BOOL bSuccess;

// fill in members of the FILTERKEYS structure

fk.cbSize = sizeof(FILTERKEYS);
fk.dwFlags = (FKF_FILTERKEYSON | FKF_HOTKEYACTIVE |
FKF_AVAILABLE | FKF_HOTKEYSOUND | FKF_CLICKON);
fk.iWaitMSec = 1000;
fk.iDelayMSec = 1000;
fk.iRepeatMSec = 500;
fk.iBounceMSec = 0;

// call SystemParametersInfo with SPI_SETFILTERKEYS code

bSuccess = SystemParametersInfo(SPI_SETFILTERKEYS, 0, (LPVOID) &fk, 0);
+$#AK Accessibility Reference
The following structures and messages are associated with accessibility features.
+$#A Accessibility Functions
Use the following functions to implement accessibility features:
GetSystemMetrics_win32_GetSystemMetricsSystemParametersInfo_win32_SystemParametersInfo

+$#A Accessibility Structures
Use the following structures to implement accessibility features:
ACCESSTIMEOUT_win32_ACCESSTIMEOUT_strFILTERKEYS_win32_FILTERKEYS_strHIGHCONTRAST_win32_HIGHCONTRAST_str MOUSEKEYS_win32_MOUSEKEYS_strSERIALKEYS_win32_SERIALKEYS_strSOUNDSENTRY_win32_SOUNDSENTRY_strSTICKYKEYS_win32_STICKYKEYS_strTOGGLEKEYS_win32_TOGGLEKEYS_str

+$#AK Atoms
In Microsoft® Windows®, an atom table is a system-defined table that stores strings and corresponding identifiers. An application places a string in an atom table and receives a 16-bit integer, called an atom, that can be used to access the string. A string that has been placed in an atom table is called an atom name.
+$#A About Atom Tables
Dynamic data exchange (DDE) applications use an atom table to share item-name and topic-name strings with other applications. Rather than passing actual strings, a DDE application passes atoms to its partner application. The partner uses the atoms to obtain the strings from the atom table.
+$#A Global Atom Table
The Windows global atom table, which can contain up to 37 entries, is available to all applications. When an application places a string in the global atom table, the system generates an atom that is unique throughout the system. Any application that has the atom can obtain the string it identifies by querying the global atom table.
An application that defines a private clipboard-data format or DDE-data format for sharing data with other applications should place the format name in the global atom table. This technique prevents conflicts with the names of formats defined by the system or by other applications, and makes the identifiers (atoms) for the messages or formats available to the other applications.
+$#A Local Atom Tables
An application can use a local atom table to efficiently manage a large number of strings used only within the application. These strings, and the associated atoms, are available only to the application that created the table.
An application requiring the same string in a number of structures can reduce memory usage by using a local atom table. Rather than copying the string into each structure, the application can place the string in the atom table and include the resulting atom in the structures. In this way, a string appears only once in memory but can be used many times in the application.
Applications can also use local atom tables to save time when searching for a particular string. To perform a search, an application need only place the search string in the atom table and compare the resulting atom with the atoms in the relevant structures. Comparing atoms is typically faster than comparing strings.
By default, a local atom table can contain up to 37 entries. However, the size of a local atom table, unlike the size of a global atom table, can be changed. An application uses the InitAtomTable_win32_InitAtomTable function to accomplish this task. If the application calls InitAtomTable, however, it must do so before calling any other atom-management functions.
+$#A Atom Types
Applications can use two types of atoms: string and integer. The following sections describe these types.
+$#A String Atoms
Applications pass null-terminated strings to atom tables and receive string atoms (16-bit integers) in return. String atoms have the following properties:
· The values of string atoms are in the range 0xC000 through 0xFFFF.
· Case is not significant in searches for an atom name in an atom table. Also, the entire string must match in a search operation; no substring matching is performed.
· The string associated with a string atom can be no more than 255 bytes in size. This limitation applies to all atom functions.
· A reference count is associated with each atom name. The count is incremented each time the atom name is added to the table and decremented each time the atom name is deleted from it. This prevents different users of the same string atom from destroying each other's atom names. When the reference count for an atom name equals zero, the system removes the atom and the atom name from the table.
+$#A Integer Atoms
Integer atoms differ from string atoms in the following ways:
· The values of integer atoms are in the range 0x0001 through 0xBFFF. The values of integer atoms and string atoms do not overlap, so both types of atoms can be used in the same block of code.
· The string representation of an integer atom is #dddd, where the values represented by dddd are decimal digits. Leading zeros are ignored.
· There is no reference count or storage overhead associated with an integer atom.
Several Windows functions accept either string or integer atoms as parameters. When passing an integer atom to these functions, an application must use the MAKEINTATOM_win32_MAKEINTATOM macro to create the atom.
+$#A Atom Creation and Usage Count
An application creates a local atom by calling the AddAtom_win32_AddAtom function; it creates a global atom by calling the GlobalAddAtom_win32_GlobalAddAtom function. Both functions require a pointer to a string. The system searches the appropriate atom table for the string and returns the corresponding atom to the application. In the case of a string atom, if the string already resides in the atom table, the system increments the reference count for the string during this process.
Repeated calls to add the same atom name return the same atom. If the atom name does not exist in the table when AddAtom is called, the atom name is added to the table and a new atom is returned. If it is a string atom, its reference count is also set to one.
An application should call the DeleteAtom_win32_DeleteAtom function when it no longer needs to use a local atom; it should call the GlobalDeleteAtom_win32_GlobalDeleteAtom function when it no longer needs a global atom. In the case of a string atom, either of these functions reduces the reference count of the corresponding atom by one. When the reference count reaches zero, the system deletes the atom name from the table.
The atom name of a string atom remains in the global atom table as long as its reference count is greater than zero, even after the application that placed it in the table terminates. A local atom table is destroyed when the associated application terminates, regardless of the reference counts of the atoms in the table.
+$#A Atom-Table Queries
An application can determine whether a particular string is already in an atom table by using the FindAtom_win32_FindAtom or GlobalFindAtom_win32_GlobalFindAtom function. These functions search an atom table for the specified string and, if the string is there, return the corresponding atom.
An application can use the GetAtomName_win32_GetAtomName or GlobalGetAtomName_win32_GlobalGetAtomName function to retrieve an atom-name string from an atom table, provided the application has the atom corresponding to the string sought. Both functions copy the atom-name string of the specified atom to a buffer and return the length of the string that was copied. GetAtomName retrieves an atom-name string from a local atom table, and GlobalGetAtomName retrieves an atom-name string from the global atom table.
+$#A Atom String Formats
The AddAtom_win32_AddAtom, GlobalAddAtom_win32_GlobalAddAtom, FindAtom_win32_FindAtom, and GlobalFindAtom_win32_GlobalFindAtom functions take a pointer to a null-terminated string. An application can specify this pointer in one of the following ways.
Format Description
#dddd An integer specified as a decimal string. Used to create or find an integer atom.
string atom name A string atom name. Used to add a string atom name to an atom table and receive an atom in return.

+$#A Using Atoms
The most typical use for atoms is in DDE applications. In the DDE protocol, applications use global atoms to identify the applications exchanging data, the nature of the data being exchanged, and the actual data items being exchanged. For more information about DDE and for examples that show how to use atoms, see Dynamic Data Exchange_win32_Dynamic_Data_Exchange.
+$#A Atom Functions
Following are the functions associated with atoms.
AddAtom_win32_AddAtomDeleteAtom_win32_DeleteAtomFindAtom_win32_FindAtomGetAtomName_win32_GetAtomNameGlobalAddAtom_win32_GlobalAddAtomGlobalDeleteAtom_win32_GlobalDeleteAtomGlobalFindAtom_win32_GlobalFindAtomGlobalGetAtomName_win32_GlobalGetAtomNameInitAtomTable_win32_InitAtomTable
Obsolete or Deleted Functions
GetAtomHandle_win32_GetAtomHandle

+$#AK Communications
A communications resource is a physical or logical device that provides a single bidirectional, asynchronous data stream. Serial ports, parallel ports, fax machines, and modems are examples of communications resources. For each communications resource, there is a service provider, consisting of a library or driver, that enables applications to access the resource.
+$#A About Communications
The file input and output (I/O) functions (CreateFile_win32_CreateFile, CloseHandle_win32_CloseHandle, ReadFile_win32_ReadFile, ReadFileEx_win32_ReadFileEx, WriteFile_win32_WriteFile, and WriteFileEx_win32_WriteFileEx) provide the basic interface for opening and closing a communications resource handle and for performing read and write operations. The Microsoft® Win32® application programming interface (API) also includes a set of communications functions that provide access to communications resources. This overview describes the use of file I/O and communications functions, which enable applications to perform the following tasks:
· Open a handle to a specified communications resource.
· Set and query the configuration of a serial communications resource.
· Read from or write to a serial communications resource.
· Monitor a specified set of events that might occur for a given serial communications resource.
· Send a control command to the device driver associated with a specified communications resource, causing the driver to execute an extended function.

+$#A Communications Resource Handles
A process uses the CreateFile_win32_CreateFile function to open a handle to a communications resource. For example, specifying COM1 opens a handle to a serial port, and LPT1 opens a handle to a parallel port. If the specified resource is currently being used by another process, CreateFile fails. Any thread of the process can use the handle returned by CreateFile to identify the resource in any of the functions that access the resource.
When using CreateFile to open a handle directly to a device, an application must use the special characters " \\ .\" to identify the device. For example, to open a handle to drive A, specify " \\ .\a:" for the lpszName parameter of CreateFile. The calling process can use the handle in the DeviceIoControl_win32_DeviceIoControl function to send control codes to the device.
When the process calls CreateFile to open a communications resource, it specifies the following attributes:
· What type of read-write access exists for the specified resource.
· Whether the handle can be inherited by child processes.
· Whether the handle can be used in overlapped (asynchronous) I/O operations. (For a description of overlapped operations, see Synchronization_win32_Synchronization.)

When the process uses CreateFile_win32_CreateFile to open a communications resource, it must specify certain values for the following parameters:
· The fdwShareMode parameter must be zero, opening the resource for exclusive access.
· The fdwCreate parameter must specify the OPEN_EXISTING flag.
· The hTemplateFile parameter must be NULL.

+$#A Modification of Communications Resource Settings
When the CreateFile_win32_CreateFile function opens a handle to a serial communications resource, the system initializes and configures the resource according to the values set up the last time the resource was opened. Preserving the previous settings enables the user to retain the settings specified through an MS-DOS® mode command when the device is reopened. The values inherited from the previous open operation include the configuration settings of the device control block (a DCB_win32_DCB_str structure) and the time-out values used in I/O operations. If the device has never been opened, it is configured with the system defaults.
To determine the initial configuration of a serial communications resource, a process calls the GetCommState_win32_GetCommState function, which fills in a serial port DCB structure with the current configuration settings. To modify this configuration, a process specifies a DCB structure in a call to the SetCommState_win32_SetCommState function.
Members of the DCB structure specify the configuration settings such as the baud rate, the number of data bits per byte, and the number of stop bits per byte. Other DCB members specify special characters and enable parity checking and flow control. When a process needs to modify only a few of these configuration settings, it should first call GetCommState to fill in a DCB structure with the current configuration. Then the process can adjust the important values in the DCB structure and reconfigure the device by calling SetCommState and specifying the modified DCB structure. This procedure ensures that the unmodified members of the DCB structure contain appropriate values. For example, a common error is to configure a device with a DCB structure in which the structure's XonChar member is equal to the XoffChar member. Some members of the DCB structure are different from those in previous versions of Microsoft Windows®. In particular, the flags for controlling RTS (request-to-send) and DTR (data-terminal-ready) flow control have changed.
The BuildCommDCB_win32_BuildCommDCB function provides another way to modify a DCB structure. BuildCommDCB uses a string with the same form as the command-line arguments of the mode command to specify the baud rate, parity scheme, number of stop bits, and number of data bits. The remaining members of DCB_win32_DCB_str are not changed by this function, except that the appropriate members are set to disable XON/XOFF and hardware flow control. BuildCommDCB only modifies a DCB structure; it does not reconfigure the device.
A process can reconfigure a communications resource by using the GetCommProperties_win32_GetCommProperties function to get information from a device driver about the configuration settings that it supports. The process can use this information to avoid specifying a configuration that is not supported.
The SetCommState_win32_SetCommState function reconfigures the communications resource, but it does not affect the internal output and input buffers of the specified driver. The buffers are not flushed, and pending read and write operations are not terminated prematurely.
A process reinitializes a communications resource by using the SetupComm_win32_SetupComm function, which performs the following tasks:
· Terminates pending read and write operations, even if they have not been completed.
· Discards unread characters and frees the internal output and input buffers of the driver associated with the specified resource.
· Reallocates the internal output and input buffers.

A process is not required to call SetupComm_win32_SetupComm. If it does not, the resource's driver initializes the device with the default settings the first time that the communications resource handle is used.
+$#A Communications Resource Configuration
The COMMCONFIG_win32_COMMCONFIG_str structure defines the configuration of a communications resource, serial or otherwise. The format of the structure varies depending on the type of communications resource (the provider subtype). The first few structure members are common to all communications resources; additional members are defined for specific provider subtypes. Specific service providers may extend the COMMCONFIG structure as well.
An application can get and set the configuration of a communications resource by using the GetCommConfig_win32_GetCommConfig and SetCommConfig_win32_SetCommConfig functions. When opened, a communications resource is initialized using the default configuration for its provider subtype. To get and set the default configuration for a provider subtype, use the GetDefaultCommConfig_win32_GetDefaultCommConfig and SetDefaultCommConfig_win32_SetDefaultCommConfig functions.
To prompt the user for configuration information, use the CommConfigDialog_win32_CommConfigDialog function. This function displays a dialog box defined by the service provider and fills in a COMMCONFIG structure based on user input.
+$#A Modem Configuration
Modem configuration functions enable you to configure a modem before making a connection. An application can set modem options and determine the features of a modem without using commands specific to any modem device. Following are the general features an application may set before making a call:
· Primary mode of operation (synchronous, asynchronous, and whether error control is enabled).
· V.42 error control (defined by CCITT recommendation V.42), including specific parameters. CCITT stands for the International Telegraph and Telephone Consultative Committee.
· V.42bis (defined by CCITT recommendation V.42bis) and MNP5 data compression.
· Time-out options, including call setup, inactivity, and buffered data delivery.

Before setting a modem's configuration, an application should determine the capabilities of the modem device by using the GetCommProperties_win32_GetCommProperties function. This function fills in a COMMPROP_win32_COMMPROP_str structure. This structure contains both a general portion, which applies to all communications devices, and a portion that is specific to each provider subtype. For modem devices, the provider-specific portion of the COMMPROP structure is a MODEMDEVCAPS_win32_MODEMDEVCAPS_str structure.
An application can get and set the current configuration of a modem by using the GetCommConfig_win32_GetCommConfig and SetCommConfig_win32_SetCommConfig functions, both of which use a COMMCONFIG_win32_COMMCONFIG_str structure. This structure contains both a general portion, which applies to all communications devices, and a portion that is specific to each provider subtype. For modem devices, the provider-specific portion of the COMMCONFIG structure is a MODEMSETTINGS_win32_MODEMSETTINGS_str structure.
After configuring a modem, an application can use the telephony application programming interface (TAPI) to actually establish a connection.
The modem configuration functions do not provide for long-term management and maintenance of a modem. Modem service providers should supply modem configuration dialog boxes for this purpose.
+$#A Read and Write Operations
The Win32 API supports both synchronous and asynchronous (overlapped) file I/O operations on serial communications resources. Overlapped operations enable the calling thread to perform other tasks while the operation executes in the background. A thread uses the ReadFile_win32_ReadFile or ReadFileEx_win32_ReadFileEx function to read from a communications resource, and the WriteFile_win32_WriteFile or WriteFileEx_win32_WriteFileEx function to write to a communications resource. ReadFile and WriteFile can be performed synchronously or asynchronously. ReadFileEx and WriteFileEx can only be performed asynchronously.
The behavior of these read and write functions is affected by whether the function is executed as an overlapped operation, whether the time-out parameters are associated with the handle, and whether flow control parameters are associated with the handle.
A thread can also write to a communications resource by using the TransmitCommChar_win32_TransmitCommChar function, which transmits a specified character ahead of any pending data in the output buffer. This function is useful for transmitting a high priority signal character to the receiving system. Transmission of the high priority character is still subject to flow control and write time-outs, and the operation is performed synchronously.
A thread can use the PurgeComm_win32_PurgeComm function to discard all characters in a device's output or input buffer. PurgeComm can also terminate pending read or write operations, even if the operations have not been completed. If a thread uses PurgeComm to flush an output buffer, the deleted characters are not transmitted. To empty the output buffer while ensuring that the contents are transmitted, a thread can call the FlushFileBuffers_win32_FlushFileBuffers function (a synchronous operation). Note, however, that FlushFileBuffers is subject to flow control but not to write time-outs, and it will not return until all pending write operations have been transmitted.
+$#A Overlapped Operations
Overlapped operations enable a thread to execute a time-consuming I/O operation in the background, leaving the thread free to perform other tasks. To enable overlapped I/O operations on a communications resource, the thread must specify the FILE_FLAG_OVERLAPPED flag in the CreateFile_win32_CreateFile function when the handle is opened. To execute the ReadFile_win32_ReadFile or WriteFile_win32_WriteFile function as an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED_win32_OVERLAPPED_str structure.The OVERLAPPED structure must contain a handle to a manual-reset (not an auto-reset) event object. The system sets the state of the event object to not-signaled when a call to the I/O function returns before the operation has been completed. The system sets the state of the event object to signaled when the operation has been completed. The thread uses a wait function to check the current state of the event object or to wait for its state to be signaled.
The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions can be performed only as overlapped operations. The calling thread specifies a pointer to the FileIOCompletionRoutine_win32_FileIOCompletionRoutine function, which is executed when the overlapped operation is completed. The completion routine is executed only if the calling thread performs an alertable operation.
For more information about event objects, wait functions, alertable waits, and completion routines, see Synchronization_win32_Synchronization.
+$#A Time-Outs
A handle to a communications resource has an associated set of time-out parameters that affect the behavior of read and write operations. Time-outs can cause a ReadFile_win32_ReadFile, ReadFileEx_win32_ReadFileEx, WriteFile_win32_WriteFile, or WriteFileEx_win32_WriteFileEx operation to conclude when a time-out interval elapses, even though the specified number of characters have not been read or written. It is not treated as an error when a time-out occurs during a read or write operation (that is, the read or write function's return value indicates success). The count of bytes actually read or written is reported by ReadFile or WriteFile (or by the GetOverlappedResult_win32_GetOverlappedResult or FileIOCompletionRoutine_win32_FileIOCompletionRoutine function, if the I/O was performed as an overlapped operation).
When an application opens a communications resource, the operating system sets the resource's time-out values to the values in effect when the resource was last used. If the communications resource has never been opened, the operating system sets the time-out values to some default value. In either case, an application should always determine the current time-out values after opening the resource, and then explicitly set them to meet its requirements. To determine the current time-out values of a communications resource, use the GetCommTimeouts_win32_GetCommTimeouts function. To change the time-out values, use the SetCommTimeouts_win32_SetCommTimeouts function.
Two types of time-outs are enabled by the time-out parameters. An interval time-out occurs when the time between the receipt of any two characters exceeds a specified number of milliseconds. Timing starts when the first character is received and is restarted when each new character is received. A total time-out occurs when the total amount of time consumed by a read operation exceeds a calculated number of milliseconds. Timing starts immediately when the I/O operation begins. Write operations support only total time-outs. Read operations support both interval and total time-outs, which can be used separately or combined.
The time, in milliseconds, of the total time-out period for a read or write operation is calculated by using the multiplier and constant values from the COMMTIMEOUTS_win32_COMMTIMEOUTS_str structure specified in the GetCommTimeouts or SetCommTimeouts function. The following formula is used:
Timeout = (MULTIPLIER * number_of_bytes) + CONSTANT
Using both a multiplier and a constant enables the total time-out period to vary, depending on the amount of data being requested. An application can use only the constant by setting the multiplier to zero, or use only the multiplier by setting the constant to zero. If both the constant and multiplier are zero, total time-out is not used.
If all read time-out parameters are zero, read time-outs are not used, and a read operation is not complete until the requested number of bytes have been read or an error occurs. Similarly, if all write time-out parameters are zero, a write operation is not completed until the requested number of bytes have been written or an error occurs.
If the read interval time-out parameter is the MAXDWORD value and both read total time-out parameters are zero, a read operation is completed immediately after reading whatever characters are available in the input buffer, even if it is empty. This is the same as the ReadComm function in previous versions of Windows.
Interval timing forces a read operation to return when there is a lull in reception. A process using interval time-outs can set a fairly short interval parameter, so it can respond quickly to small, isolated bursts of one or a few characters, yet it can still collect large buffers of characters with a single call when data is received in a steady stream.
Time-outs for a write operation can be useful when transmission is blocked by some kind of flow control or when the SetCommBreak_win32_SetCommBreak function has been called to suspend character transmission.
The following table summarizes the behavior of read operations based on the values specified for total and interval time-outs.
Total Interval Behavior
0 0 Returns when the buffer is completely filled. Time-outs are not used.
T 0 Returns when the buffer is completely filled or when T milliseconds have elapsed since the beginning of the operation.
0 Y Returns when the buffer is completely filled or when Y milliseconds have elapsed between the receipt of any two characters. Timing does not begin until the first character is received.
T Y Returns when the buffer is completely filled or when either type of time-out occurs.

Note, however, that timing is relative to the system controlling the physical device. For a remote device such as a modem, the timing is relative to the server system to which the modem is attached. Any network propagation delay is not factored in. For example, a client application might specify a total time-out that computes to be 500 milliseconds. When 500 milliseconds have elapsed at the server, a time-out error is returned to the client. If there is a 50 milliseconds network propagation delay, the client will not be notified of the time-out until approximately 50 milliseconds after the time-out actually occurred.
The time-out parameters affect the behavior of overlapped read and write operations on a communications device. With overlapped I/O, the ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, ReadFileEx_win32_ReadFileEx, or WriteFileEx_win32_WriteFileEx function can return before the operation has been completed. The time-out parameters can determine when the operation has been completed.
+$#A Communications Errors
There are other circumstances where a read or write operation can be completed with fewer than the requested number of characters, even though a time-out has not occurred. Following are some examples:
· Some drivers support the use of special characters, which complete a read operation immediately with only the characters that have been read up to the point when they are received.
· The PurgeComm_win32_PurgeComm function can be called to prematurely terminate pending read or write operations. This function can also delete the contents of the output or input buffers, or both.
· If a communications error occurs during a read or write operation, all I/O operations on the communications resource are terminated. Break conditions, parity errors, or framing errors are examples of such errors. When an error occurs, the process must call the ClearCommError_win32_ClearCommError function to clear the error flag before it can begin additional I/O operations. ClearCommError reports the specific error that occurred and the current status of the device.

+$#A Communications Events
A process can monitor a set of events that occur in a communications resource. For example, an application can use event monitoring to determine when the CTS (clear-to-send) and DSR (data-set-ready) signals change state.
A process can monitor events on a given communications resource by using the SetCommMask_win32_SetCommMask function to create an event mask. To determine the current event mask for a communications resource, a process can use the GetCommMask_win32_GetCommMask function. The following values specify events that can be monitored.
Value Meaning
EV_BREAK A break was detected on input.
EV_CTS The CTS (clear-to-send) signal changed state.
EV_DSR The DSR (data-set-ready) signal changed state.
EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING A ring indicator was detected.
EV_RLSD The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR A character was received and placed in the input buffer.
EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the device's DCB_win32_DCB_str structure, which is applied to a serial port by using the SetCommState_win32_SetCommState function.
EV_TXEMPTY The last character in the output buffer was sent.

After a set of events is specified, a process uses the WaitCommEvent_win32_WaitCommEvent function to wait for one of the events to occur. WaitCommEvent can be used synchronously or as an overlapped operation. For additional information about executing a function as an overlapped operation, see Synchronization_win32_Synchronization.
When one of the events specified in the event mask occurs, the process completes the wait operation and sets an event mask variable to indicate the type of event detected. If the SetCommMask_win32_SetCommMask is called for a communications resource while a wait is pending for that resource, WaitCommEvent returns an error.
The WaitCommEvent function detects events that have occurred since the last call to SetCommMask_win32_SetCommMask or WaitCommEvent. For example, if you specify the EV_RXCHAR event as a wait-satisfying event, a call to WaitCommEvent will be satisfied if there are characters in the driver's input buffer that have arrived since the last call to WaitCommEvent or SetCommMask. Thus, given the following pseudo-code,
while (we_care) {
WaitCommEvent
T1: // Read bytes
// process them
T2: }

... any characters received between T1 and T2 will satisfy the next call to WaitCommEvent.
When monitoring an event that occurs when a signal (CTS, DSR, and so on) changes state, WaitCommEvent reports the change, but not the current state. To query the current state of the CTS (clear-to-send), DSR (data-set-ready), RLSD (receive-line-signal-detect), and ring indicator signals, a process can use the GetCommModemStatus_win32_GetCommModemStatus function.
+$#A Extended Functions
Some communications functions can be called for a device by using the EscapeCommFunction_win32_EscapeCommFunction function. This function sends a code to direct the device to execute an extended function. For example, an application can suspend character transmission with the SETBREAK code and resume transmission with the CLRBREAK code. These particular operations can also be started by calling the SetCommBreak_win32_SetCommBreak and ClearCommBreak_win32_ClearCommBreak functions. EscapeCommFunction can also be used to implement manual modem control. For example, the CLRDTR and SETDTR codes can be used to implement manual DTR (data-terminal-ready) flow control. Note, however, that an error occurs if a process uses EscapeCommFunction to manipulate the DTR line when the device has been configured to enable DTR handshaking, or the RTS (request-to-send) line if RTS handshaking is enabled.
The DeviceIoControl_win32_DeviceIoControl function enables a process to send an extended function code directly to a specified device driver, causing the device to perform a given operation. DeviceIoControl gives a device associated with a communications resource capabilities not supported by the standard serial communications functions. It enables an application to configure a device using parameters unique to that device as well as to call any device-specific functions.
+$#A Using the Communications Functions
· Configuring a communications resource_win32_Configuring_a_Communications_Resource
· Monitoring communications events_win32_Monitoring_Communications_Events

+$#A Configuring a Communications Resource
The following example opens a handle to COM1 and fills in a DCB_win32_DCB_str structure with the current configuration. The DCB structure is then modified and used to reconfigure the device.
DCB dcb;HANDLE hCom;DWORD dwError;BOOL fSuccess;hCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, /* comm devices must be opened w/exclusive-access */ NULL, /* no security attrs */ OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */ 0, /* not overlapped I/O */ NULL /* hTemplate must be NULL for comm devices */ );if (hCom == INVALID_HANDLE_VALUE) { dwError = GetLastError(); /* handle error */}/* * Omit the call to SetupComm to use the default queue sizes. * Get the current configuration. */fSuccess = GetCommState(hCom, &dcb);if (!fSuccess) { /* Handle the error. *}/* Fill in the DCB: baud=9600, 8 data bits, no parity, 1 stop bit. */dcb.BaudRate = 9600;dcb.ByteSize = 8;dcb.Parity = NOPARITY;dcb.StopBits = ONESTOPBIT;fSuccess = SetCommState(hCom, &dcb);if (!fSuccess) { /* Handle the error. *}
+$#A Monitoring Communications Events
The following example code opens the serial port for overlapped I/O, creates an event mask to monitor CTS and DSR signals, and then waits for an event to occur. The WaitCommEvent_win32_WaitCommEvent function should be executed as an overlapped operation so the other threads of the process cannot perform I/O operations during the wait.
HANDLE hCom;OVERLAPPED o;BOOL fSuccess;DWORD dwEvtMask;hCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, /* exclusive access */ NULL, /* no security attrs */ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );if (hCom == INVALID_HANDLE_VALUE) { /* Deal with the error. */}/* Set the event mask. */fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR);if (!fSuccess) { /* deal with error */}/* Create an event object for use in WaitCommEvent. */o.hEvent = CreateEvent(NULL, /* no security attributes */ FALSE, /* auto reset event */ FALSE, /* not signaled */ NULL /* no name */ );assert(o.hEvent);if (WaitCommEvent(hCom, &dwEvtMask, &o)) { if (dwEvtMask & EV_DSR) { /* * . . . */ } if (dwEvtMask & EV_CTS) { /* * . . . */ }}
+$#A Communication Functions
Following are the functions used with communications devices.
BuildCommDCB_win32_BuildCommDCBBuildCommDCBAndTimeouts_win32_BuildCommDCBAndTimeoutsClearCommBreak_win32_ClearCommBreakClearCommError_win32_ClearCommErrorCommConfigDialog_win32_CommConfigDialogDeviceIoControl_win32_DeviceIoControlEscapeCommFunction_win32_EscapeCommFunctionGetCommConfig_win32_GetCommConfigGetCommMask_win32_GetCommMaskGetCommModemStatus_win32_GetCommModemStatusGetCommProperties_win32_GetCommPropertiesGetCommState_win32_GetCommStateGetCommTimeouts_win32_GetCommTimeoutsPurgeComm_win32_PurgeCommSetCommBreak_win32_SetCommBreakSetCommConfig_win32_SetCommConfigSetCommMask_win32_SetCommMaskSetCommState_win32_SetCommStateSetCommTimeouts_win32_SetCommTimeoutsSetupComm_win32_SetupCommTransmitCommChar_win32_TransmitCommCharWaitCommEvent_win32_WaitCommEvent

+$#AK Consoles and Character-Mode Support
The Microsoft® Win32® application programming interface (API) provides consoles that manage input and output (I/O) for character-mode applications (applications that do not provide their own graphical user interface).
This overview describes Win32 support for character-mode applications.
+$#A About Character-Mode Support
Consoles provide high-level support for simple character-mode applications that interact with the user by using functions that read from standard input and write to standard output or standard error. Consoles also provide sophisticated low-level support that gives direct access to a console's screen buffer and that enables applications to receive extended input information (such as mouse input).
+$#A Consoles
A console is an interface that provides I/O to character-mode applications. This processor-independent mechanism makes it easy to port existing character-mode applications or to create new character-mode tools and applications.
A console consists of an input buffer and one or more screen buffers. The input buffer contains a queue of input records, each of which contains information about an input event. The input queue always includes key-press and key-release events. It can also include mouse events (pointer movements and button presses and releases) and events during which user actions affect the size of the active screen buffer. A screen buffer is a two-dimensional array of character and color data for output in a console window.
The Win32 API functions enable different levels of access to a console. The high-level console I/O functions enable an application to read from standard input to retrieve keyboard input stored in a console's input buffer. The functions also enable an application to write to standard output or standard error to display text in the console's screen buffer. The high-level functions also support redirection of standard handles and control of console modes for different I/O functionality. The low-level console I/O functions enable applications to receive detailed input about keyboard and mouse events, as well as events involving user interactions with the console window. The low-level functions also enable greater control of output to the screen.
+$#A Creation of a Console
Microsoft Windows® creates a new console when it starts a console process, a character-mode process whose entry point is the main function. For example, Windows creates a new console when it starts the command processor. When the command processor starts a new console process, the user can specify whether the system creates a new console for the new process or whether it inherits the command processor's console. A process can create a console by using one of the following methods:
· A graphical user interface (GUI) or console process that is not currently attached to a console can use the AllocConsole_win32_AllocConsole function to create a new console.
· A GUI or console process can use the CreateProcess_win32_CreateProcess function to create a new console process and specify a flag to tell the system to create a new console.

Typically, a process uses AllocConsole to create a console when an error occurs requiring interaction with the user. For example, a GUI process can create a console when an error occurs that prevents it from using its normal graphical interface, or a console process that does not normally interact with the user can create a console when an error makes it necessary. The process that calls AllocConsole must not be attached to an existing console. GUI processes are not attached to any console when they are created. A console process is not attached to a console if the DETACHED_PROCESS flag was specified in a call to CreateProcess when the process was created. A process can use the FreeConsole_win32_FreeConsole function to detach itself from an inherited console or from a console created by AllocConsole.
A process can also create a console by specifying the CREATE_NEW_CONSOLE flag in a call to CreateProcess. This method creates a new console that is accessible to the child process but not to the parent process. Separate consoles enable both parent and child processes to interact with the user without conflict. If this flag is not specified when a console process is created, both processes are attached to the same console, and there is no guarantee that the correct process will receive the input intended for it. Applications can prevent confusion by creating child processes that do not inherit handles of the input buffer, or by enabling only one child process at a time to inherit an input buffer handle while preventing the parent process from reading console input until the child has finished.
Creating a new console results in a new console window, as well as separate I/O screen buffers. The process associated with the new console uses the GetStdHandle_win32_GetStdHandle function to get the handles of the new console's input and screen buffers. These handles enable the process to access the console.
When a process uses CreateProcess_win32_CreateProcess, it can specify a STARTUPINFO_win32_STARTUPINFO_str structure, whose members control the characteristics of the first new console (if any) created for the child process. The STARTUPINFO structure specified in the call to CreateProcess affects a console created if the CREATE_NEW_CONSOLE flag is specified. It also affects a console created if the child process subsequently uses AllocConsole_win32_AllocConsole. The following console characteristics can be specified:
· The size, in character cells, and the location, in screen pixel coordinates, of the new console window
· The size, in character cells, of the new console's screen buffer
· The text and background color attributes of the new console's screen buffer
· The name appearing in the title bar of the new console's window

The system uses default values if the STARTUPINFO_win32_STARTUPINFO_str values are not specified. A child process can use the GetStartupInfo_win32_GetStartupInfo function to determine the values in its STARTUPINFO structure.
A process cannot change the location of its console window on the screen, but the following console functions are available to set or retrieve the other properties specified in the STARTUPINFO structure.
Function Description
GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo Retrieves the window size, screen buffer size, and color attributes.
SetConsoleWindowInfo_win32_SetConsoleWindowInfo Changes the size of the console window.
SetConsoleScreenBufferSize_win32_SetConsoleScreenBufferSize Changes the size of the screen buffer.
SetConsoleTextAttribute_win32_SetConsoleTextAttribute Sets the color attributes.
SetConsoleTitle_win32_SetConsoleTitle Sets the console window title.
GetConsoleTitle_win32_GetConsoleTitle Retrieves the console window title.

+$#A Closing a Console
A process can use the FreeConsole_win32_FreeConsole function to detach itself from its console. If other processes share the console, the console is not destroyed, but the process that called FreeConsole cannot refer to it. After calling FreeConsole, the process can use AllocConsole_win32_AllocConsole to create a new console.
A console is closed when the last process attached to it terminates or calls FreeConsole.
+$#A Console Handles
A console process uses handles to access the input and screen buffers of its console. A process can use the GetStdHandle_win32_GetStdHandle, CreateFile_win32_CreateFile, or CreateConsoleScreenBuffer_win32_CreateConsoleScreenBuffer function to open one of these handles.
The GetStdHandle function provides a mechanism for retrieving the standard input (STDIN), standard output (STDOUT), and standard error (STDERR) handles associated with a process. During console creation, the system creates these handles. Initially, STDIN is a handle of the console's input buffer, and STDOUT and STDERR are handles of the console's active screen buffer. However, the SetStdHandle_win32_SetStdHandle function can redirect the standard handles by changing the handle associated with STDIN, STDOUT, or STDERR. Because the parent's standard handles are inherited by any child process, subsequent calls to GetStdHandle return the redirected handle. A handle returned by GetStdHandle may, therefore, refer to something other than console I/O. For example, before creating a child process, a parent process can use SetStdHandle to set a pipe handle to be the STDIN handle that is inherited by the child process. When the child process calls GetStdHandle, it gets the pipe handle. This means that the parent process can control the standard handles of the child process. The handles returned by GetStdHandle have GENERIC_READ | GENERIC_WRITE access unless SetStdHandle has been used to set the standard handle to have lesser access.
The value of the handles returned by GetStdHandle will not be 0, 1, and 2, so the standard predefined stream constants in the STDIO.H header file (STDIN, STDOUT, and STDERR) cannot be used in functions that require a console handle.
The CreateFile function enables a process to get a handle of its console's input buffer and active screen buffer, even if STDIN and STDOUT have been redirected. To open a handle of a console's input buffer, specify the CONIN$ value in a call to CreateFile. Specify the CONOUT$ value in a call to CreateFile to open a handle of a console's active screen buffer. CreateFile enables you to specify the read-write access of the handle that it returns.
The CreateConsoleScreenBuffer_win32_CreateConsoleScreenBuffer function creates a new screen buffer and returns a handle, which can be used in any function that accepts a handle of console output. The new screen buffer is not active until its handle is specified in a call to the SetConsoleActiveScreenBuffer_win32_SetConsoleActiveScreenBuffer function. Note that changing the active screen buffer does not affect the handle returned by GetStdHandle_win32_GetStdHandle. Similarly, using SetStdHandle to change the STDOUT handle does not affect the active screen buffer.
Console handles returned by CreateFile_win32_CreateFile and CreateConsoleScreenBuffer can be used in any of the console functions that require a handle of a console's input buffer or of a console screen buffer. Handles returned by GetStdHandle can be used by the console functions if they have not been redirected to refer to something other than console I/O. If a standard handle has been redirected to refer to a file or a pipe, however, the handle can only be used by the ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions.
A process can use the DuplicateHandle_win32_DuplicateHandle function to create a duplicate console handle that has different access or inheritability from the original handle. Note, however, that a process can create a duplicate console handle only for its own use. This differs from other handle types (such as file, pipe, or mutex objects), for which DuplicateHandle can create a duplicate that is valid for a different process.
To close a console handle, a process can use the CloseHandle_win32_CloseHandle function.
+$#A Console Input Buffer
Each console has an input buffer that contains a queue of input event records. When a console's window has the keyboard focus, a console formats each input event (such as a single keystroke, a movement of the mouse, or a mouse-button click) as an input record that it places in the console's input buffer.
Applications can access a console's input buffer indirectly by using the high-level console I/O functions, or directly by using the low-level console I/O functions. The high-level input functions filter and process the data in the input buffer, returning only a stream of input characters. The low-level input functions enable applications to read input records directly from a console's input buffer, or to place input records into the input buffer.
An input record is a structure containing information about the type of event that occurred (keyboard, mouse, window resizing, focus, or menu event) as well as specific details about the event. The EventType member in an INPUT_RECORD_win32_INPUT_RECORD_str structure indicates which type of event is contained in the record.
Focus and menu events are placed in a console's input buffer for internal use by the system and should be ignored by applications.
Keyboard Events
Keyboard events are generated when any key is pressed or released; this includes control keys. However, the ALT key has special meaning to Windows when pressed and released without being combined with another character, and it is not passed through to the application. Also, the CTRL+C key combination is not passed through if the input handle is in processed mode.
If the input event is a keystroke, the Event member in INPUT_RECORD_win32_INPUT_RECORD_str is a KEY_EVENT_RECORD_win32_KEY_EVENT_RECORD_str structure containing the following information:
· A Boolean value indicating whether the key was pressed or released.
· A repeat count that can be greater than one when a key is held down.
· The virtual-key code, identifying the given key in a device-independent manner.
· The virtual-scan code, indicating the device-dependent value generated by the keyboard hardware.
· The translated Unicode™ or ANSI character.
· A flag variable indicating the state of the control keys (the ALT, CTRL, SHIFT, NUM LOCK, SCROLL LOCK, and CAPS LOCK keys) and indicating whether an enhanced key was pressed. Enhanced keys for the IBM® 101-key and 102-key keyboards are the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad and the divide (/) and ENTER keys in the numeric keypad.

Mouse Events
Mouse events are generated whenever the user moves the mouse or presses or releases one of the mouse buttons. Mouse events are placed in the input buffer only if the following conditions are met:
· The console input mode is set to ENABLE_MOUSE_INPUT (the default mode).
· The console window has the keyboard focus.
· The mouse pointer is within the borders of the console's window.

If the input event is a mouse event, the Event member in INPUT_RECORD_win32_INPUT_RECORD_str is a MOUSE_EVENT_RECORD_win32_MOUSE_EVENT_RECORD_str structure containing the following information:
· The coordinates of the mouse pointer in terms of the character-cell row and column in the screen buffer's coordinate system.
· A flag variable indicating the state of the mouse buttons.
· A flag variable indicating the state of the control keys (ALT, CTRL, SHIFT, NUM LOCK, SCROLL LOCK, and CAPS LOCK) and indicating whether an enhanced key was pressed. Enhanced keys for the IBM 101-key and 102-key keyboards are the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad and the divide (/) and ENTER keys in the numeric keypad.
· A flag variable indicating whether the event was a normal button-press or button-release event, a mouse movement event, or the second click of a double-click event.

Note that the mouse position coordinates are in terms of the screen buffer, not the console window. The screen buffer may have been scrolled with respect to the window, so the upper left corner of the window is not necessarily the (0,0) coordinate of the screen buffer. To determine the coordinates of the mouse relative to the coordinate system of the window, subtract the window origin coordinates from the mouse position coordinates. Use the GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo function to determine the window origin coordinates.
The dwButtonState member of the MOUSE_EVENT_RECORD_win32_MOUSE_EVENT_RECORD_str structure has a bit corresponding to each mouse button. The bit is 1 if the button is down and 0 if the button is up. A button-release event is detected by a 0 value for the dwEventFlags member of MOUSE_EVENT_RECORD and a change in a button's bit from 1 to 0. The GetNumberOfConsoleMouseButtons_win32_GetNumberOfConsoleMouseButtons function retrieves the number of buttons on the mouse.
Buffer-Resizing Events
A console window's menu enables the user to change the size of the active screen buffer; this change generates a buffer-resizing event. Buffer-resizing events are placed in the input buffer if the console's input mode is set to ENABLE_WINDOW_INPUT (that is, the default mode is disabled).
If the input event is a buffer-resizing event, the Event member of INPUT_RECORD_win32_INPUT_RECORD_str is a WINDOW_BUFFER_SIZE_RECORD_win32_WINDOW_BUFFER_SIZE_RECORD_str structure containing the new size of the screen buffer, expressed in character-cell columns and rows.
If the user reduces the size of the screen buffer, any data in the discarded portion of the buffer is lost.
Changes to the screen buffer size as a result of application calls to the SetConsoleScreenBufferSize_win32_SetConsoleScreenBufferSize function are not generated as buffer-resizing events.
+$#A Console Screen Buffers
A screen buffer is a two-dimensional array of character and color data for output in a console window. A console can have multiple screen buffers. The active screen buffer is the one that is displayed on the screen.
The system creates a screen buffer whenever it creates a new console. A process can use the CreateConsoleScreenBuffer_win32_CreateConsoleScreenBuffer function to create additional screen buffers for its console. A new screen buffer is not active until its handle is specified in a call to the SetConsoleActiveScreenBuffer_win32_SetConsoleActiveScreenBuffer function. However, screen buffers can be accessed for reading and writing whether they are active or inactive.
Each screen buffer has its own two-dimensional array of character information records. The data for each character is stored in a CHAR_INFO_win32_CHAR_INFO_str structure that specifies the Unicode or ANSI character and the foreground and background colors in which that character is displayed.
A number of properties associated with a screen buffer can be set independently for each screen buffer. This means that changing the active screen buffer can have a dramatic effect on the appearance of the console window. The properties associated with a screen buffer include:
· Screen buffer size, in character rows and columns.
· Text attributes (foreground and background colors for displaying text to be written by the WriteFile_win32_WriteFile or WriteConsole_win32_WriteConsole function).
· Window size and location (the rectangular region of the screen buffer that is displayed in the console window).
· Cursor position, appearance, and visibility.
· Output modes (ENABLE_PROCESSED_OUTPUT and ENABLE_WRAP_AT_EOL_OUTPUT). For more information about console output modes, see High-Level Console Modes_win32_High_Level_Console_Modes.

When a screen buffer is created, it contains blanks. Its cursor is visible and positioned at the buffer's origin (0,0), and the window is positioned with its upper left corner at the buffer's origin. The size of the screen buffer, the window size, the text attributes, and the appearance of the cursor are determined by the user or by the system defaults. To retrieve the current values of the various properties associated with the screen buffer, use the GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo, GetConsoleCursorInfo_win32_GetConsoleCursorInfo, and GetConsoleMode_win32_GetConsoleMode functions.
Applications that change any of the screen buffer properties should either create their own screen buffer or save the state of the inherited screen buffer during startup and restore it at exit.
+$#A Window and Screen Buffer Size
The size of a screen buffer is expressed in terms of a coordinate grid based on character cells. The width is the number of character cells in each row, and the height is the number of rows. Associated with each screen buffer is a window that determines the size and location of the rectangular portion of the screen buffer displayed in the console window. A screen buffer's window is defined by specifying the character-cell coordinates of the upper left and lower right cells of the window's rectangle.
A screen buffer can be any size, limited only by available memory. The dimensions of a screen buffer's window cannot exceed the corresponding dimensions of either the screen buffer or the maximum window that can fit on the screen based on the current font size (controlled exclusively by the user).
The GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo function returns the following information about a screen buffer and its window:
· The current size of the screen buffer
· The current location of the window
· The maximum size of the window given the current screen buffer size, the current font size, and the screen size

The GetLargestConsoleWindowSize_win32_GetLargestConsoleWindowSize function returns the maximum size of a console's window based on the current font and screen sizes. This size differs from the maximum window size returned by GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo in that the screen buffer size is ignored.
To change a screen buffer's size, use the SetConsoleScreenBufferSize_win32_SetConsoleScreenBufferSize function. This function fails if either dimension of the specified size is less than the corresponding dimension of the console's window.
To change the size or location of a screen buffer's window, use the SetConsoleWindowInfo_win32_SetConsoleWindowInfo function. This function fails if the specified window-corner coordinates exceed the limits of the screen buffer or the screen. Changing the window size of the active screen buffer changes the size of the console window displayed on the screen.
A process can change its console's input mode to enable window input so that the process is able to receive input when the user changes the screen buffer size. If an application enables window input, it can use GetConsoleScreenBufferInfo to retrieve window and screen buffer size at startup. This information can then be used to determine the way data is displayed in the window. If the user changes the screen buffer size, the application can respond by changing the way data is displayed. For example, an application can adjust the way text wraps at the end of the line if the number of characters per row changes. If an application does not enable window input, it must either use the inherited window and screen buffer sizes, or set them to the desired size during startup and restore the inherited sizes at exit. For additional information about window input mode, see Low-Level Console Modes_win32_Low_Level_Console_Modes.
+$#A Scrolling the Screen Buffer
The console window displays a portion of the active screen buffer. Each screen buffer maintains its own current window rectangle that specifies the coordinates of the upper left and lower right character cells to be displayed in the console window. To determine the current window rectangle of a screen buffer, use GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo. When a screen buffer is created, the upper left corner of its window is at the upper left corner of the screen buffer at (0,0).
The window rectangle can change to display different parts of the screen buffer. The window rectangle of a screen buffer can change in the following situations:
· When SetConsoleWindowInfo_win32_SetConsoleWindowInfo is called to specify a new window rectangle, it scrolls the view of the screen buffer by changing the position of the window rectangle without changing the size of the window. For examples of scrolling the window's contents, see Scrolling a Screen Buffer's Window_win32_Scrolling_a_Screen_Buffer_s_Window.
{ewc msdncd, EWGraphic, group10738 0 /a "SDK_01.bmp"}
· When using the WriteFile_win32_WriteFile function to write to a screen buffer with wrap at end-of-line (EOL) output mode enabled, the window rectangle shifts automatically, so the cursor is always displayed.
· When the SetConsoleCursorPosition_win32_SetConsoleCursorPosition function specifies a new cursor position that is outside the boundaries of the current window rectangle, the window rectangle shifts automatically to display the cursor.
· When the user changes the size of the console window or uses the window's scroll bars, the window rectangle of the active screen buffer can change. This change is not reported as a window resizing event in the input buffer.

In each of these situations, the window rectangle shifts to display a different part of the screen buffer, but the contents of the screen buffer remain in the same position. The following situations can cause the screen buffer's contents to shift:
· When the ScrollConsoleScreenBuffer_win32_ScrollConsoleScreenBuffer function is called, a rectangular block is copied from one part of a screen buffer to another.
· When using WriteFile_win32_WriteFile to write to a screen buffer with wrap at EOL output mode enabled, the screen buffer's contents scroll automatically when the end of the screen buffer is encountered. This scrolling discards the top row of the screen buffer.

ScrollConsoleScreenBuffer specifies the screen buffer rectangle that is moved and the new upper left coordinates to which the rectangle is copied. This function can scroll a portion or the entire contents of the screen buffer.
The illustration shows a ScrollConsoleScreenBuffer_win32_ScrollConsoleScreenBuffer operation that scrolls the entire contents of the screen buffer up by several rows. The contents of the top rows are discarded, and the bottom rows are filled with a specified character and color.
{ewc msdncd, EWGraphic, group10738 1 /a "SDK_02.bmp"}
The effects of ScrollConsoleScreenBuffer can be limited by specifying an optional clipping rectangle so that the contents of the screen buffer outside the clipping rectangle are unchanged. The effect of clipping is to create a subwindow (the clipping rectangle) whose contents are scrolled without affecting the rest of the screen buffer. For an example that uses ScrollConsoleScreenBuffer, see Scrolling a Screen Buffer's Contents_win32_Scrolling_a_Screen_Buffer_s_Contents.
Cursor Appearance and Position
A screen buffer's cursor can be visible or hidden. When it is visible, its appearance can vary, ranging from from completely filling a character cell to appearing as a horizontal line at the bottom of the cell. To retrieve information about the appearance and visibility of the cursor, use the GetConsoleCursorInfo_win32_GetConsoleCursorInfo function. This function reports whether the cursor is visible and describes the appearance of the cursor as the percentage of a character cell that it fills. To set the appearance and visibility of the cursor, use the SetConsoleCursorInfo_win32_SetConsoleCursorInfo function.
Characters written by the high-level console I/O functions are written at the current cursor location, advancing the cursor to the next location. To determine the current cursor position in the coordinate system of a screen buffer, use GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo. You can use SetConsoleCursorPosition_win32_SetConsoleCursorPosition to set the cursor position and, thereby, control the placement of text that is written or echoed by the high-level I/O functions. If you move the cursor, text at the new cursor location is overwritten.
The position, appearance, and visibility of the cursor are set independently for each screen buffer.
Screen Buffer Color Attributes
Each screen buffer character cell stores the color attributes for the colors used in drawing the text and background of that cell. An application can set the color data for each character cell individually, storing the data in the Attributes member of the CHAR_INFO_win32_CHAR_INFO_str structure for each cell.
The current text attributes of each screen buffer determine the foreground (text) and background colors of characters subsequently written or echoed by the high-level functions. An application can use GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo to determine the current text attributes of a screen buffer and the SetConsoleTextAttribute_win32_SetConsoleTextAttribute function to set the text attributes. Changing a screen buffer's text attributes does not affect the display of characters previously written. These text attributes do not affect characters written by the low-level console I/O functions (such as the WriteConsoleOutput_win32_WriteConsoleOutput or WriteConsoleOutputCharacter_win32_WriteConsoleOutputCharacter function) which either explicitly specify the attributes for each cell that is written or leave the attributes unchanged at the written positions.
The following attribute constants are defined in the WINCON.H header file:
· FOREGROUND_BLUE
· FOREGROUND_GREEN
· FOREGROUND_RED
· FOREGROUND_INTENSITY
· BACKGROUND_BLUE
· BACKGROUND_GREEN
· BACKGROUND_RED
· BACKGROUND_INTENSITY

The foreground attributes specify the text color, and the background attributes specify the color used to fill the cell's background. An application can combine the constants to achieve different colors. For example, the following combination results in bright cyan text on a blue background.
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_BLUE

If no background constant is specified, the background is black, and if no foreground constant is specified, the text is black. For example, the following combination produces black text on a white background.
BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED

+$#A Input and Output Methods
The Win32 API provides two very different approaches to console I/O, the choice of which depends on how much flexibility and control an application needs. The high-level approach enables simple character stream I/O, but it limits access to a console's input and screen buffers. The low-level approach requires that developers write more code and choose among a greater range of functions, but it also gives an application more flexibility.
An application can use the file I/O functions, ReadFile_win32_ReadFile and WriteFile_win32_WriteFile, and the console functions, ReadConsole_win32_ReadConsole and WriteConsole_win32_WriteConsole, for high-level I/O that provides indirect access to a console's input and screen buffers. The high-level input functions filter and process the data in a console's input buffer to return input as a stream of characters, discarding mouse and buffer-resizing input. Similarly, the high-level output functions write a stream of characters that are displayed at the current cursor location in a screen buffer. An application controls the way these functions work by setting a console's I/O modes.
The low-level I/O functions provide direct access to a console's input and screen buffers, enabling an application to access mouse and buffer-resizing input events and extended information for keyboard events. Low-level output functions enable an application to read from or write to a specified number of consecutive character cells in a screen buffer, or to read or write to rectangular blocks of character cells at a specified location in a screen buffer. A console's input modes affect low-level input by enabling the application to determine whether mouse and buffer-resizing events are placed in the input buffer. A console's output modes have no effect on low-level output.
The high-level and low-level I/O methods are not mutually exclusive, and an application can use any combination of these functions. Typically, however, an application uses one approach or the other exclusively.
The sections that follow describe in detail the console modes and the high-level and low-level I/O functions.
+$#A Console Modes
Associated with each console input buffer are a set of input modes that affects input operations. Similarly, each console screen buffer has a set of output modes that affects output operations. The input modes can be divided into two groups: those that affect the high-level input functions and those that affect the low-level input functions. The output modes only affect applications that use the high-level output functions.
The GetConsoleMode_win32_GetConsoleMode function reports the current input mode of a console's input buffer or the current output mode of a screen buffer. The SetConsoleMode_win32_SetConsoleMode function sets the current mode of either a console input buffer or a screen buffer. If a console has multiple screen buffers, the output modes of each can be different. An application can change I/O modes at any time. For more information about the console modes that affect high-level and low-level I/O operations, see High-Level Console Modes_win32_High_Level_Console_Modes and Low-Level Console Modes_win32_Low_Level_Console_Modes.
+$#A High-Level Console I/O
The high-level I/O functions provide a simple way to read a stream of characters from console input or to write a stream of characters to console output. A high-level read operation gets input characters from a console's input buffer and stores them in a specified buffer. A high-level write operation takes characters from a specified buffer and writes them to a screen buffer at the current cursor location, advancing the cursor as each character is written.
High-level I/O gives you a choice between the ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions and the ReadConsole_win32_ReadConsole and WriteConsole_win32_WriteConsole functions. They are identical, except for two important differences. The console functions support the use of either Unicode characters or the ANSI character set; the file I/O functions do not support Unicode. Also, the file I/O functions can be used to access files, pipes, and serial communications devices; the console functions can only be used with console handles. This distinction is important if an application relies on standard handles that may have been redirected.
When using either set of high-level functions, an application can control the text and background colors used to display characters subsequently written to a screen buffer. An application can also use the console modes that affect high-level console I/O to enable or disable the following properties:
· Echoing of keyboard input to the active screen buffer
· Line input, in which a read operation does not return until the ENTER key is pressed
· Automatic processing of keyboard input to handle carriage returns, CTRL+C, and other input details
· Automatic processing of output to handle line wrapping, carriage returns, backspaces, and other output details

+$#A High-Level Console Modes
The behavior of the high-level console functions is affected by the console input and output modes. All of the following console input modes are enabled for a console's input buffer when a console is created:
· Line input mode
· Processed input mode
· Echo input mode

Both of the following console output modes are enabled for a console screen buffer when it is created:
· Processed output mode
· Wrapping at EOL output mode

All three input modes, along with processed output mode, are designed to work together. It is best to either enable or disable all of these modes as a group. When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application. When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.
An application can use the GetConsoleMode_win32_GetConsoleMode function to determine the current mode of a console's input buffer or screen buffer. You can enable or disable any of these modes by using the following values in the SetConsoleMode_win32_SetConsoleMode function. Note that setting the output mode of one screen buffer does not affect the output mode of other screen buffers.
Mode Description
ENABLE_PROCESSED_INPUT
Used with a console input handle to cause the system to process any system editing or control key input rather than returning it as input in the read operation's buffer. If line input is also enabled, backspaces and carriage returns are handled correctly. A backspace causes the cursor to move back one space without affecting the character at the cursor position. A carriage return is converted to carriage return - linefeed character combination. If echo input mode is enabled and the output should reflect system editing, processed output must be enabled for the active screen buffer. If processed input is enabled, the CTRL+C key combination is passed on to the appropriate handler regardless of whether line input is enabled. For more information about control handlers, see Console Control Handlers_win32_Console_Control_Handlers.
ENABLE_LINE_INPUT
Used with a console input handle to cause the ReadFile_win32_ReadFile and ReadConsole_win32_ReadConsole functions to return when the ENTER key is pressed. If line input mode is disabled, the functions return when one or more characters are available in the input buffer.
ENABLE_ECHO_INPUT
Used with a console input handle to cause keyboard input read by the ReadFile_win32_ReadFile or ReadConsole_win32_ReadConsole function to be echoed to the active screen buffer. Characters are echoed only if the process that calls ReadFile or ReadConsole has an open handle of the active screen buffer. Echo mode cannot be enabled unless line input is also enabled. The output mode of the active screen buffer affects the way echoed input is displayed.
ENABLE_PROCESSED_OUTPUT
Used with a console screen buffer handle to cause the system to perform the appropriate action for ANSI control characters that are written to a screen buffer. The backspace, tab, bell, carriage return, and linefeed characters are processed. A tab character moves the cursor to the next tab stop, which occurs every eight characters. A bell character sounds a short tone.
ENABLE_WRAP_AT_EOL_OUTPUT
Used with a console screen buffer handle to cause the current output position (cursor position) to move to the first column in the next row (line) when the end of the current row is reached. If the bottom of the window region is reached, the window origin is moved down one row. This movement has the effect of scrolling the contents of the window up one row. If the bottom of the screen buffer is reached, the contents of the screen buffer are scrolled up one row, and the top row of the screen buffer is discarded. If this mode is disabled, the last character in the row is overwritten with any subsequent characters.

+$#A High-Level Console Input and Output Functions
The ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions, or the ReadConsole_win32_ReadConsole and WriteConsole_win32_WriteConsole functions, enable an application to read console input and write console output as a stream of characters. ReadConsole and WriteConsole behave exactly like ReadFile and WriteFile except that they can be used either as wide-character functions (in which text arguments must use Unicode) or as ANSI functions (in which text arguments must use characters from the Windows 3.x character set). Applications that need to maintain a single set of sources to support either Unicode or the ANSI character set should use ReadConsole and WriteConsole.
ReadConsole and WriteConsole can only be used with console handles; ReadFile and WriteFile can be used with other handles (such as files or pipes). ReadConsole and WriteConsole fail if used with a standard handle that has been redirected and is no longer a console handle.
To get keyboard input, a process can use ReadFile or ReadConsole with a handle of the console's input buffer, or it can use ReadFile to read input from a file or a pipe if STDIN has been redirected. These functions only return keyboard events that can be translated into ANSI characters (or Unicode characters in the case of ReadConsole). The input that can be returned includes control key combinations. The functions do not return keyboard events involving the function keys or arrow keys. Input events generated by mouse, window, focus, or menu input are discarded.
If line input mode is enabled (the default mode), ReadFile and ReadConsole do not return to the calling application until the ENTER key is pressed. If line input mode is disabled, the functions do not return until at least one character is available. In either mode, all available characters are read until either no more keys are available or the specified number of characters has been read. Unread characters are buffered until the next read operation. The functions report the total number of characters actually read. If echo input mode is enabled, characters read by these functions are written to the active screen buffer at the current cursor position.
A process can use WriteFile_win32_WriteFile or WriteConsole_win32_WriteConsole to write to either an active or inactive screen buffer, or it can use WriteFile to write to a file or a pipe if STDOUT has been redirected. Processed output mode and wrap at EOL output mode control the way characters are written or echoed to a screen buffer.
Characters written by WriteFile or WriteConsole, or echoed by ReadFile_win32_ReadFile or ReadConsole_win32_ReadConsole, are inserted in a screen buffer at the current cursor position. As each character is written, the cursor position advances to the next character cell; however, the behavior at the end of a row depends on the screen buffer's wrap at EOL output mode. An application can use the GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo function to determine the current cursor position and the SetConsoleCursorPosition_win32_SetConsoleCursorPosition function to set the cursor position.
For an example that uses the high-level console I/O functions, see Using the High-Level Input and Output Functions_win32_Using_the_High_Level_Input_and_Output_Functions.
+$#A Low-Level Console I/O
The low-level console I/O functions expand an application's control over console I/O by enabling direct access to a console's input and screen buffers. These functions enable an application to perform the following tasks:
· Receive input about mouse and buffer-resizing events
· Receive extended information about keyboard input events
· Write input records to the input buffer
· Read input records without removing them from the input buffer
· Determine the number of pending events in the input buffer
· Flush the input buffer
· Read and write strings of Unicode or ANSI characters at a specified location in a screen buffer
· Read and write strings of text and background color attributes at a specified screen buffer location
· Read and write rectangular blocks of character and color data at a specified screen buffer location
· Write a single Unicode or ANSI character, or a text and background color attribute combination, to a specified number of consecutive cells beginning at a specified screen buffer location

+$#A Low-Level Console Modes
The types of input events reported in a console's input buffer depend on the console's mouse and window input modes. The console's processed input mode determines how the system handles the CTRL+C key combination. To set or retrieve the state of a console's input modes, an application can specify a console input buffer handle in a call to the SetConsoleMode_win32_SetConsoleMode or GetConsoleMode_win32_GetConsoleMode function. The following modes are used with console input handles.
Mode Description
ENABLE_MOUSE_INPUT
Controls whether mouse events are reported in the input buffer. By default, mouse input is enabled and window input is disabled. Changing either of these modes affects only input that occurs after the mode is set; pending mouse or window events in the input buffer are not flushed. The mouse pointer is displayed regardless of the mouse mode.
ENABLE_WINDOW_INPUT
Controls whether buffer-resizing events are reported in the input buffer. By default, mouse input is enabled and window input is disabled. Changing either of these modes affects only input that occurs after the mode is set; pending mouse or window events in the input buffer are not flushed. The mouse pointer is displayed regardless of the mouse mode.
ENABLE_PROCESSED_INPUT
Controls the processing of input for applications using the high-level console I/O functions. However, if processed input mode is enabled, the CTRL+C key combination is not reported in the console's input buffer. Instead, it is passed on to the appropriate control handler function. For more information about control handlers, see Console Control Handlers_win32_Console_Control_Handlers.

The output modes of a screen buffer do not affect the behavior of the low-level output functions.
+$#A Low-Level Console Input Functions
A console's input buffer contains input records that can include information about keyboard, mouse, buffer-resizing, focus, and menu events. The low-level functions provide direct access to the input buffer, unlike the high-level functions that filter and process the input buffer's data, discarding all but keyboard input.
The Win32 API provides five low-level functions for accessing a console's input buffer:
· ReadConsoleInput_win32_ReadConsoleInput
· PeekConsoleInput_win32_PeekConsoleInput
· GetNumberOfConsoleInputEvents_win32_GetNumberOfConsoleInputEvents
· WriteConsoleInput_win32_WriteConsoleInput
· FlushConsoleInputBuffer_win32_FlushConsoleInputBuffer

The ReadConsoleInput, PeekConsoleInput, and WriteConsoleInput functions use the INPUT_RECORD_win32_INPUT_RECORD_str structure to read from or write to an input buffer.
Following are descriptions of the low-level console input functions.
Function Description
ReadConsoleInput_win32_ReadConsoleInput Reads and removes input records from an input buffer. The function does not return until at least one record is available to be read. Then all available records are transferred to the buffer of the calling process until either no more records are available or the specified number of records has been read. Unread records remain in the input buffer for the next read operation. The function reports the total number of records that have been read. For an example that uses ReadConsoleInput, see Reading Input Buffer Events_win32_Reading_Input_Buffer_Events.
PeekConsoleInput_win32_PeekConsoleInput Reads without removing the pending input records in an input buffer. All available records up to the specified number are copied into the buffer of the calling process. If no records are available, the function returns immediately. The function reports the total number of records that have been read.
GetNumberOfConsoleInputEvents_win32_GetNumberOfConsoleInputEvents Determines the number of unread input records in an input buffer.
WriteConsoleInput_win32_WriteConsoleInput Places input records into the input buffer behind any pending records in the buffer. The input buffer grows dynamically, if necessary, to hold as many records as are written. To use this function, the specified input buffer handle must have GENERIC_WRITE access.
FlushConsoleInputBuffer_win32_FlushConsoleInputBuffer Discards all unread events in the input buffer. To use this function, the specified input buffer handle must have GENERIC_WRITE access.

A thread of an application's process can perform a wait operation to wait for input to be available in an input buffer. To initiate a wait operation, specify a handle of the input buffer in a call to any of the wait functions (WaitForSingleObject_win32_WaitForSingleObject, WaitForMultipleObjects_win32_WaitForMultipleObjects, WaitForSingleObjectEx_win32_WaitForSingleObjectEx, or WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx). These functions can return when the state of one or more objects is signaled. The state of a console input handle becomes signaled when there are unread records in its input buffer. The state is reset to nonsignaled when the input buffer becomes empty. If there is no input available, the calling thread enters an efficient wait state, consuming very little processor time while waiting for the conditions of the wait operation to be satisfied.
+$#A Low-Level Console Output Functions
The low-level console output functions provide direct access to the character cells of a screen buffer. One set of functions reads from or writes to consecutive cells beginning at any location in the screen buffer. Another set of functions reads from or writes to rectangular blocks of cells.
The following functions read from or write to a specified number of consecutive character cells in a screen buffer, beginning with a specified cell.
Function Description
ReadConsoleOutputCharacter_win32_ReadConsoleOutputCharacter
Copies a string of Unicode or ANSI characters from a screen buffer.
WriteConsoleOutputCharacter_win32_WriteConsoleOutputCharacter
Writes a string of Unicode or ANSI characters to a screen buffer.
ReadConsoleOutputAttribute_win32_ReadConsoleOutputAttribute
Copies a string of text and background color attributes from a screen buffer.
WriteConsoleOutputAttribute_win32_WriteConsoleOutputAttribute
Writes a string of text and background color attributes to a screen buffer.
FillConsoleOutputCharacter_win32_FillConsoleOutputCharacter
Writes a single Unicode or ANSI character to a specified number of consecutive cells in a screen buffer.
FillConsoleOutputAttribute_win32_FillConsoleOutputAttribute
Writes a text and background color attribute combination to a specified number of consecutive cells in a screen buffer.

For all of these functions, when the last cell of a row is encountered, reading or writing wraps around to the first cell of the next row. When the end of the last row of the screen buffer is encountered, the write functions discard all unwritten characters or attributes, and the read functions report the number of characters or attributes actually written..
The following functions read from or write to rectangular blocks of character cells at a specified location in a screen buffer.
Function Description
ReadConsoleOutput_win32_ReadConsoleOutput
Copies character and color data from a specified block of screen buffer cells into a given block in a destination buffer.
WriteConsoleOutput_win32_WriteConsoleOutput
Writes character and color data to a specified block of screen buffer cells from a given block in a source buffer.

These functions treat screen buffers and source or destination buffers as two-dimensional arrays of CHAR_INFO_win32_CHAR_INFO_str structures (containing character and color attribute data for each cell). The functions specify the width and height, in character cells, of the source or destination buffer, and the pointer to the buffer is treated as a pointer to the origin cell (0,0) of the two-dimensional array. The functions use a SMALL_RECT_win32_SMALL_RECT_str structure to specify which rectangle to access in the screen buffer, and the coordinates of the upper left cell in the source or destination buffer determine the location of the corresponding rectangle in that buffer.
These functions automatically clip the specified screen buffer rectangle to fit within the boundaries of the screen buffer. For example, if the rectangle specifies lower right coordinates that are (column 100, row 50) and the screen buffer is only 80 columns wide, the coordinates are clipped so that they are (column 79, row 50). Similarly, this adjusted rectangle is again clipped to fit within the boundaries of the source or destination buffer. The screen buffer coordinates of the actual rectangle that was read from or written to are specified. For an example that uses these functions, see Reading and Writing Blocks of Characters and Attributes_win32_Reading_and_Writing_Blocks_of_Characters_and_Attributes.
The illustration shows a ReadConsoleOutput_win32_ReadConsoleOutput operation where clipping occurs when the block is read from the screen buffer, and again when the block is copied into the destination buffer. The function reports the actual screen buffer rectangle that it copied from.
{ewc msdncd, EWGraphic, group10738 2 /a "SDK_03.bmp"}
+$#A Console Code Pages
A code page is a mapping of 256 character codes to individual characters. Different code pages include different special characters, typically customized for a language or a group of languages.
Associated with each console are two code pages: one for input and one for output. A console uses its input code page to translate keyboard input into the corresponding character value. It uses its output code page to translate the character values written by the various output functions into the images displayed in the console window. An application can use the SetConsoleCP_win32_SetConsoleCP and GetConsoleCP_win32_GetConsoleCP functions to set and retrieve a console's input code pages and the SetConsoleOutputCP_win32_SetConsoleOutputCP and GetConsoleOutputCP_win32_GetConsoleOutputCP functions to set and retrieve its output code pages.
The identifiers of the code pages available on the local computer are stored in the registry under the following key.
%HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage%

For information about using the registry functions to determine the available code pages, see Registry_win32_Registry.
For more information about code pages, see String Manipulation_win32_String_Manipulation.
+$#A Console Control Handlers
Each console process has its own list of control handler functions that are called by the system when the process receives a CTRL+C, CTRL+BREAK, or CTRL+CLOSE signal. Initially, the list of control handlers for each process contains only a default handler function that calls the ExitProcess_win32_ExitProcess function. A console process can add or remove additional handler functions by calling the SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler function. This function does not affect the lists of control handlers for other processes. When a console process receives any of the control signals, it calls the handler functions on a last-registered, first-called basis until one of the handlers returns TRUE. If none of the handlers returns TRUE, the default handler is called.
The following typedef declaration illustrates the format of a control handler function.
typedef BOOL (*PHANDLER_ROUTINE)(DWORD dwCtrlType);

The function's dwCtrlType parameter identifies which control signal was received, and the return value indicates whether the signal was handled.
For an example of a control handler function, see Registering a Control Handler Function_win32_Registering_a_Control_Handler_Function.
+$#A CTRL+C and CTRL+BREAK Signals
The CTRL+C and CTRL+BREAK key combinations receive special handling by console processes. By default, when a console window has the keyboard focus, CTRL+C or CTRL+BREAK is treated as a signal (SIGINT or SIGBREAK) and not as keyboard input. By default, these signals are passed to all console processes that are attached to the console, causing the system to call the control handler function or functions associated with these processes. Detached processes (GUI processes or console processes started with the DETACHED_PROCESS or CREATE_NEW_CONSOLE flag) are not affected.
CTRL+BREAK is always treated as a signal, but an application can change the default CTRL+C behavior in two ways that prevent the handler functions from being called:
· The SetConsoleMode_win32_SetConsoleMode function can disable the ENABLE_PROCESSED_INPUT input mode for a console's input buffer, so CTRL+C is reported as keyboard input rather than as a signal.
· When SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler is called with NULL and TRUE values for its parameters, the calling process ignores CTRL+C signals. Normal CTRL+C processing is restored by calling SetConsoleCtrlHandler with NULL and FALSE values. This attribute of ignoring or not ignoring CTRL+C signals is inherited by child processes, but it can be enabled or disabled by any process without affecting existing processes.

+$#A CTRL+CLOSE Signal
The system generates a CTRL+CLOSE signal when the user closes a console. All processes attached to the console receive the signal, giving each process an opportunity to clean up before termination. When a process receives this signal, the handler function can take one of the following actions after performing any cleanup operations:
· Call ExitProcess_win32_ExitProcess to terminate the process.
· Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.
· Return TRUE. In this case, no other handler functions are called, and a pop-up dialog box asks the user whether to terminate the process. If the user chooses not to terminate the process, the system does not close the console until the process finally terminates.

+$#A Console Process Groups
When a process uses the CreateProcess_win32_CreateProcess function to create a new console process, it can specify the CREATE_NEW_PROCESS_GROUP flag to make the new process the root process of a console process group. The process group includes all processes that are descendants of the root process.
A process can use the GenerateConsoleCtrlEvent_win32_GenerateConsoleCtrlEvent function to send a CTRL+C or CTRL+BREAK signal to all processes in a console process group. The signal is only received by those processes in the group that are attached to the same console as the process that called GenerateConsoleCtrlEvent.
+$#A Using the Console
· Using the high-level input and output functions_win32_Using_the_High_Level_Input_and_Output_Functions
· Writing characters or colors to consecutive cells_win32_Writing_Characters_or_Colors_to_Consecutive_Cells
· Reading and writing blocks of characters and attributes_win32_Reading_and_Writing_Blocks_of_Characters_and_Attributes
· Reading input buffer events_win32_Reading_Input_Buffer_Events
· Scrolling a screen buffer's window_win32_Scrolling_a_Screen_Buffer_s_Window
· Scrolling a screen buffer's contents_win32_Scrolling_a_Screen_Buffer_s_Contents
· Registering a control handler function_win32_Registering_a_Control_Handler_Function
· Console application issues_win32_Console_Application_Issues

+$#A Using the High-Level Input and Output Functions
The following example uses the high-level console I/O functions for console I/O. For more information about the high-level console I/O functions, see High-Level Console I/O_win32_High_Level_Console_I_O.
The example assumes that the default I/O modes are in effect initially for the first calls to the ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions. Then the input mode is changed to turn off line input mode and echo input mode for the second calls to ReadFile and WriteFile. The SetConsoleTextAttribute_win32_SetConsoleTextAttribute function is used to set the colors in which subsequently written text will be displayed. Before exiting, the program restores the original console input mode and color attributes.
The example's NewLine function is used when line input mode is disabled. It handles carriage returns by moving the cursor position to the first cell of the next row. If the cursor is already in the last row of the screen buffer, the contents of the screen buffer are scrolled up one line. For an example that illustrates the use of the ScrollConsoleScreenBuffer_win32_ScrollConsoleScreenBuffer function to scroll a screen buffer, see Scrolling a Screen Buffer's Contents_win32_Scrolling_a_Screen_Buffer_s_Contents.
#include <windows.h>

VOID NewLine(VOID);
VOID ScrollScreenBuffer(HANDLE, INT);

HANDLE hStdout, hStdin;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

VOID main(void) {
LPSTR lpszPrompt1 = "Type something and press Enter:\n";
LPSTR lpszPrompt2 = "Type any key: ";
CHAR chBuffer[256];
DWORD cRead, cWritten, fdwMode, fdwOldMode;
WORD wOldColorAttrs;

/* Get handles to STDIN and STDOUT. */

hStdin = GetStdHandle(STD_INPUT_HANDLE);
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdin == INVALID_HANDLE_VALUE ||
hStdout == INVALID_HANDLE_VALUE)
MyErrorExit("GetStdHandle");

/* Save the current text colors. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
MyErrorExit("GetConsoleScreenBufferInfo");
wOldColorAttrs = csbiInfo.wAttributes;

/* Set the text attr. to draw red text on black background. */

if (! SetConsoleTextAttribute(hStdout, FOREGROUND_RED))
MyErrorExit("SetConsoleTextAttribute");

/*
* Write to STDOUT and read from STDIN by using the default
* modes. Input is echoed automatically, and ReadFile
* does not return until a carriage return is typed.
*
* The default input modes are line, processed, and echo.
* The default output modes are processed and wrap at EOL.
*/

while (1) {
if (! WriteFile(
hStdout, /* output handle */
lpszPrompt1, /* prompt string */
lstrlen(lpszPrompt1), /* string length */
&cWritten, /* bytes written */
NULL) ) /* not overlapped */
break;
if (! ReadFile(
hStdin, /* input handle */
chBuffer, /* buffer to read into */
255, /* size of buffer */
&cRead, /* actual bytes read */
NULL) ) /* not overlapped */
break;
if (chBuffer[0] == 'q') break;
}

/* Turn off the line input mode, and echo the input mode. */

if (! GetConsoleMode(hStdin, &fdwOldMode))
MyErrorExit("GetConsoleMode");
fdwMode = fdwOldMode &
~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
if (! SetConsoleMode(hStdin, fdwMode))
MyErrorExit("SetConsoleMode");

/* Prompt for input. */

if (! WriteFile(
hStdout, /* output handle */
lpszPrompt2, /* prompt string */
lstrlen(lpszPrompt2), /* string length */
&cWritten, /* bytes written */
NULL) ) /* not overlapped */
MyErrorExit("WriteFile");

/*
* Without line and echo input modes, ReadFile returns
* when any input is available. Carriage returns must
* be handled, and WriteFile is used to echo input.
*/

while (1) {
if (! ReadFile(hStdin, chBuffer, 1, &cRead, NULL))
break;
if (chBuffer[0] == '\r')
NewLine();
else
if (! WriteFile(hStdout, chBuffer, cRead,
&cWritten, NULL))
break;
if (chBuffer[0] == 'q') break;
}

/* Restore the original console mode. */

if (! SetConsoleMode(hStdin, fdwOldMode))
MyErrorExit("SetConsoleMode");

/* Restore the original text colors. */

if (! SetConsoleTextAttribute(hStdout, wOldColorAttrs))
MyErrorExit("SetConsoleTextAttribute");

}

/*
* The NewLine function handles carriage returns when the processed
* input mode is disabled. It gets the current cursor position
* and resets it to the first cell of the next row.
*/

VOID NewLine(VOID) {

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
MyErrorExit("GetConsoleScreenBufferInfo");
csbiInfo.dwCursorPosition.X = 0;

/*
* If it is the last line in the screen buffer, scroll
* the buffer up.
*/

if ((csbiInfo.dwSize.Y-1) == csbiInfo.dwCursorPosition.Y) {
ScrollScreenBuffer(hStdout, 1);
}

/* Otherwise, advance the cursor to the next line. */

else
csbiInfo.dwCursorPosition.Y += 1;

if (! SetConsoleCursorPosition(hStdout,
csbiInfo.dwCursorPosition))
MyErrorExit("SetConsoleCursorPosition");
}

+$#A Writing Characters or Colors to Consecutive Cells
Characters or color attributes can be written to specified character cells in a screen buffer. The following example uses the WriteConsoleOutputCharacter_win32_WriteConsoleOutputCharacter function to write a string of characters beginning at the upper left corner of a screen buffer. Then the example uses the WriteConsoleOutputAttribute_win32_WriteConsoleOutputAttribute function to write a string of color attributes to the first 51 cells of the same row. The coord parameter for both functions specifies the character cell in the screen buffer at which writing begins. The location in the console window where these characters or colors appear depends on the current window rectangle of the screen buffer. For additional information about the relationship between a screen buffer and its windows, see Window and Screen Buffer Size_win32_Window_and_Screen_Buffer_Size and Scrolling the Screen Buffer_win32_Scrolling_the_Screen_Buffer.
HANDLE hOutput;
LPTSTR lpszString = "Character String";
DWORD cWritten;
BOOL fSuccess;
COORD coord;
WORD wColors[3], wColor;
CHAR chFillChar;

/* Write a string of characters to a screen buffer. */

coord.X = 0; /* start at first cell */
coord.Y = 0; /* of first row */
fSuccess = WriteConsoleOutputCharacter(
hOutput, /* screen buffer handle */
lpszString, /* pointer to source string */
lstrlen(lpszString), /* length of string */
coord, /* first cell to write to */
&cWritten); /* actual number written to */
if (! fSuccess)
MyErrorExit("WriteConsoleOutputCharacter");

/* Write a string of colors to a screen buffer. */

wColors[0] = BACKGROUND_RED;
wColors[1] = BACKGROUND_RED | /* white background */
BACKGROUND_GREEN |
BACKGROUND_BLUE;
wColors[2] = BACKGROUND_BLUE;
for (;fSuccess && coord.X < 50; coord.X += 3)
fSuccess = WriteConsoleOutputAttribute(
hOutput, /* screen buffer handle */
wColors, /* pointer to source string */
3, /* length of string */
coord, /* first cell to write to */
&cWritten); /* actual number written to */
if (! fSuccess)
MyErrorExit("WriteConsoleOutputAttribute");

The same character or color attribute can be written to a specified number of consecutive screen buffer cells beginning at a specified location. The following example uses the FillConsoleOutputCharacter_win32_FillConsoleOutputCharacter function to clear a 80-by-50-character screen buffer, and then it uses the FillConsoleOutputAttribute_win32_FillConsoleOutputAttribute function to set the color attributes of the same cells.
/* Fill an 80-by-50-character screen buffer with the space character. */

coord.X = 0; /* start at first cell */
coord.Y = 0; /* of first row */
chFillChar = ' ';

fSuccess = FillConsoleOutputCharacter(
hStdout, /* screen buffer handle */
chFillChar, /* fill with spaces */
80*50, /* number of cells to fill */
coord, /* first cell to write to */
&cWritten); /* actual number written to */
if (! fSuccess)
MyErrorExit("FillConsoleOutputCharacter");

/* Set 80-by-50-character screen buffer colors to white text on red. */

wColor = BACKGROUND_RED |
FOREGROUND_RED |
FOREGROUND_GREEN |
FOREGROUND_BLUE;
fSuccess = FillConsoleOutputAttribute(
hStdout, /* screen buffer handle */
wColor, /* color to fill with */
80*50, /* number of cells to fill */
coord, /* first cell to write to */
&cWritten); /* actual number written to */
if (! fSuccess)
MyErrorExit("FillConsoleOutputAttribute");

+$#A Reading and Writing Blocks of Characters and Attributes
The ReadConsoleOutput_win32_ReadConsoleOutput function copies a rectangular block of character and color attribute data from a console screen buffer into a destination buffer. The function treats the destination buffer as a two-dimensional array of CHAR_INFO_win32_CHAR_INFO_str structures. Similarly, the WriteConsoleOutput_win32_WriteConsoleOutput function copies a rectangular block of character and color attribute data from a source buffer to a console screen buffer. For more information about reading from or writing to rectangular blocks of screen buffer cells, see Input and Output Methods_win32_Input_and_Output_Methods.
The following example uses the CreateConsoleScreenBuffer_win32_CreateConsoleScreenBuffer function to create a new screen buffer. After the SetConsoleActiveScreenBuffer_win32_SetConsoleActiveScreenBuffer function makes this the active screen buffer, a block of characters and color attributes is copied from the top two rows of the SDTOUT screen buffer into a temporary buffer. The data is then copied from the temporary buffer into the new active screen buffer. When the application is finished using the new screen buffer, it calls SetConsoleActiveScreenBuffer to restore the original STDOUT screen buffer.
#include <windows.h>

VOID main(void) {
HANDLE hStdout, hNewScreenBuffer;
SMALL_RECT srctReadRect;
SMALL_RECT srctWriteRect;
CHAR_INFO chiBuffer[160]; // [2][80];
COORD coordBufSize;
COORD coordBufCoord;
BOOL fSuccess;

/*
* Get a handle of the STDOUT screen buffer to copy from and
* create a new screen buffer to copy to.
*/

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hNewScreenBuffer = CreateConsoleScreenBuffer(
GENERIC_READ | /* read-write access */
GENERIC_WRITE,
0, /* not shared */
NULL, /* no security attributes */
CONSOLE_TEXTMODE_BUFFER, /* must be TEXTMODE */
NULL); /* reserved; must be NULL */
if (hStdout == INVALID_HANDLE_VALUE ||
hNewScreenBuffer == INVALID_HANDLE_VALUE)
MyErrorExit("CreateConsoleScreenBuffer");

/* Make the new screen buffer the active screen buffer. */

if (! SetConsoleActiveScreenBuffer(hNewScreenBuffer) )
MyErrorExit("SetConsoleActiveScreenBuffer");

/* Set the source rectangle. */

srctReadRect.Top = 0; /* top left: row 0, col 0 */
srctReadRect.Left = 0;
srctReadRect.Bottom = 1; /* bot. right: row 1, col 79 */
srctReadRect.Right = 79;

/* The temporary buffer size is 2 rows x 80 columns. */

coordBufSize.Y = 2;
coordBufSize.X = 80;

/*
* The top left destination cell of the temporary buffer is
* row 0, col 0.
*/

coordBufCoord.X = 0;
coordBufCoord.Y = 0;

/* Copy the block from the screen buffer to the temp. buffer. */

fSuccess = ReadConsoleOutput(
hStdout, /* screen buffer to read from */
chiBuffer, /* buffer to copy into */
coordBufSize, /* col-row size of chiBuffer */
coordBufCoord, /* top left dest. cell in chiBuffer */
&srctReadRect); /* screen buffer source rectangle */
if (! fSuccess)
MyErrorExit("ReadConsoleOutput");

/* Set the destination rectangle. */

srctWriteRect.Top = 10; /* top lt: row 10, col 0 */
srctWriteRect.Left = 0;
srctWriteRect.Bottom = 11; /* bot. rt: row 11, col 79 */
srctWriteRect.Right = 79;

/* Copy from the temporary buffer to the new screen buffer. */

fSuccess = WriteConsoleOutput(
hNewScreenBuffer, /* screen buffer to write to */
chiBuffer, /* buffer to copy from */
coordBufSize, /* col-row size of chiBuffer */
coordBufCoord, /* top left src cell in chiBuffer */
&srctWriteRect); /* dest. screen buffer rectangle */
if (! fSuccess)
MyErrorExit("WriteConsoleOutput");
Sleep(10000);

/* Restore the original active screen buffer. */

if (! SetConsoleActiveScreenBuffer(hStdout))
MyErrorExit("SetConsoleActiveScreenBuffer");

}

+$#A Reading Input Buffer Events
The ReadConsoleInput_win32_ReadConsoleInput function can be used to directly access a console's input buffer. When a console is created, mouse input is enabled and window input is disabled. To ensure that the process receives all types of events, this example uses the SetConsoleMode_win32_SetConsoleMode function to enable window and mouse input. Then it goes into a loop that reads and handles console input events.
VOID MouseEventProc(MOUSE_EVENT_RECORD);
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);
VOID KeyEventProc(KEY_EVENT_RECORD);
VOID GetInputEvents(VOID);

DWORD main(VOID) {
HANDLE hStdin;
DWORD cNumRead, fdwMode, fdwSaveOldMode, i;
INPUT_RECORD irInBuf[128];

/* Get the standard input handle. */

hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin == INVALID_HANDLE_VALUE)
MyErrorExit("GetStdHandle");

/* Save the current input mode, to be restored on exit. */

if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
MyErrorExit("GetConsoleMode");

/* Enable the window and mouse input events. */

fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
if (! SetConsoleMode(hStdin, fdwMode) )
MyErrorExit("SetConsoleMode");

/* Loop to read and handle the input events. */

while (1) {

/* Wait for the events. */

if (! ReadConsoleInput(
hStdin, /* input buffer handle */
irInBuf, /* buffer to read into */
128, /* size of read buffer */
&cNumRead) ) /* number of records read */
MyErrorExit("ReadConsoleInput");

/* Dispatch the events to the appropriate handler. */

for (i = 0; i < cNumRead; i++)
switch(irInBuf[i].EventType) {

case KEY_EVENT: /* keyboard input */
KeyEventProc(irInBuf[i].Event.KeyEvent);
break;

case MOUSE_EVENT: /* mouse input */
MouseEventProc(irInBuf[i].Event.MouseEvent);
break;

case WINDOW_BUFFER_SIZE_EVENT: /* scrn buf. resizing */
ResizeEventProc(
irInBuf[i].Event.WindowBufferSizeEvent);
break;

case FOCUS_EVENT: /* disregard focus events */

case MENU_EVENT: /* disregard menu events */
break;

default:
MyErrorExit("unknown event type");
break;
}

}

return 0;

}

+$#A Scrolling a Screen Buffer's Window
The SetConsoleWindowInfo_win32_SetConsoleWindowInfo function can be used to scroll the contents of a screen buffer in the console window. This function can also change the window size. The function can either specify the new upper left and lower right corners of the screen buffer's window as absolute screen buffer coordinates or specify the changes from the current window coordinates. The function fails if the specified window coordinates are outside the boundaries of the screen buffer.
The following example scrolls the view of the screen buffer up one row by modifying the absolute window coordinates returned by the GetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfo function.
HANDLE hStdout;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
SMALL_RECT srctWindow;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Get the current screen buffer size and window position. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
MyErrorExit("GetConsoleScreenBufferInfo");

/* Set srctWindow to the current window size and location. */

srctWindow = csbiInfo.srWindow;

/* If window is not at the screen buffer top, move it up one line. */

if ( srctWindow.Top > 0 ) {
srctWindow.Top -= 1; /* move top up by one row */
srctWindow.Bottom -= 1; /* move bottom up by one row */
if (! SetConsoleWindowInfo(
hStdout, /* screen buffer handle */
TRUE, /* absolute coordinates */
&srctWindow)) /* specifies new location */
MyErrorExit("SetConsoleWindowInfo");
}

The same scrolling can be done by specifying changes in the window coordinates.
/* Get the current screen buffer window position. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
MyErrorExit("GetConsoleScreenBufferInfo");

/*If window is not at the screen buffer top, move it up one line. */

if (csbiInfo.srWindow.Top > 0) {
srctWindow.Top = -1; /* move top up by one row */
srctWindow.Bottom = -1; /* move bottom up by one row */
srctWindow.Left = 0; /* no change */
srctWindow.Right = 0; /* no change */
if (! SetConsoleWindowInfo(
hStdout, /* screen buffer handle */
FALSE, /* deltas, not absolute */
&srctWindow)) /* specifies new location */
MyErrorExit("SetConsoleWindowInfo");
}

+$#A Scrolling a Screen Buffer's Contents
The ScrollConsoleScreenBuffer_win32_ScrollConsoleScreenBuffer function moves a block of character cells from one part of a screen buffer to another part of the same screen buffer. The function specifies the upper left and lower right cells of the source rectangle to be moved and the destination coordinates of the new location for the upper left cell. The character and color data in the source cells is moved to the new location, and any cells left empty by the move are filled in with a specified character and color. If a clipping rectangle is specified, the cells outside of it are left unchanged.
ScrollConsoleScreenBuffer can be used to delete a line by specifying coordinates of the first cell in the line as the destination coordinates and specifying a scrolling rectangle that includes all the rows below the line.
The following example shows the use of a clipping rectangle to scroll only the bottom 15 rows of the screen buffer. The rows in the specified rectangle are scrolled up one line at a time, and the top row of the block is discarded. The contents of the screen buffer outside the clipping rectangle are left unchanged.
HANDLE hStdout;
BOOL fSuccess;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
SMALL_RECT srctScrollRect, srctClipRect;
CHAR_INFO chiFill;
COORD coordDest;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdout == INVALID_HANDLE_VALUE)
MyErrorExit("GetStdHandle");

/* Get the screen buffer size. */

fSuccess = GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
if (! fSuccess)
MyErrorExit("GetConsoleScreenBufferInfo");

/*
* The scrolling rectangle is the bottom 15 rows of the
* screen buffer.
*/

srctScrollRect.Top = csbiInfo.dwSize.Y - 16;
srctScrollRect.Bottom = csbiInfo.dwSize.Y - 1;
srctScrollRect.Left = 0;
srctScrollRect.Right = csbiInfo.dwSize.X - 1;

/* The destination for the scroll rectangle is one row up. */

coordDest.X = 0;
coordDest.Y = csbiInfo.dwSize.Y - 17;

/*
* The clipping rectangle is the same as the scrolling rectangle.
* The destination row is left unchanged.
*/

srctClipRect = srctScrollRect;

/* Fill the bottom row with green blanks. */

chiFill.Attributes = BACKGROUND_GREEN | FOREGROUND_RED;
chiFill.Char.AsciiChar = ' ';

/* Scroll up one line. */

fSuccess = ScrollConsoleScreenBuffer(
hStdout, /* screen buffer handle */
&srctScrollRect, /* scrolling rectangle */
&srctClipRect, /* clipping rectangle */
coordDest, /* top left destination cell*/
&chiFill); /* fill character and color */

+$#A Registering a Control Handler Function
This section shows an example of the SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler function that is used to install a control handler.
When a CTRL+C signal is received, the control handler returns TRUE, indicating that it has handled the signal. Doing this prevents other control handlers from being called.
When a CTRL_CLOSE_EVENT signal is received, the control handler returns TRUE, causing the system to display a dialog box that gives the user the choice of terminating the process and closing the console or allowing the process to continue execution. If the user chooses not to terminate the process, the system closes the console when the process finally terminates.
When a CTRL+BREAK, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT signal is received, the control handler returns FALSE. Doing this causes the signal to be passed to the next control handler function. If no other control handlers have been registered or none of the registered handlers returns TRUE, the default handler will be used, resulting in the process being terminated.
BOOL CtrlHandler(DWORD fdwCtrlType) {
switch (fdwCtrlType) {

/* Handle the CTRL+C signal. */

case CTRL_C_EVENT:

Beep(1000, 1000);
return TRUE;

/* CTRL+CLOSE: confirm that the user wants to exit. */

case CTRL_CLOSE_EVENT:

return TRUE;

/* Pass other signals to the next handler. */

case CTRL_BREAK_EVENT:

case CTRL_LOGOFF_EVENT:

case CTRL_SHUTDOWN_EVENT:

default:

return FALSE;

}

}

VOID main(void) {
BOOL fSuccess;

fSuccess = SetConsoleCtrlHandler(
(PHANDLER_ROUTINE) CtrlHandler, /* handler function */
TRUE); /* add to list */
if (! fSuccess)
MyErrorExit("Could not set control handler");

+$#A Console Application Issues
The 8-bit console functions use the OEM code page. All other functions use the ANSI code page by default. This means that strings returned by the console functions may not be processed correctly by the other functions and vice versa. For example, if FindFirstFileA returns a string that contains certain extended ANSI characters, WriteConsoleA will not display the string properly.
The best long-term solution for a console application is to use Unicode. Barring that solution, a console application should use the SetFileApisToOEM_win32_SetFileApisToOEM function. That function changes relevant Win32 file functions so that they produce OEM character set strings rather than ANSI character set strings.
Following are the Win32 file functions:
_lopen_win32__lopen GetDriveType_win32_GetDriveType LoadLibrary_win32_LoadLibrary
CopyFile_win32_CopyFile GetFileAttributes_win32_GetFileAttributes LoadLibraryEx_win32_LoadLibraryEx
CreateDirectory_win32_CreateDirectory GetFullPathName_win32_GetFullPathName MoveFile_win32_MoveFile
CreateFile_win32_CreateFile GetModuleFileName_win32_GetModuleFileName MoveFileEx_win32_MoveFileEx
CreateProcess_win32_CreateProcess GetModuleHandle_win32_GetModuleHandle OpenFile_win32_OpenFile
DeleteFile_win32_DeleteFile GetSystemDirectory_win32_GetSystemDirectory RemoveDirectory_win32_RemoveDirectory
FindFirstFile_win32_FindFirstFile GetTempFileName_win32_GetTempFileName SearchPath_win32_SearchPath
FindNextFile_win32_FindNextFile GetTempPath_win32_GetTempPath SetCurrentDirectory_win32_SetCurrentDirectory
GetCurrentDirectory_win32_GetCurrentDirectory GetVolumeInformation_win32_GetVolumeInformation SetFileAttributes_win32_SetFileAttributes
GetDiskFreeSpace_win32_GetDiskFreeSpace GetWindowsDirectory_win32_GetWindowsDirectory

When dealing with command lines, a console application should obtain the command line in Unicode form and convert it to OEM form, using the relevant character-to-OEM functions. Note, also, that argv uses the ANSI character set.
+$#AK Console Reference
The following functions and structures are associated with accessing a console.
+$#A Console Functions
Following are the functions used to access a console.
AllocConsole_win32_AllocConsoleCreateConsoleScreenBuffer_win32_CreateConsoleScreenBufferFillConsoleOutputAttribute_win32_FillConsoleOutputAttributeFillConsoleOutputCharacter_win32_FillConsoleOutputCharacterFlushConsoleInputBuffer_win32_FlushConsoleInputBufferFreeConsole_win32_FreeConsoleGenerateConsoleCtrlEvent_win32_GenerateConsoleCtrlEventGetConsoleCP_win32_GetConsoleCPGetConsoleCursorInfo_win32_GetConsoleCursorInfoGetConsoleMode_win32_GetConsoleModeGetConsoleOutputCP_win32_GetConsoleOutputCPGetConsoleScreenBufferInfo_win32_GetConsoleScreenBufferInfoGetConsoleTitle_win32_GetConsoleTitleGetLargestConsoleWindowSize_win32_GetLargestConsoleWindowSizeGetNumberOfConsoleInputEvents_win32_GetNumberOfConsoleInputEventsGetNumberOfConsoleMouseButtons_win32_GetNumberOfConsoleMouseButtonsGetStdHandle_win32_GetStdHandleHandlerRoutine_win32_HandlerRoutinePeekConsoleInput_win32_PeekConsoleInputReadConsole_win32_ReadConsoleReadConsoleInput_win32_ReadConsoleInputReadConsoleOutput_win32_ReadConsoleOutputReadConsoleOutputAttribute_win32_ReadConsoleOutputAttributeReadConsoleOutputCharacter_win32_ReadConsoleOutputCharacterScrollConsoleScreenBuffer_win32_ScrollConsoleScreenBufferSetConsoleActiveScreenBuffer_win32_SetConsoleActiveScreenBufferSetConsoleCP_win32_SetConsoleCPSetConsoleCtrlHandler_win32_SetConsoleCtrlHandlerSetConsoleCursorInfo_win32_SetConsoleCursorInfoSetConsoleCursorPosition_win32_SetConsoleCursorPositionSetConsoleMode_win32_SetConsoleModeSetConsoleOutputCP_win32_SetConsoleOutputCPSetConsoleScreenBufferSize_win32_SetConsoleScreenBufferSizeSetConsoleTextAttribute_win32_SetConsoleTextAttributeSetConsoleTitle_win32_SetConsoleTitleSetConsoleWindowInfo_win32_SetConsoleWindowInfoSetStdHandle_win32_SetStdHandleWriteConsole_win32_WriteConsoleWriteConsoleInput_win32_WriteConsoleInputWriteConsoleOutput_win32_WriteConsoleOutputWriteConsoleOutputAttribute_win32_WriteConsoleOutputAttributeWriteConsoleOutputCharacter_win32_WriteConsoleOutputCharacter
+$#A Console Structures
Following are the structures used to access a console.
CHAR_INFO_win32_CHAR_INFO_strCONSOLE_CURSOR_INFO_win32_CONSOLE_CURSOR_INFO_strCONSOLE_SCREEN_BUFFER_INFO_win32_CONSOLE_SCREEN_BUFFER_INFO_strFOCUS_EVENT_RECORD_win32_FOCUS_EVENT_RECORD_strINPUT_RECORD_win32_INPUT_RECORD_strMENU_EVENT_RECORD_win32_MENU_EVENT_RECORD_strWINDOW_BUFFER_SIZE_RECORD_win32_WINDOW_BUFFER_SIZE_RECORD_str

+$#AK Data Decompression Library
This overview describes important concepts relating to data compression and describes the decompression functions in LZEXPAND.DLL.
+$#A About the Data Decompression Library
The Microsoft® Windows® operating system includes a dynamic-link library (DLL) named LZEXPAND.DLL. Typically, an application calls functions in LZEXPAND.DLL to decompress data previously compressed by using the Microsoft File Compression Utility (COMPRESS.EXE).
+$#A Data Compression
Data compression is an operation that reduces the size of a file by minimizing redundant data. In a file that contains text, redundant data can be frequently occurring characters, such as the space character, or common vowels, such as the letters e and a; it can also be frequently occurring character strings. Data compression creates a compressed version of a file by minimizing this redundant data.
Each type of data-compression algorithm minimizes redundant data in a unique manner. For example, the Huffman encoding algorithm assigns a code to characters in a file based on how frequently those characters occur. Another compression algorithm, called run-length encoding, generates a two-part value for repeated characters: the first part specifies the number of times the character is repeated, and the second part identifies the character. Another compression algorithm, known as the Lempel-Ziv algorithm, converts variable-length strings into fixed-length codes that consume less space than the original strings.
To compress large applications or data files, you can run COMPRESS.EXE from the command line. COMPRESS.EXE uses the Lempel-Ziv compression algorithm.
+$#A Data Decompression
Applications can call the functions in LZEXPAND.DLL to decompress files compressed by using COMPRESS.EXE. The functions can also process uncompressed files without attempting to decompress them. To use these functions, applications must include the LZEXPAND.H header file.
The following table describes each function found in LZEXPAND.DLL.
Function Purpose
CopyLZFile_win32_CopyLZFile This function is obsolete. It is provided to simplify porting of 16-bit Windows-based applications. Win32-based applications should use the LZCopy_win32_LZCopy function.
GetExpandedName_win32_GetExpandedName Retrieves the original name of a compressed file, if the /r option was used during compression of the file.
LZClose_win32_LZClose Closes a file that was opened when the application called the LZOpenFile_win32_LZOpenFile function.
LZCopy_win32_LZCopy Copies a source file to a destination file. If the source file is compressed, this function creates a decompressed destination file. If the source file is not compressed, this function duplicates the original file. This function is intended for single-file copy operations.
LZDone_win32_LZDone This function is obsolete and should not be used in Win32-based applications. It is provided to simplify porting of 16-bit Windows-based applications.
LZInit_win32_LZInit Creates structures used for decompressing files.
LZOpenFile_win32_LZOpenFile Opens a file and returns a handle that identifies it.
LZRead_win32_LZRead Reads a specified number of bytes from a file. If the file is compressed, this function decompresses the bytes before copying them to the destination buffer.
LZSeek_win32_LZSeek Positions the file pointer within the decompressed image of a compressed file. The application calls this function to position the pointer before calling the LZRead_win32_LZRead function.
LZStart_win32_LZStart This function is obsolete and should not be used in Win32-based applications. It is provided to simplify porting of 16-bit Windows-based applications.

+$#A Using the Data Decompression Library
Functions in LZEXPAND.DLL can be used to decompress single or multiple files. They can also be used to decompress compressed files a portion at a time.
· Decompressing a single file_win32_Decompressing_a_Single_File
· Decompressing multiple files_win32_Decompressing_Multiple_Files
· Reading bytes from compressed files_win32_Reading_Bytes_from_Compressed_Files

+$#A Decompressing a Single File
An application can decompress a single compressed file by performing the following tasks.
1. Open the source file by calling the LZOpenFile_win32_LZOpenFile function.
2. Open the destination file by calling LZOpenFile.
3. Copy the source file to the destination file by calling the LZCopy_win32_LZCopy function and passing the handles returned by LZOpenFile.
4. Close the files by calling the LZClose_win32_LZClose function.

+$#A Decompressing Multiple Files
An application can decompress multiple files by performing the following tasks.
1. Open the source files by calling the LZOpenFile_win32_LZOpenFile function.
2. Open the destination files by calling LZOpenFile.
3. Copy the source files to the destination files by calling the LZCopy_win32_LZCopy function.
4. Close the files by calling the LZClose_win32_LZClose function.

+$#A Reading Bytes from Compressed Files
In addition to decompressing a complete file in a single operation, an application can decompress a compressed file a portion at a time by using the LZSeek_win32_LZSeek and LZRead_win32_LZRead functions. These functions are particularly useful when it is necessary to extract parts of large files. For example, a font manufacturer may have compressed files containing font metrics in addition to character data. To use the information in these files, an application would need to decompress the file; however, most applications would use only part of the file at any particular time. To get information about font metrics, the application would extract data from the header. To get information from the text, the application would reposition the file pointer by calling LZSeek and extract character data by calling LZRead.
+$#A Data Decompression Library Functions
Following are the functions used to decompress files.
GetExpandedName_win32_GetExpandedNameLZClose_win32_LZCloseLZCopy_win32_LZCopyLZInit_win32_LZInitLZOpenFile_win32_LZOpenFileLZRead_win32_LZReadLZSeek_win32_LZSeek
Obsolete or Deleted Functions
CopyLZFile_win32_CopyLZFileLZDone_win32_LZDoneLZStart_win32_LZStart

+$#AK Debugging
A debugger is an application that enables a developer to observe and correct programming errors.
The first section of this overview describes the debugging-specific features of certain standard Windows functions ¾ specifically, process, thread, and exception-handling functions.
The second section of this overview describes the Windows debugging functions. These functions enable an application to wait for debugging events, cause breakpoint exceptions, transfer execution control to the debugger, and so on.
+$#A About Debugging Support
Microsoft® Windows® supports several functions that can be used to create a basic, event-driven debugger. "Event-driven" means that the debugger is notified every time certain events occur in the process being debugged. Notification enables the debugger to take appropriate action in response to the events.
+$#A Support from Process, Thread, and Exception Functions
Some functions essential to debugging are actually part of the process, thread, or exception-handling architectures. This section describes how to start a process that is going to be debugged and how to examine and manipulate the context and execution of a thread. It also describes how a debugger should handle exceptions.
+$#A Process Functions
The CreateProcess_win32_CreateProcess function enables a debugger to start a process and debug it. The fdwCreate parameter of CreateProcess is used to specify the type of debugging operation. If the DEBUG_PROCESS flag is specified for the parameter, a debugger debugs the new process and all of the process's descendants, provided that the descendants are created without the DEBUG_PROCESS flag.
If the DEBUG_PROCESS and DEBUG_ONLY_THIS_PROCESS flags are specified for fdwCreate, a debugger debugs the new process but none of its descendants.
One debugger can debug another by creating a process with the DEBUG_PROCESS flag. The new process (the debugger being debugged) must then create a process with the DEBUG_PROCESS flag.
The OpenProcess_win32_OpenProcess function enables a debugger to obtain the identifier of an existing process. (The DebugActiveProcess_win32_DebugActiveProcess function uses this identifier to attach the debugger to the process.) Typically, debuggers open a process with the PROCESS_VM_READ and PROCESS_VM_WRITE flags. Using these flags enables the debugger to read from and write to the virtual memory of the process by using the ReadProcessMemory_win32_ReadProcessMemory and WriteProcessMemory_win32_WriteProcessMemory functions. For more information about processes, see Processes and Threads_win32_Processes_and_Threads.
+$#A Thread Functions
The CreateThread_win32_CreateThread function creates a new thread for a process. Debuggers typically need to examine or change the contents of a thread's registers. To accomplish this, a debugger must obtain a handle of the thread by using the DuplicateHandle_win32_DuplicateHandle function and specifying the appropriate access to the thread (THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, or both).
A process with appropriate access to a thread can examine the thread's registers by using the GetThreadContext_win32_GetThreadContext function and set the contents of the thread's registers by using the SetThreadContext_win32_SetThreadContext function.
A process can also obtain THREAD_SUSPEND_RESUME access to a thread. This type of access enables a debugger to control a thread's execution with the SuspendThread_win32_SuspendThread and ResumeThread_win32_ResumeThread functions. For more information about threads, see Processes and Threads_win32_Processes_and_Threads.
+$#A Exception Handling for Debugging
When an exception occurs in a process that is being debugged, the kernel notifies the debugger by passing the exception to it. This is known as first-chance notification. The kernel then suspends all threads in the process being debugged.
If the debugger does not handle the exception, the kernel attempts to locate an appropriate exception handler. If the kernel cannot locate an appropriate one, the kernel again notifies the debugger that an exception has occurred. This is known as last-chance notification. If the debugger does not handle the exception after the last-chance notification, the kernel terminates the process being debugged.
For more information about exception handling, see Structured Exception Handling_win32_Structured_Exception_Handling.
+$#A Debugging Functions
Although most of the Windows debugging functions are used to create a debugger, several functions are designed for use in the process being debugged.
To start a process and debug it, a debugger must use CreateProcess_win32_CreateProcess, as described in the previous section.
To debug a process that is already executing, the debugger should use DebugActiveProcess_win32_DebugActiveProcess with the process identifier retrieved by OpenProcess_win32_OpenProcess. DebugActiveProcess attaches the debugger to the active process. In this case, only the active process can be debugged; its descendants cannot. The debugger must have appropriate access to the executing process to use DebugActiveProcess. For more information about access rights, see Security_win32_Security.
After the debugger has either created or attached itself to the process it intends to debug, the kernel notifies the debugger of all debugging events that occur in the process, and, if specified, in the process's descendants. For more information about debugging events, see Debugging Events_win32_Debugging_Events.
The debugger uses the WaitForDebugEvent_win32_WaitForDebugEvent function at the beginning of its main loop. This function blocks the debugger until a debugging event occurs. When the debugging event occurs, the kernel suspends all threads in the process being debugged and notifies the debugger of the event. The debugger can interact with the user, or manipulate the state of the process being debugged, by using the FlushInstructionCache_win32_FlushInstructionCache, GetThreadContext_win32_GetThreadContext, GetThreadSelectorEntry_win32_GetThreadSelectorEntry, ReadProcessMemory_win32_ReadProcessMemory, SetThreadContext_win32_SetThreadContext, and WriteProcessMemory_win32_WriteProcessMemory functions.
GetThreadSelectorEntry returns the descriptor table entry for a specified selector and thread. Debuggers use the descriptor table entry to convert a segment-relative address to a linear virtual address. The ReadProcessMemory and WriteProcessMemory functions require linear virtual addresses.
Debuggers frequently read the memory of the process being debugged and write the memory that contains instructions to the instruction cache. After the instructions are written, the debugger calls FlushInstructionCache to execute the cached instructions.
The debugger uses the ContinueDebugEvent_win32_ContinueDebugEvent function at the end of its main loop. This function allows the process being debugged to continue executing.
+$#A Communicating with the Debugger
The OutputDebugString_win32_OutputDebugString function sends a string from the process being debugged to the debugger by generating an OUTPUT_DEBUG_STRING_EVENT debugging event.
The DebugBreak_win32_DebugBreak function causes a breakpoint exception in the current process. A breakpoint is a location in a program where execution is stopped to allow the developer to examine the program's code, variables, and register values and, as necessary, to make changes, continue execution, or terminate execution.
The FatalExit_win32_FatalExit function terminates the current process and gives execution control to the debugger, but unlike DebugBreak, it does not generate an exception. This function should only be used as a last resort, because it does not always free the process's memory or close its files.
+$#A Debugging Events
A debugging event is an incident in the process being debugged that causes the kernel to notify the debugger. Debugging events include creating a process, creating a thread, loading a dynamic-link library (DLL), unloading a DLL, sending an output string, and generating an exception.
If a debugging event occurs while a debugger is waiting for one, the kernel fills the DEBUG_EVENT_win32_DEBUG_EVENT_str structure specified by WaitForDebugEvent_win32_WaitForDebugEvent with information describing the event.
When the kernel notifies the debugger of a debugging event, it also suspends all threads in the affected process. The threads do not resume execution until the debugger continues the debugging event by using ContinueDebugEvent_win32_ContinueDebugEvent. The following debugging events may occur while a process is being debugged.
Debugging event Description
CREATE_PROCESS_DEBUG_EVENT
Generated whenever a new process is created in a process being debugged or whenever the debugger begins debugging an already active process. The kernel generates this debugging event before the process begins to execute in user mode and before the kernel generates any other debugging events for the new process.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains a CREATE_PROCESS_DEBUG_INFO_win32_CREATE_PROCESS_DEBUG_INFO_str structure. This structure includes a handle of the new process, a handle of the process's image file, a handle of the process's initial thread, and other information that describes the new process.
The handle of the process has PROCESS_VM_READ and PROCESS_VM_WRITE access. If a debugger has these types of access to a thread, it can read and write to the process's memory by using the ReadProcessMemory_win32_ReadProcessMemory and WriteProcessMemory_win32_WriteProcessMemory functions.
The handle of the process's image file has GENERIC_READ access and is opened for read-sharing.
The handle of the process's initial thread has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. If a debugger has these types of access to a thread, it can read from and write to the thread's registers by using the GetThreadContext_win32_GetThreadContext and SetThreadContext_win32_SetThreadContext functions and can suspend and resume the thread by using the SuspendThread_win32_SuspendThread and ResumeThread_win32_ResumeThread functions.
CREATE_THREAD_DEBUG_EVENT
Generated whenever a new thread is created in a process being debugged or whenever the debugger begins debugging an already active process. This debugging event is generated before the new thread begins to execute in user mode.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains a CREATE_THREAD_DEBUG_INFO_win32_CREATE_THREAD_DEBUG_INFO_str structure. This structure includes a handle of the new thread and the thread's starting address. The handle has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. If a debugger has these types of access to a thread, it can read from and write to the thread's registers by using the GetThreadContext_win32_GetThreadContext and SetThreadContext_win32_SetThreadContext functions and can suspend and resume the thread by using the SuspendThread_win32_SuspendThread and ResumeThread_win32_ResumeThread functions.
EXCEPTION_DEBUG_EVENT
Generated whenever an exception occurs in the process being debugged. Possible exceptions include attempting to access inaccessible memory, executing breakpoint instructions, attempting to divide by zero, or any other exception noted in Structured Exception Handling_win32_Structured_Exception_Handling.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains an EXCEPTION_DEBUG_INFO_win32_EXCEPTION_DEBUG_INFO_str structure. This structure describes the exception that caused the debugging event.
Besides the standard exception conditions, an additional exception code can occur during console process debugging. The kernel generates a DBG_CONTROL_C exception code when CTRL+C is input to a console process that handles CTRL+C signals and is being debugged. This exception code is not meant to be handled by applications. An application should never use an exception handler to deal with it. It is raised only for the benefit of the debugger and is only used when a debugger is attached to the console process.
If a process is not being debugged or if the debugger passes on the DBG_CONTROL_C exception unhandled (through the gn command), the application's list of handler functions is searched, as documented for the SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler function.
If the debugger handles the DBG_CONTROL_C exception (through the gh command), an application will not notice the CTRL+C except in code like this.
while ((inputChar = getchar()) != EOF) ... while (gets(inputString)) ...
Thus, the debugger cannot be used to stop the read wait in such code from terminating.
EXIT_PROCESS_DEBUG_EVENT
Generated whenever the last thread in a process being debugged exits. This debugging event occurs immediately after the kernel unloads the process's DLLs and updates the process's exit code.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains an EXIT_PROCESS_DEBUG_INFO_win32_EXIT_PROCESS_DEBUG_INFO_str structure that specifies the exit code.
The debugger deallocates any internal structures associated with the process on receipt of this debugging event. The kernel closes the debugger's handle of the exiting process and all of the process's threads.
EXIT_THREAD_DEBUG_EVENT
Generated whenever a thread that is part of a process being debugged exits. The kernel generates this debugging event immediately after it updates the thread's exit code.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains an EXIT_THREAD_DEBUG_INFO_win32_EXIT_THREAD_DEBUG_INFO_str structure that specifies the exit code.
The debugger deallocates any internal structures associated with the thread on receipt of this debugging event. The system closes the debugger's handle of the exiting thread.
This debugging event does not occur if the exiting thread is the last thread of a process. In this case, the EXIT_PROCESS_DEBUG_EVENT debugging event occurs instead.
LOAD_DLL_DEBUG_EVENT
Generated whenever a process being debugged loads a DLL. This debugging event occurs when the system loader resolves links to a DLL or when the debugged process uses the LoadLibrary_win32_LoadLibrary function. This debugging event only occurs the first time the kernel attaches a DLL to the virtual address space of a process.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains a LOAD_DLL_DEBUG_INFO_win32_LOAD_DLL_DEBUG_INFO_str structure. This structure includes a handle of the newly loaded DLL, the base address of the DLL, and other information that describes the DLL.
Typically, a debugger loads a symbol table associated with the DLL on receipt of this debugging event.
OUTPUT_DEBUG_STRING_EVENT
Generated when a process being debugged uses the OutputDebugString_win32_OutputDebugString function.
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains an OUTPUT_DEBUG_STRING_INFO_win32_OUTPUT_DEBUG_STRING_INFO_str structure. This structure specifies the address, length, and format of the debugging string.
UNLOAD_DLL_DEBUG_EVENT
Generated whenever a process being debugged unloads a DLL by using the FreeLibrary_win32_FreeLibrary function. This debugging event only occurs the last time a DLL is unloaded from a process's address space (that is, when the DLL's usage count is zero).
The DEBUG_EVENT_win32_DEBUG_EVENT_str structure contains an UNLOAD_DLL_DEBUG_INFO_win32_UNLOAD_DLL_DEBUG_INFO_str structure. This structure specifies the base address of the DLL in the address space of the process that unloads the DLL.
Typically, a debugger unloads a symbol table associated with the DLL upon receiving this debugging event.
When a process exits, the kernel automatically unloads the process's DLLs, but does not generate an UNLOAD_DLL_DEBUG_EVENT debugging event.

+$#A Using Debugging Support
The following example uses the WaitForDebugEvent_win32_WaitForDebugEvent and ContinueDebugEvent_win32_ContinueDebugEvent functions to illustrate how a simple debugger might be organized.
DEBUG_EVENT DebugEv; /* debugging event info. */
DWORD dwContinueStatus = DBG_CONTINUE; /* exception continuation */

for(;;) {

/*
* Wait for a debugging event to occur. The second parameter
* indicates that the function does not return until
* a debugging event occurs.
*/

WaitForDebugEvent(&DebugEv, INFINITE);

/* Process the debugging event code. */

switch (DebugEv.dwDebugEventCode) {

case EXCEPTION_DEBUG_EVENT:

/*
* Process the exception code. When handling
* exceptions, remember to set the continuation
* status parameter (dwContinueStatus). This value
* is used by the ContinueDebugEvent function.
*/

switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) {

case EXCEPTION_ACCESS_VIOLATION:

/*
* First chance: Pass this on to the kernel.
* Last chance: Display an appropriate error.
*/

case EXCEPTION_BREAKPOINT:

/*
* First chance: Display the current
* instruction and register values.
*/

case EXCEPTION_DATATYPE_MISALIGNMENT:

/*
* First chance: Pass this on to the kernel.
* Last chance: Display an appropriate error.
*/

case EXCEPTION_SINGLE_STEP:

/*
* First chance: Update the display of the
* current instruction and register values.
*/

case DBG_CONTROL_C:

/*
* First chance: Pass this on to the kernel.
* Last chance: Display an appropriate error.
*/

/* Handle other exceptions. */

}

case CREATE_THREAD_DEBUG_EVENT:

/*
* As needed, examine or change the thread's registers
* with the GetThreadContext and SetThreadContext functions;
* and suspend and resume thread execution with the
* SuspendThread and ResumeThread functions.
*/

case CREATE_PROCESS_DEBUG_EVENT:

/*
* As needed, examine or change the registers of the
* process's initial thread with the GetThreadContext and
* SetThreadContext functions; read from and write to the
* process's virtual memory with the ReadProcessMemory and
* WriteProcessMemory functions; and suspend and resume
* thread execution with the SuspendThread and ResumeThread
* functions.
*/

case EXIT_THREAD_DEBUG_EVENT:

/* Display the thread's exit code. */

case EXIT_PROCESS_DEBUG_EVENT:

/* Display the process's exit code. */

case LOAD_DLL_DEBUG_EVENT:

/*
* Read the debugging information included
* in the newly loaded DLL.
*/

case UNLOAD_DLL_DEBUG_EVENT:

/*
* Display a message that the DLL has
* been unloaded.
*/

case OUTPUT_DEBUG_STRING_EVENT:

/* Display the output debugging string. */

}

/* Resume executing the thread that reported the debugging event. */

ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId, dwContinueStatus);

}

+$#A Debugging Functions
Following are the functions that support debugging:
ContinueDebugEvent_win32_ContinueDebugEventDebugActiveProcess_win32_DebugActiveProcessDebugBreak_win32_DebugBreakFatalAppExit_win32_FatalAppExitFatalExit_win32_FatalExitFlushInstructionCache_win32_FlushInstructionCacheGetThreadContext_win32_GetThreadContextGetThreadSelectorEntry_win32_GetThreadSelectorEntryOutputDebugString_win32_OutputDebugStringReadProcessMemory_win32_ReadProcessMemorySetDebugErrorLevel_win32_SetDebugErrorLevelSetThreadContext_win32_SetThreadContextWaitForDebugEvent_win32_WaitForDebugEventWriteProcessMemory_win32_WriteProcessMemory

+$#AK Device Input and Output
Applications use a device input and output control to communicate with a device driver. This overview discusses device handles, which identify the device being accessed, and device control codes, which specify the action the driver is to perform.
+$#A About Device Input and Output
The DeviceIoControl_win32_DeviceIoControl function provides a device input and output control (IOCTL) interface through which an application can communicate directly with a device driver. The DeviceIoControl function is a general-purpose interface that can send control codes to a variety of devices.
+$#A Device Handles
A DeviceIoControl_win32_DeviceIoControl call specifies a handle of a device driver to identify the device being accessed. You can use the CreateFile_win32_CreateFile function to get a device driver handle.
The CreateFile call must specify either the name of a device or the name of the driver associated with a device. To specify a device name, the lpszname parameter of CreateFile specifies a string with the format
\\.\DeviceName

The types of devices that you can open and the method that you must use to specify them in the CreateFile_win32_CreateFile function depend on the Win32 platform on which your application is running.
On Windows NT, DeviceIoControl_win32_DeviceIoControl can open a handle of a specific device. For example, to open a handle to the logical drive A:, specify "\\\\.\\a:". Alternatively, you can use the names "\\\\.\\PhysicalDrive0", "\\\\.\\PhysicalDrive1", and so on, to open handles to the physical drives on a system.
For Win32-based applications running on Windows 95, DeviceIoControl must specify a handle of a virtual device driver. For example, to open a handle to the system VxD, specify "\\\\.\\vwin32".
The CreateFile_win32_CreateFile call should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when opening a handle to a device driver. However, when opening a communications resource, such as a serial port, CreateFile must specify exclusive access. Other CreateFile parameters are used as follows when opening a device handle:
· The fdwCreate parameter must specify the OPEN_EXISTING flag.
· The hTemplateFile parameter must be NULL.
· The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped (asynchronous) I/O operations.

+$#A Device Control Codes
An application can use the DeviceIoControl_win32_DeviceIoControl function to send a control code to a device driver. The control code specifies the action that the driver is to perform. For example, a control code can ask a device driver to return information about the corresponding device, or direct the driver to carry out an action on the device, such as formatting a disk.
A number of standard control codes are defined in the Win32 header files. For a list of standard control codes, see DeviceIoControl . In addition, device drivers can define their own device-specific control codes. The types of control codes you can specify depend on the device being accessed and the Win32 platform on which your application is running.
Applications running on Windows NT can use the standard control codes or device-specific control codes to perform direct input and output operations on a floppy disk drive, hard disk drive, tape drive, or CD-ROM drive.
Win32-based applications running on Windows 95 cannot use the standard control codes in the Win32 header files. These applications are restricted to using the control codes supported by the virtual device driver being accessed. For example, the system VxD, VWIN32.VXD, supports the input and output control (IOCTL) functions originally provided by MS-DOS Interrupt 21h.
The control code specified in a DeviceIoControl_win32_DeviceIoControl call determines the values that must be specified for the other DeviceIoControl parameters. For example, a Windows NT application can specify the IOCTL_DISK_FORMAT_TRACKS_win32_IOCTL_DISK_FORMAT_TRACKS control code. In this case, the lpvInBuffer parameter must point to a filled-in FORMAT_PARAMETERS_win32_FORMAT_PARAMETERS_str structure, and the cbInBuffer parameter must specify the size of the structure.
+$#A Using Device Input and Output Control
· Calling DeviceIoControl on Windows NT_win32_Calling_DeviceIoControl_on_Windows_NT
· Calling DeviceIoControl on Windows 95_win32_Calling_DeviceIoControl_on_Windows_95
+$#A Calling DeviceIoControl on Windows NT
On Windows NT, an application can use the DeviceIoControl_win32_DeviceIoControl function to perform direct input and output operations on, or retrieve information about, a floppy disk drive, hard disk drive, tape drive, or CD-ROM drive. The following example demonstrates how to retrieve information about the first physical drive in the system. It uses the CreateFile_win32_CreateFile function to obtain the device handle of the first physical drive, and then uses the DeviceIoControl function with the IOCTL_DISK_GET_DRIVE_GEOMETRY_win32_IOCTL_DISK_GET_DRIVE_GEOMETRY control code to fill a DISK_GEOMETRY_win32_DISK_GEOMETRY_str structure with information about the drive.
BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
{
HANDLE hDevice;
BOOL fResult;
DWORD cb;

hDevice = CreateFile("\\\\.\\PhysicalDrive0",
0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);

if (hDevice == NULL)
return FALSE;

fResult = DeviceIoControl(hDevice,
IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
pdg, sizeof(*pdg), &cb, (LPOVERLAPPED) NULL);

if (!fResult)
return FALSE;

CloseHandle(hDevice);
}

This example succeeds only when it runs on Windows NT, for two reasons:
· The standard device input/output control codes are available only on Windows NT, and
· On Windows 95, an application must specify a virtual device driver in the CreateFile function ¾ not a specific device.

+$#A Calling DeviceIoControl on Windows 95
A Win32-based application running on Windows 95 can use the DeviceIoControl_win32_DeviceIoControl function to send control codes directly to a virtual device driver (VxD). Any VxD can support any number of control codes, or none at all.
The system VxD, VWIN32.VXD, supports the input and output control (IOCTL) functions originally provided by MS-DOS Interrupt 21h. The following example shows how to call Get Media ID (Interrupt 21h Function 440Dh Minor Code 66h) from a Win32-based application:

"\\\\.\\VxDName"
In Windows 95,

#define VWIN32_DIOC_DOS_IOCTL 1

typedef struct _DEVIOCTL_REGISTERS {
DWORD reg_EBX;
DWORD reg_EDX;
DWORD reg_ECX;
DWORD reg_EAX;
DWORD reg_EDI;
DWORD reg_ESI;
DWORD reg_Flags;
} DEVIOCTL_REGISTERS, *PDEVIOCTL_REGISTERS;

typedef struct _MID {
WORD midInfoLevel;
DWORD midSerialNum;
BYTE midVolLabel[11];
BYTE midFileSysType[8];
} MID, *PMID;

BOOL GetMediaID(PMID pmid, UINT nDrive)
{
DEVIOCTL_REGISTERS reg;

reg.reg_EAX = 0x440D; /* IOCTL for block devices */
reg.reg_EBX = nDrive; /* zero-based drive ID */
reg.reg_ECX = 0x0866; /* Get Media ID command */
reg.reg_EDX = (DWORD) pmid; /* receives media ID info */

if (!DoIOCTL(&reg))
return FALSE;

if (reg.reg_Flags & 0x8000) /* error if carry flag set */
return FALSE;

return TRUE;
}

BOOL DoIOCTL(PDEVIOCTL_REGISTERS preg)
{
HANDLE hDevice;

BOOL fResult;
DWORD cb;

preg->reg_Flags = 0x8000; /* assume error (carry flag set) */

hDevice = CreateFile("\\\\.\\vwin32",
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

if (hDevice == (HANDLE) INVALID_HANDLE_VALUE)
return FALSE;
else {
fResult = DeviceIoControl(hDevice, VWIN32_DIOC_DOS_IOCTL,
preg, sizeof(*preg), preg, sizeof(*preg), &cb, 0);

if (!fResult)
return FALSE;
}

CloseHandle(hDevice);

return TRUE;
}
+$#AK Device Input and Output Reference
The following functions, operations, and structures are associated with device inp ut and output.
+$#A Device Input and Output Functions
Use the followin g function to i mplement device input and output:
DeviceIoControl_win32_DeviceIoControl
+$#A Device Input and Output Operations
Use the following operations to implement device input and output:
FSCTL_DISMOUNT_VOLUME_win32_FSCTL_DISMOUNT_VOLUMEFSCTL_LOCK_VOLUME_win32_FSCTL_LOCK_VOLUMEFSCTL_UNLOCK_VOLUME_win32_FSCTL_UNLOCK_VOLUMEIOCTL_DISK_CHECK_VERIFY_win32_IOCTL_DISK_CHECK_VERIFYIOCTL_DISK_EJECT_MEDIA_win32_IOCTL_DISK_EJECT_MEDIAIOCTL_DISK_FORMAT_TRACKS_win32_IOCTL_DISK_FORMAT_TRACKSIOCTL_DISK_GET_DRIVE_GEOMETRY_win32_IOCTL_DISK_GET_DRIVE_GEOMETRYIOCTL_DISK_GET_DRIVE_LAYOUT_win32_IOCTL_DISK_GET_DRIVE_LAYOUTIOCTL_DISK_GET_MEDIA_TYPES_win32_IOCTL_DISK_GET_MEDIA_TYPESIOCTL_DISK_GET_PARTITION_INFO_win32_IOCTL_DISK_GET_PARTITION_INFOIOCTL_DISK_LOAD_MEDIA_win32_IOCTL_DISK_LOAD_MEDIAIOCTL_DISK_MEDIA_REMOVAL_win32_IOCTL_DISK_MEDIA_REMOVALIOCTL_DISK_PERFORMANCE_win32_IOCTL_DISK_PERFORMANCEIOCTL_DISK_REASSIGN_BLOCKS_win32_IOCTL_DISK_REASSIGN_BLOCKSIOCTL_DISK_SET_DRIVE_LAYOUT_win32_IOCTL_DISK_SET_DRIVE_LAYOUTIOCTL_DISK_SET_PARTITION_INFO_win32_IOCTL_DISK_SET_PARTITION_INFOIOCTL_DISK_VERIFY_win32_IOCTL_DISK_VERIFYIOCTL_SERIAL_LSRMST_INSERT_win32_IOCTL_SERIAL_LSRMST_INSERT
+$#A Device Input and Output Structures
Use these structures in device input and output operations:
FORMAT_PARAMETERS_win32_FORMAT_PARAMETERS_strDISK_GEOMETRY_win32_DISK_GEOMETRY_strDRIVE_LAYOUT_INFORMATION_win32_DRIVE_LAYOUT_INFORMATION_strPARTITION_INFORMATION_win32_PARTITION_INFORMATION_strPREVENT_MEDIA_REMOVAL_win32_PREVENT_MEDIA_REMOVAL_strDISK_PERFORMANCE_win32_DISK_PERFORMANCE_strREASSIGN_BLOCKS_win32_REASSIGN_BLOCKS_strSET_PARTITION_INFORMATION_win32_SET_PARTITION_INFORMATION_strVERIFY_INFORMATION_win32_VERIFY_INFORMATION_str
+$#AK Dynamic Data Exchange
This overview provides guidelines for implementing dynamic data exchange for applications that cannot use the Dynamic Data Exchange Management Library (DDEML). For more information about the DDEML, see Dynamic Data Exchange Management Library_win32_Dynamic_Data_Exchange_Management_Library.
+$#A About Dynamic Data Exchange
The Microsoft® Win32® application programming interface (API) provides several methods for transferring data between applications. One method is to use the Win32 dynamic data exchange (DDE) protocol. The DDE protocol is a set of messages and guidelines. It sends messages between applications that share data and uses shared memory to exchange data between applications. Applications can use the DDE protocol for one-time data transfers and for continuous exchanges in which applications send updates to one another as new data becomes available.
The Win32 API also includes the Dynamic Data Exchange Management Library (DDEML). The DDEML is a dynamic-link library (DLL) that applications running with Windows can use to share data. The DDEML provides an application programming interface (API) that simplifies the task of adding DDE capability to a Win32-based application. Instead of sending, posting, and processing DDE messages directly, an application uses the DDEML functions to manage DDE conversations. (A DDE conversation is the interaction between client and server applications.)
The DDEML also provides a facility for managing the strings and data that DDE applications share. Instead of using atoms and pointers to shared memory objects, DDE applications create and exchange string handles, which identify strings, and data handles, which identify memory objects. The DDEML also makes it possible for a server application to register the service names it supports. The names are broadcast to other applications in the system, which can use the names to connect to the server. Moreover, the DDEML ensures compatibility among DDE applications by forcing them to implement the DDE protocol in a consistent manner.
Existing applications that use the message-based DDE protocol are fully compatible with those that use the DDEML. That is, an application that uses message-based DDE can establish conversations and perform transactions with applications that use the DDEML. Because of the many advantages of the DDEML, new applications should use it rather than the DDE messages. To use the API elements of the DDEML, you must include the DDEML header file in your source files, link with the DDEML library, and ensure that the DDEML dynamic-link library is in the system's search path.
+$#A Dynamic Data Exchange Protocol
Because the Win32 API has a message-based architecture, passing messages is the most appropriate method for automatically transferring information between applications. However, Win32 messages contain only two parameters (wParam and lParam) for passing data. As a result, these parameters must refer indirectly to other pieces of data when more than a few words of information pass between applications. The DDE protocol defines exactly how applications should use the wParam and lParam parameters to pass larger pieces of data by means of global atoms and shared memory handles. The DDE protocol has specific rules for allocating and deleting global atoms and shared memory objects.
A global atom is a reference to a character string. In the DDE protocol, atoms identify the applications exchanging data, the nature of the data being exchanged, and the data items themselves. For more information about atoms, see Atoms_win32_Atoms. A shared memory handle is a handle to a memory object allocated by GlobalAlloc_win32_GlobalAlloc, using the GMEM_DDESHARE flag. In the DDE protocol, shared memory objects store data items passed between applications, protocol options, and remote command execution strings.
+$#A Uses for Windows Dynamic Data Exchange
DDE is most appropriate for data exchanges that do not require ongoing user interaction. Usually, an application provides a method for the user to establish the link between the applications exchanging data. Once that link is established, however, the applications exchange data without further user involvement.
DDE can be used to implement a broad range of application features ¾ for example:
· Linking to real-time data, such as to stock market updates, scientific instruments, or process control.
· Creating compound documents, such as a word processing document that includes a chart produced by a graphics application. Using DDE, the chart will change when the source data is changed, while the rest of the document remains the same.
· Performing data queries between applications, such as a spreadsheet querying a database for accounts past due.

+$#A Dynamic Data Exchange from the User's Point of View
The following example illustrates illustrates how two DDE applications can cooperate, as seen from the user's point of view.
A spreadsheet user wants to use Microsoft® Excel to track the price of a particular stock on the New York Stock Exchange. The user has a Win32-based application called Quote that in turn has access to NYSE data. The DDE conversation between Microsoft Excel and Quote takes place as follows:
· The user initiates the conversation by supplying the name of the application (Quote) that will supply the data and the particular topic of interest (NYSE). The resulting DDE conversation is used to request quotes on specific stocks.
· Microsoft Excel broadcasts the application and topic names to all DDE applications currently running in the system. Quote responds, establishing a conversation with Microsoft Excel about the NYSE topic.
· The user can then create a spreadsheet formula in a cell that requests that the spreadsheet be automatically updated whenever a particular stock quotation changes. For example, the user could request an automatic update whenever a change occurs in the selling price of ZAXX stock by specifying the following Microsoft Excel formula:
='Quote'|'NYSE'!ZAXX

· The user can terminate the automatic updating of the ZAXX stock quotation at any time. Other data links that were established separately (such as for quotations for other stocks) still will remain active under the same NYSE conversation.
· The user can also terminate the entire conversation between Microsoft Excel and Quote on the NYSE topic, so that no specific data links on that topic can be established without initiating a new conversation.

+$#A Dynamic Data Exchange Concepts
The following sections explain the important concepts and terminology that are key to understanding dynamic data exchange.
+$#A Client, Server, and Conversation
Two applications participating in dynamic data exchange are said to be engaged in a DDE conversation. The application that initiates the conversation is the DDE client application; the application that responds to the client is the DDE server application. An application can engage in several conversations at the same time, acting as the client in some and as the server in others.
A DDE conversation takes place between two windows, one for each of the participating applications. A window may be the main window of the application; a window associated with a specific document, as in a multiple document interface (MDI) application; or a hidden (invisible) window whose only purpose is to process DDE messages.
Since a DDE conversation is identified by the pair of handles to the windows engaged in the conversation, no window should be engaged in more than one conversation with another window. Either the client application or the server application must provide a different window for each of its conversations with a particular server or client application.
An application can ensure a pair of client and server windows is never involved in more than one conversation by creating a hidden window for each conversation. The sole purpose of this window is to process DDE messages.
+$#A Application, Topic, and Item Names
The DDE protocol identifies the units of data passed between the client and server with a three-level hierarchy of application, topic, and item names.
Each DDE conversation is uniquely defined by the application name and topic. At the beginning of a DDE conversation, the client and server determine the application name and topic. The application name is usually the name of the server application. For example, when Microsoft Excel acts as the server in a conversation, the application name is Excel.
The DDE topic is a general classification of data within which multiple data items may be "discussed" (exchanged) during the conversation. For applications that operate on file-based documents, the topic is usually a filename. For other applications, the topic is an application-specific name.
Because the client and server window handles together identify a DDE conversation, the application name and topic that define a conversation cannot be changed during the course of the conversation.
A DDE data item is information related to the conversation topic exchanged between the applications. Values for the data item can be passed from the server to the client, or from the client to the server. Data can be passed with any of the standard clipboard formats or with a registered clipboard format. A special, registered format named Link identifies an item in a DDE conversation. For more information about clipboard formats, see Clipboard_win32_Clipboard.
+$#A The System Topic
Applications should support the system topic at all times. This topic provides a context for information that may be of general interest to another application.
Data-item values must be rendered in the CF_TEXT clipboard format. Individual elements of item values for a system topic must be delimited by tab characters. The following table suggests some items for the system topic.
Item Description
Formats Tab-delimited list of clipboard formats the application can render. Typically, CF_ formats are listed with the "CF_" portion of the names removed (for example, CF_TEXT is listed as "TEXT_win32_Text").
Help Text that briefly explains how to use the DDE server.
ReturnMessage Supporting detail for the most recently used WM_DDE_ACK_win32_WM_DDE_ACK message. This item is useful when more than eight bits of application-specific return data are required.
Status Indication of the current status of the application. When a server receives a WM_DDE_REQUEST_win32_WM_DDE_REQUEST message for this system-topic item, it should respond by posting a WM_DDE_DATA_win32_WM_DDE_DATA message with a string containing either Busy or Ready, as appropriate.
SysItems List of system-topic items the application supports.
TopicItemList Similar to the SysItems item, except that TopicItemList should be supported for each topic other than the system topic. This allows browsing of the items supported under any topic. If the items cannot be enumerated, this item should contain only "TopicItemList".
Topics List of topics the application supports at the current time; this list can vary from moment to moment.

+$#A Permanent Data Links
Once a DDE conversation has begun, the client can establish one or more permanent data links with the server. A data link is a communications mechanism by which the server notifies the client whenever the value of a given data item changes. The data link is permanent in the sense that this notification process continues until the data link or the DDE conversation itself is terminated.
There are two kinds of permanent DDE data links: warm and hot. In a warm data link, the server notifies the client that the value of the data item has changed, but the server does not send the data value to the client until the client requests it. In a hot data link, the server immediately sends the changed data value to the client.
Applications that support warm or hot data links typically provide a Copy or Paste Link command in their Edit menu to enable the user to establish links between applications. For more information, see Initiating a Data Link_win32_Initiating_a_Data_Link.
+$#A Atoms and Shared Memory Objects
Certain arguments of DDE messages are global atoms or shared memory objects. Applications using these arguments must follow explicit rules about when to allocate and delete them. In all cases, the message sender must delete any atom or shared memory object that the intended receiver will not receive because of an error condition, such as failure of the PostMessage_win32_PostMessage function.
DDE uses shared memory objects for three purposes:
· To carry a data-item value to be exchanged. This is an item referenced by the hData parameter in the WM_DDE_DATA_win32_WM_DDE_DATA and WM_DDE_POKE_win32_WM_DDE_POKE messages.
· To carry options in a message. This is an item referenced by the hOptions parameter in a WM_DDE_ADVISE_win32_WM_DDE_ADVISE message.
· To carry a command execution string. This is an item referenced by the hCommands parameter in the WM_DDE_EXECUTE_win32_WM_DDE_EXECUTE message and its corresponding WM_DDE_ACK_win32_WM_DDE_ACK message.

An application that receives a DDE shared memory object must treat it as read only. The application must not use the object as a mutual read-write area for the free exchange of data.
As it does with a DDE atom, an application should free a shared memory object to manage memory effectively. The application should also lock and unlock memory objects.
+$#A Dynamic Data Exchange Messages Overview
Because DDE is a message-based protocol, it employs no special Win32 functions or libraries. All DDE transactions are conducted by passing certain defined DDE messages between the client and server windows.
There are nine DDE messages; the symbolic constants for these messages are defined in the DDE header file. Certain structures for the various DDE messages are also defined in this header file.
The following table summarizes the nine DDE messages.
Message Description
WM_DDE_ACK_win32_WM_DDE_ACK Acknowledges receiving or not receiving a message.
WM_DDE_ADVISE_win32_WM_DDE_ADVISE Requests the server application to supply an update or notification for a data item whenever it changes. This establishes a permanent data link.
WM_DDE_DATA_win32_WM_DDE_DATA Sends a data-item value to the client application.
WM_DDE_EXECUTE_win32_WM_DDE_EXECUTE Sends a string to the server application, which is expected to process the string as a series of commands.
WM_DDE_INITIATE_win32_WM_DDE_INITIATE Initiates a conversation between the client and server applications.
WM_DDE_POKE_win32_WM_DDE_POKE Sends a data-item value to the server application.
WM_DDE_REQUEST_win32_WM_DDE_REQUEST Requests the server application to provide the value of a data item.
WM_DDE_TERMINATE_win32_WM_DDE_TERMINATE Terminates a conversation.
WM_DDE_UNADVISE_win32_WM_DDE_UNADVISE Terminates a permanent data link.

An application calls SendMessage_win32_SendMessage to issue the WM_DDE_INITIATE_win32_WM_DDE_INITIATE message or a WM_DDE_ACK_win32_WM_DDE_ACK message sent in response to WM_DDE_INITIATE. All other messages are sent by PostMessage_win32_PostMessage. The first parameter of these calls is the handle of the receiving window; the second parameter contains the message to be sent; the third parameter identifies the sending window; and the fourth parameter contains the message-specific arguments.
+$#A Dynamic Data Exchange Message Flow
A typical DDE conversation consists of the following events:
1. The client application initiates the conversation, and the server application responds.
2. The applications exchange data by any or all of the following methods:
· The server application sends data to the client at the client's request.
· The client application sends unsolicited data to the server application.
· The client application requests the server application to notify the client whenever a data item changes (warm data link).
· The client application requests the server application to send data whenever the data changes (hot data link).
· The server application carries out a command at the client's request.

3. Either the client or server application terminates the conversation.

An application window that processes requests from a client or server must process them strictly in the order they are received.
A client can establish conversations with more than one server; a server can have conversations with more than one client. When handling messages from more than one source, a client or server must process the messages of a given conversation synchronously, but need not process all messages synchronously. In other words, it can shift from one conversation to another as needed.
If an application is unable to process an incoming request because it is waiting for a DDE response, it must prevent deadlock by posting a WM_DDE_ACK_win32_WM_DDE_ACK message with the fBusy member of the DDEACK_win32_DDEACK_str structure set to 1. An application can also send a busy WM_DDE_ACK message if, for any reason, it cannot process an incoming request within a reasonable amount of time.
An application should be able to handle the failure of a client or server to respond to a message within a certain time. Since the time-out interval may vary depending on the nature of the application and the configuration of the user's system (including whether it is connected to a network), the application should provide a way for the user to specify the interval.
+$#A Parameter-Packing Functions
The lParam parameter of many DDE messages contains two pieces of data. For example, the lParam of the WM_DDE_DATA_win32_WM_DDE_DATA message contains a data handle and an atom. In previous versions of Windows, an application could use the MAKELONG_win32_MAKELONG macro to prepare an lParam parameter, and the LOWORD_win32_LOWORD and HIWORD_win32_HIWORD macros to remove the low-order and high-order values from lParam. Because a Win32 data handle is a 32-bit value, a Win32-based application must use the PackDDElParam_win32_PackDDElParam function to pack the handle and atom into an lParam parameter, and the UnpackDDElParam_win32_UnpackDDElParam function to remove the values. DDE applications must use PackDDElParam and UnpackDDElParam for all messages posted during a DDE conversation.
The Win32 API also includes the ReuseDDElParam_win32_ReuseDDElParam and FreeDDElParam_win32_FreeDDElParam functions. ReuseDDElParam allows a DDE application to reuse a packed lParam parameter, helping reduce the number of memory reallocations the application must perform during a conversation. An application can use FreeDDElParam to free the memory associated with a data handle received during a DDE conversation.
+$#A Using Dynamic Data Exchange
· Initiating a DDE conversation_win32_Initiating_a_Conversation
· Transferring a single data item_win32_Transferring_a_Single_Item
· Establishing a permanent data link_win32_Establishing_a_Permanent_Data_Link
· Carrying out commands in a server application_win32_Carrying_Out_Commands_in_a_Server_Application
· Terminating a DDE conversation_win32_Terminating_a_Conversation

+$#A Initiating a Conversation
To initiate a DDE conversation, the client sends a WM_DDE_INITIATE_win32_WM_DDE_INITIATE message. Usually, the client broadcasts this message by calling SendMessage_win32_SendMessage, with -1 as the first parameter. If the application already has the window handle of the server application, it can send the message directly to that window. The client prepares atoms for the application name and topic name by calling GlobalAddAtom_win32_GlobalAddAtom. The client can request conversations with any potential server application and for any potential topic by supplying NULL (wildcard) atoms for the application and topic.
The following example illustrates how the client initiates a conversation, where both the application and topic are specified.
/* Global variable */

static BOOL fInInitiate = FALSE;
char *szApplication;
char *szTopic;
atomApplication = *szApplication == 0 ?
NULL : GlobalAddAtom((LPSTR) szApplication);
atomTopic = *szTopic == 0 ?
NULL : GlobalAddAtom((LPSTR) szTopic);

fInInitiate = TRUE;
SendMessage((HWND) -1, /* broadcasts message */
WM_DDE_INITIATE, /* initiates conversation */
(WPARAM) hwndClientDDE, /* handle of client DDE window */
MAKELONG(atomApplication, /* application-name atom */
atomTopic)); /* topic-name atom */
fInInitiate = FALSE;
if (atomApplication != NULL)
GlobalDeleteAtom(atomApplication);
if (atomTopic != NULL)
GlobalDeleteAtom(atomTopic);

Note that if your application uses NULL atoms, you need not use the GlobalAddAtom_win32_GlobalAddAtom and GlobalDeleteAtom_win32_GlobalDeleteAtom functions. In this example, the client application creates two global atoms containing the name of the server and the name of the topic, respectively.
The client application sends a WM_DDE_INITIATE_win32_WM_DDE_INITIATE message with these two atoms in the lParam parameter of the message. In the call to the SendMessage_win32_SendMessage function, the special window handle -1 directs Windows to send this message to all other active applications. SendMessage does not return to the client application until all applications that receive the message have, in turn, returned control to Windows. This means that all WM_DDE_ACK_win32_WM_DDE_ACK messages sent in reply by the server applications are guaranteed to have been processed by the client by the time the SendMessage call has returned.
After SendMessage returns, the client application deletes the global atoms.
Server applications respond according to the logic illustrated in the following diagram.
{ewc msdncd, EWGraphic, group10800 0 /a "SDK_01.bmp"}
To acknowledge one or more topics, the server must create atoms for each conversation (requiring duplicate application-name atoms if there are multiple topics) and send a WM_DDE_ACK_win32_WM_DDE_ACK message for each conversation, as illustrated in the following example.
if ((atomApplication = GlobalAddAtom("Server")) != 0) {
if ((atomTopic = GlobalAddAtom(szTopic)) != 0) {
SendMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
MAKELONG(atomApplication, atomTopic));
GlobalDeleteAtom(atomApplication);
}

GlobalDeleteAtom(atomTopic);
}

if ((atomApplication == 0) || (atomTopic == 0)) {

.
. /* error handling */
.

}

When a server responds with a WM_DDE_ACK_win32_WM_DDE_ACK message, the client application should save the handle of the server window. The client receiving the handle as the wParam parameter of the WM_DDE_ACK message then sends all subsequent DDE messages to the server window this handle identifies.
If your client application uses a NULL atom for the application name or topic name, expect the application to receive acknowledgments from more than one server application. Multiple acknowledgements can also come from multiple instances of a DDE server, even if your client application does not use NULL atoms. A server should always use a unique window for each conversation. The window procedure in the client application can use the handle of the server window (provided as the lParam parameter of WM_DDE_INITIATE_win32_WM_DDE_INITIATE) to track multiple conversations. This allows a single client window to process several conversations without needing to terminate and reconnect with a new client window for each conversation.
+$#A Transferring a Single Item
Once a DDE conversation has been established, the client can either retrieve the value of a data item from the server by issuing the WM_DDE_REQUEST_win32_WM_DDE_REQUEST message, or submit a data-item value to the server by issuing WM_DDE_POKE_win32_WM_DDE_POKE.
+$#A Retrieving an Item from the Server
To retrieve an item from the server, the client sends the server a WM_DDE_REQUEST_win32_WM_DDE_REQUEST message specifying the item and format to retrieve, as shown in the following example.
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!PostMessage(hwndServerDDE,
WM_DDE_REQUEST,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_REQUEST, CF_TEXT, atomItem)))
GlobalDeleteAtom(atomItem);
}

if (atomItem == 0) {

.
. /* error handling */
.

}

In this example, the client specifies the clipboard format CF_TEXT as the preferred format for the requested data item.
The receiver (server) of the WM_DDE_REQUEST_win32_WM_DDE_REQUEST message typically must delete the item atom, but if the PostMessage_win32_PostMessage call fails, the client must delete the atom.
If the server has access to the requested item and can render it in the requested format, the server copies the item value as a shared memory object and sends the client a WM_DDE_DATA_win32_WM_DDE_DATA message, as illustrated in the following example.
/*
* Allocate the size of the DDE data header, plus the data: a
* string,<CR><LF><NULL>. The byte for the string's terminating
* null character is counted by DDEDATA.Value[1].
*/

if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
(LONG) sizeof(DDEDATA) + lstrlen(szItemValue) + 2)))
return;

if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) {
GlobalFree(hData);
return;

}
.
.
.

lpData->cfFormat = CF_TEXT;
lstrcpy((LPSTR) lpData->Value, (LPSTR) szItemValue);

/* Each line of CF_TEXT data is terminated by CR/LF. */

lstrcat((LPSTR) lpData->Value, (LPSTR) "\r\n");
GlobalUnlock(hData);
if ((atomItem = GlobalAddAtom((LPSTR) szItemName)) != 0) {
lParam = PackDDElParam(WM_DDE_ACK, (UINT) hData, atomItem);
if (!PostMessage(hwndClientDDE,
WM_DDE_DATA,
(WPARAM) hwndServerDDE,
lParam)) {
GlobalFree(hData);
GlobalDeleteAtom(atomItem);
FreeDDElParam(WM_DDE_ACK, lParam);
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

In this example, the server application allocates a memory object to contain the data item. The memory is allocated with the GMEM_DDESHARE option, so that the server and client applications can share the memory. After allocating the memory object, the server application locks the object so it can obtain the object's address. The data object is initialized as a DDEDATA_win32_DDEDATA_str structure.
The server application then sets the cfFormat member of the structure to CF_TEXT to inform the client application that the data is in text format. The client responds by copying the value of the requested data into the Value member of the DDEDATA structure. After the server has filled the data object, the server unlocks the data and creates a global atom containing the name of the data item.
Finally, the server issues the WM_DDE_DATA_win32_WM_DDE_DATA message by calling PostMessage_win32_PostMessage. The handle of the data object and the atom containing the item name are packed into the lParam parameter of the message by the PackDDElParam_win32_PackDDElParam function.
If PostMessage fails, the server must use the FreeDDElParam_win32_FreeDDElParam function to free the packed lParam parameter. The server must also free the packed lParam parameter for the WM_DDE_REQUEST_win32_WM_DDE_REQUEST message it received.
If the server cannot satisfy the request, it sends a negative WM_DDE_ACK_win32_WM_DDE_ACK message to the client, as shown in the following example.
/* negative acknowledgment */

PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem));

Upon receiving a WM_DDE_DATA_win32_WM_DDE_DATA message, the client processes the data-item value as appropriate. Then, if the fAckReq member pointed to in the WM_DDE_DATA message is 1, the client must send the server a positive WM_DDE_ACK_win32_WM_DDE_ACK message, as shown in the following example.
UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT) &hData,
(PUINT) &atomItem);
if (!(lpDDEData = (DDEDATA FAR*) GlobalLock(hData))
|| (lpDDEData->cfFormat != CF_TEXT)) {
PostMessage(hwndServerDDE,
WM_DDE_ACK,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem)); /* negative ACK */
}

/* Copy data from lpDDEData here.*/

if (lpDDEData->fAckReq) {
PostMessage(hwndServerDDE,
WM_DDE_ACK,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_ACK, 0x8000,
atomItem)); /* positive ACK */
}

bRelease = lpDDEData->fRelease;
GlobalUnlock(hData);
if (bRelease)
GlobalFree(hData);

In this example, the client examines the format of the data. If the format is not CF_TEXT (or if the client cannot lock the memory for the data), the client sends a negative WM_DDE_ACK_win32_WM_DDE_ACK message to indicate that it cannot process the data. If the client cannot lock a data handle because the handle contains the fAckReq member, the client should not send a negative WM_DDE_ACK message. Instead, the client should terminate the conversation.
If a client sends a negative acknowledgement in response to a WM_DDE_DATA_win32_WM_DDE_DATA message, the server is responsible for freeing the memory (but not the lParam parameter) referenced by the WM_DDE_DATA message associated with the negative acknowledgement.
If it can process the data, the client examines the fAckReq member of the DDEDATA_win32_DDEDATA_str structure to determine whether the server requested that it be informed that the client received and processed the data successfully. If the server did request this information, the client sends the server a positive WM_DDE_ACK message.
Because unlocking data invalidates the pointer to the data, the client saves the value of the fRelease member before unlocking the data object. After saving the value, the client then examines it to determine whether the server application requested the client to free the memory containing the data; the client acts accordingly.
Upon receiving a negative WM_DDE_ACK_win32_WM_DDE_ACK message, the client can ask for the same item value again, specifying a different clipboard format. Typically, a client will first ask for the most complex format it can support, then step down if necessary through progressively simpler formats until it finds one the server can provide.
If the server supports the Formats item of the system topic, the client can determine once what clipboard formats the server supports, instead of determining them each time the client requests an item. For more information about the system topic, see The System Topic_win32_The_System_Topic.
+$#A Submitting an Item to the Server
The client may send an item value to the server by using the WM_DDE_POKE_win32_WM_DDE_POKE message. The client renders the item to be sent and sends the WM_DDE_POKE message, as illustrated in the following example.
if (!(hPokeData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
(LONG) sizeof(DDEPOKE) + lstrlen(szValue) + 2)))
return;

if (!(lpPokeData = (DDEPOKE FAR*) GlobalLock(hPokeData))) {
GlobalFree(hPokeData);
return;
}

lpPokeData->fRelease = TRUE;
lpPokeData->cfFormat = CF_TEXT;
lstrcpy((LPSTR) lpPokeData->Value, (LPSTR) szValue);

/* Each line of CF_TEXT data is terminated by CR/LF. */

lstrcat((LPSTR) lpPokeData->Value, (LPSTR) "\r\n");
GlobalUnlock(hPokeData);
if ((atomItem = GlobalAddAtom((LPSTR) szItem)) != 0) {

.
.
.

if (!PostMessage(hwndServerDDE,
WM_DDE_POKE,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_POKE, (UINT) hPokeData,
atomItem))) {
GlobalDeleteAtom(atomItem);
GlobalFree(hPokeData);
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

Note that sending data by using a WM_DDE_POKE_win32_WM_DDE_POKE message is essentially the same as sending it by using WM_DDE_DATA_win32_WM_DDE_DATA, except that WM_DDE_POKE is sent from the client to the server.
If the server is able to accept the data-item value in the format rendered by the client, the server processes the item value as appropriate and sends the client a positive WM_DDE_ACK_win32_WM_DDE_ACK message. If it is unable to process the item value, because of its format or for other reasons, the server sends the client a negative WM_DDE_ACK message.
UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT) &hPokeData,
(PUINT) &atomItem);
GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE);
if (!(lpPokeData = (DDEPOKE FAR*) GlobalLock(hPokeData))
|| lpPokeData->cfFormat != CF_TEXT
|| !IsItemSupportedByServer(szItemName)) {
PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem)); /* negative ACK */
}

lstrcpy(szItemValue, lpPokeData->Value); /* copies value */
bRelease = lpPokeData->fRelease;
GlobalUnlock(hPokeData);
if (bRelease) {
GlobalFree(hPokeData);
}

PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK,
0x8000, atomItem)); /* positive ACK */

In this example, the server calls GlobalGetAtomName_win32_GlobalGetAtomName to retrieve the name of the item the client sent. The server then determines whether it supports the item and whether the item is rendered in the correct format (that is, CF_TEXT). If the item is not supported and not rendered in the correct format, or if the server cannot lock the memory for the data, the server sends a negative acknowledgment back to the client application. Note that in this case, sending a negative acknowledgement is correct because WM_DDE_POKE_win32_WM_DDE_POKE messages are always assumed to have the fAckReq member set. The server should ignore the member.
If a server sends a negative acknowledgement in response to a WM_DDE_POKE message, the client is responsible for freeing the memory (but not the lParam parameter) referenced by the WM_DDE_POKE message associated with the negative acknowledgement.
+$#A Establishing a Permanent Data Link
A client application can use DDE to establish a link to an item in a server application. After such a link is established, the server sends periodic updates of the linked item to the client, typically, whenever the value of the item changes. Thus, a permanent data stream is established between the two applications; this data stream remains in place until it is explicitly disconnected.
+$#A Initiating a Data Link
The client initiates a data link by posting a WM_DDE_ADVISE_win32_WM_DDE_ADVISE message, as shown in the following example.
if (!(hOptions = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
sizeof(DDEADVISE))))
return;
if (!(lpOptions = (DDEADVISE FAR*) GlobalLock(hOptions))) {
GlobalFree(hOptions);
return;
}

lpOptions->cfFormat = CF_TEXT;
lpOptions->fAckReq = TRUE;
lpOptions->fDeferUpd = FALSE;
GlobalUnlock(hOptions);
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!(PostMessage(hwndServerDDE,
WM_DDE_ADVISE,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_ADVISE, (UINT) hOptions,
atomItem)))) {
GlobalDeleteAtom(atomItem);
GlobalFree(hOptions);
FreeDDElParam(WM_DDE_ADVISE, lParam);
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

In this example, the client application sets the fDeferUpd flag of the WM_DDE_ADVISE_win32_WM_DDE_ADVISE message to FALSE. This directs the server application to send the data to the client whenever the data changes.
If the server is unable to service the WM_DDE_ADVISE request, it sends the client a negative WM_DDE_ACK_win32_WM_DDE_ACK message. But if the server has access to the item and can render it in the requested format, the server notes the new link (recalling the flags specified in the hOptions parameter) and sends the client a positive WM_DDE_ACK message. From then on, until the client issues a matching WM_DDE_UNADVISE_win32_WM_DDE_UNADVISE message, the server sends the new data to the client every time the value of the item changes in the server.
The WM_DDE_ADVISE message establishes the format of the data to be exchanged during the link. If the client attempts to establish another link with the same item but is using a different data format, the server can choose to reject the second data format or attempt to support it. If a warm link has been established for any data item, the server can support only one data format at a time. This is because the WM_DDE_DATA_win32_WM_DDE_DATA message for a warm link has a NULL data handle, which otherwise contains the format information. Thus, a server must reject all warm links for an item already linked, and must reject all links for an item that has warm links. Another interpretation may be that the server changes the format and the hot or warm state of a link when a second link is requested for the same data item.
In general, client applications should not attempt to establish more than one link at a time for a data item.
+$#A Initiating a Data Link with the Paste Link Command
Applications that support hot or warm data links typically support a registered clipboard format named Link. When associated with the application's Copy and Paste Link commands, this clipboard format enables the user to establish DDE conversations between applications simply by copying a data item in the server application and pasting it into the client application.
A server application supports the Link clipboard format by placing in the clipboard a string containing the application, topic, and item names when the user chooses the Copy command from the Edit menu. Following is the standard Link format:
application\0topic\0item\0\0
A single null character separates the names, and two null characters terminate the entire string.
Both the client and server applications must register the Link clipboard format, as shown:
cfLink = RegisterClipboardFormat("Link");

A client application supports the Link clipboard format by means of a Paste Link command on its Edit menu. When the user chooses this command, the client application parses the application, topic, and item names from the Link-format clipboard data. Using these names, the client application initiates a conversation for the application and topic, if such a conversation does not already exist. The client application then sends a WM_DDE_ADVISE_win32_WM_DDE_ADVISE message to the server application, specifying the item name contained in the Link-format clipboard data.
Following is an example of a client application's response when the user chooses the Paste Link command.
void DoPasteLink(hwndClientDDE)
HWND hwndClientDDE;
{
HANDLE hData;
LPSTR lpData;
HWND hwndServerDDE;
CHAR szApplication[APP_MAX_SIZE + 1];
CHAR szTopic[TOPIC_MAX_SIZE + 1];
CHAR szItem[ITEM_MAX_SIZE + 1];
int nBufLen;

if (OpenClipboard(hwndClientDDE)) {
if (!(hData = GetClipboardData(cfLink)) ||
!(lpData = GlobalLock(hData))) {
CloseClipboard();
return;
}

/* Parse the clipboard data. */

if ((nBufLen = lstrlen(lpData)) >= APP_MAX_SIZE) {
CloseClipboard();
GlobalUnlock(hData);
return;
}
lstrcpy(szApplication, lpData);
lpData += (nBufLen + 1); /* skips over null */
if ((nBufLen = lstrlen(lpData)) >= TOPIC_MAX_SIZE) {
CloseClipboard();
GlobalUnlock(hData);
return;
}
lstrcpy(szTopic, lpData);
lpData += (nBufLen + 1); /* skips over null */
if ((nBufLen = lstrlen(lpData)) >= ITEM_MAX_SIZE) {
CloseClipboard();
GlobalUnlock(hData);
return;
}
lstrcpy(szItem, lpData);
GlobalUnlock(hData);
CloseClipboard();

if (hwndServerDDE =
FindServerGivenAppTopic(szApplication, szTopic)) {

/* App/topic conversation is already started. */

if (DoesAdviseAlreadyExist(hwndServerDDE, szItem))
MessageBox(hwndMain,
"Advisory already established",
"Client", MB_ICONEXCLAMATION | MB_OK);
else
SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
}
else {

/* Client must initiate a new conversation first. */

SendInitiate(szApplication, szTopic);
if (hwndServerDDE =
FindServerGivenAppTopic(szApplication,
szTopic))
SendAdvise(hwndClientDDE, hwndServerDDE, szItem);
}
}
return;
}

In this example, the client application opens the clipboard and determines whether it contains data in the Link format (that is, cfLink) it had previously registered. If not, or if it cannot lock the data in the clipboard, the client returns.
After the client application retrieves a pointer to the clipboard data, it parses the data to extract the application, topic, and item names.
The client application determines whether a conversation on the topic already exists between it and the server application. If a conversation does exist, the client checks whether a link already exists for the data item. If such a link exists, the client displays a message box to the user; otherwise, it calls its own SendAdvise function to send a WM_DDE_ADVISE_win32_WM_DDE_ADVISE message to the server for the item.
If a conversation on the topic does not already exist between the client and the server, the client first calls its own SendInitiate function to broadcast the WM_DDE_INITIATE_w in 32_WM_D DE_I NITIATE message to request a conversation and, second, calls its own FindServerGivenAppTopic function to establish the conversation with the window that responds on behalf of the server application. After the conversation has begun, the client application calls SendAdvise to request the link.
+$#A Notifying the Client that Data Has Changed
When the client establishes a link by using the WM_DDE_ADVISE_win32_WM_DDE_ADVISE message, with the fDeferUpd member not set (that is, equal to zero) in the DDEDATA_win32_DDEDATA_str structure, the client has requested the server send the data item each time the item's value changes. In such cases, the server renders the new value of the data item in the previously specified format and sends the client a WM_DDE_DATA_win32_WM_DDE_DATA message, as shown in the following example.
/*
* Allocate the size of a DDE data header, plus data (a string),
* plus a <CR><LF><NULL>
*/

if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
sizeof(DDEDATA) + lstrlen(szItemValue) + 3)))
return;
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) {
GlobalFree(hData);
return;
}

lpData->fAckReq = bAckRequest; /* as specified in original */
/* WM_DDE_ADVISE message */
lpData->cfFormat = CF_TEXT;
lstrcpy(lpData->Value, szItemValue); /* copies value to be sent */
lstrcat(lpData->Value, "\r\n"); /* CR/LF for CF_TEXT format */
GlobalUnlock(hData);
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!PostMessage(hwndClientDDE,
WM_DDE_DATA,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_DATA, (UINT) hData, atomItem))) {
GlobalFree(hData);
GlobalDeleteAtom(atomItem);
FreeDDElParam(WM_DDE_DATA, lParam);
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

In this example, the client processes the item value as appropriate. If the fAckReq flag for the item is set, the client sends the server a positive WM_DDE_ACK_win32_WM_DDE_ACK message.
When the client establishes the link, with the fDeferUpd member set (that is, equal to 1), the client has requested that only a notification, not the data itself, be sent each time the data changes. In such cases, when the item value changes, the server does not render the value but simply sends the client a WM_DDE_DATA_win32_WM_DDE_DATA message with a null data handle, as illustrated in the following example.
if (bDeferUpd) { /* checking whether the flag was originally */
/* set in the WM_DDE_ADVISE message */
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!PostMessage(hwndClientDDE,
WM_DDE_DATA,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_DATA, 0,
atomItem))) { /* NULL data */
GlobalDeleteAtom(atomItem);
FreeDDElParam(WM_DDE_DATA, lParam);
}
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

As necessary, the client can request the latest value of the data item by issuing a normal WM_DDE_REQUEST_win32_WM_DDE_REQUEST message, or it can simply ignore the notice from the server that the data has changed. In either case, if fAckReq is equal to 1, the client is expected to send a positive WM_DDE_ACK_win32_WM_DDE_ACK message to the server.
+$#A Terminating a Data Link
If the client requests that a specific data link be terminated, the client sends the server a WM_DDE_UNADVISE_win32_WM_DDE_UNADVISE message, as shown in the following example.
if ((atomItem = GlobalAddAtom(szItemName)) != 0) {
if (!PostMessage(hwndServerDDE,
WM_DDE_UNADVISE,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_UNADVISE, 0, atomItem))) {
GlobalDeleteAtom(atomItem);
FreeDDElParam(WM_DDE_UNADVISE, lParam);
}
}

if (atomItem == 0) {

.
. /* error handling */
.

}

The server checks whether the client currently has a link to the specific item in this conversation. If a link exists, the server sends the client a positive WM_DDE_ACK_win32_WM_DDE_ACK message; the server is then no longer required to send updates about the item. If no link exists, the server sends the client a negative WM_DDE_ACK message.
The WM_DDE_UNADVISE_win32_WM_DDE_UNADVISE message specifies a data format. A format of zero informs the server to stop all links for the specified item, even if several hot links are established and each uses a different format.
To terminate all links for a conversation, the client application sends the server a WM_DDE_UNADVISE message with a null item atom. The server determines whether the conversation has at least one link currently established. If a link exists, the server sends the client a positive WM_DDE_ACK message; the server then no longer has to send any updates in the conversation. If no link exists, the server sends the client a negative WM_DDE_ACK message.
+$#A Carrying Out Commands in a Server Application
A Win32-based application can use the WM_DDE_EXECUTE_win32_WM_DDE_EXECUTE message to cause a certain command or series of commands to be carried out in another application. To do this, the client sends the server a WM_DDE_EXECUTE message containing a handle to a command string, as shown in the following example.
if (!(hCommand = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
sizeof(szCommandString) + 1)))
return;
if (!(lpCommand = GlobalLock(hCommand))) {
GlobalFree(hCommand);
return;
}

lstrcpy(lpCommand, szCommandString);
GlobalUnlock(hCommand);
if (!PostMessage(hwndServerDDE,
WM_DDE_EXECUTE,
(WPARAM) hwndClientDDE,
PackDDElParam(WM_DDE_EXECUTE, 0, (UINT) hCommand))) {
GlobalFree(hCommand);
FreeDDElParam(WM_DDE_EXECUTE, lParam);
}

In this example, the server attempts to carry out the specified command string. If it succeeds, the server sends the client a positive WM_DDE_ACK_win32_WM_DDE_ACK message; otherwise, it sends a negative WM_DDE_ACK message. This WM_DDE_ACK message reuses the hCommand handle passed in the original WM_DDE_EXECUTE_win32_WM_DDE_EXECUTE message.
If the client's command execution string requests that the server terminate, the server should respond by sending a positive WM_DDE_ACK message and then post a WM_DDE_TERMINATE_win32_WM_DDE_TERMINATE message before terminating. All other commands sent with a WM_DDE_EXECUTE message should be executed synchronously; that is, the server should send a WM_DDE_ACK message only after successfully completing the command.
+$#A Terminating a Conversation
Either the client or the server can issue a WM_DDE_TERMINATE_win32_WM_DDE_TERMINATE message to terminate a conversation at any time. Similarly, both the client and server applications should be prepared to receive this message at any time. An application must terminate all of its conversations before shutting down.
In the following example, the application terminating the conversation posts a WM_DDE_TERMINATE message.
PostMessage(hwndServerDDE, WM_DDE_TERMINATE,
PackDDElParam(WM_DDE_TERMINATE, (UINT) hwndClientDDE, 0), 0);

This informs the other application that the sending application will send no further messages and the recipient can close its window. The recipient is expected in all cases to respond promptly by sending a WM_DDE_TERMINATE_win32_WM_DDE_TERMINATE message. The recipient must not send a negative, busy, or positive WM_DDE_ACK_win32_WM_DDE_ACK message.
After an application has sent the WM_DDE_TERMINATE message to the partner in a DDE conversation, it must not respond to messages from that partner, since the partner might have destroyed the window to which the response would be sent.
If an application receives a DDE message other than WM_DDE_TERMINATE after it has posted WM_DDE_TERMINATE, it should free all objects associated with the received messages except the data handles for WM_DDE_DATA_win32_WM_DDE_DATA or WM_DDE_POKE_win32_WM_DDE_POKE messages that do not have the fRelease member set.
When an application is about to terminate, it should end all active DDE conversations before completing processing of the WM_DESTROY_win32_WM_DESTROY message. However, if an application does not end its active DDE conversations, the system will terminate any DDE conversations associated with a window when the window is destroyed. The following example shows how a server application terminates all DDE conversations.
void TerminateConversations(hwndServerDDE)
HWND hwndServerDDE;
{
HWND hwndClientDDE;

/* Terminate each active conversation. */

while (hwndClientDDE = GetNextLink(hwndClientDDE)) {
SendTerminate(hwndServerDDE, hwndClientDDE);
}
return;
}

BOOL AtLeastOneLinkActive(VOID)
{
return TRUE;
}

HWND GetNextLink(hwndDummy)
HWND hwndDummy;
{
return (HWND) 1;
}

VOID SendTerminate(HWND hwndServerDDE, HWND hwndClientDDE)
{
return;
}

+$#AK Dynamic Data Exchange Reference
The following functions and messages are associated with dynamic data exchange.
+$#A Dynamic Data Exchange Functions
Following are the functions used with DDE.
DdeSetQualityOfService_win32_DdeSetQualityOfServiceFreeDDElParam_win32_FreeDDElParamImpersonateDdeClientWindow_win32_ImpersonateDdeClientWindowPackDDElParam_win32_PackDDElParamReuseDDElParam_win32_ReuseDDElParamUnpackDDElParam_win32_UnpackDDElParam
+$#A Dynamic Data Exchange Messages
Following are the messages used with DDE.
WM_DDE_ACK_win32_WM_DDE_ACKWM_DDE_ADVISE_win32_WM_DDE_ADVISEWM_DDE_DATA_win32_WM_DDE_DATAWM_DDE_EXECUTE_win32_WM_DDE_EXECUTEWM_DDE_INITIATE_win32_WM_DDE_INITIATEWM_DDE_POKE_win32_WM_DDE_POKEWM_DDE_REQUEST_win32_WM_DDE_REQUESTWM_DDE_TERMINATE_win32_WM_DDE_TERMINATEWM_DDE_UNADVISE_win32_WM_DDE_UNADVISE

+$#AK Dynamic Data Exchange Management Library
In Microsoft® Windows®, dynamic data exchange (DDE) is a form of interprocess communications that uses shared memory to exchange data between applications. Applications can use DDE for one-time data transfers and for ongoing exchanges and updating of data.
+$#A About the DDEML
DDE differs from the clipboard data-transfer mechanism that is also part of the Windows operating system. One difference is that the clipboard is almost always used as a one-time response to a specific action by the user ¾ such as choosing the Paste command from a menu. Although DDE may also be initiated by a user, it typically continues without the user's further involvement.
The dynamic data exchange management library (DDEML) provides an application programming interface (API) that simplifies the task of adding DDE capability to a Windows application. Instead of sending, posting, and processing DDE messages directly, an application uses the functions provided by the DDEML to manage DDE conversations. A DDE conversation is the interaction between client and server applications. The DDEML also provides a means for managing the strings and data shared among DDE applications. Instead of using atoms and pointers to shared memory objects, DDE applications create and exchange string handles, which identify strings, and data handles, which identify DDE objects. The DDEML provides a function (DdeNameService_win32_DdeNameService) that enables a server application to register the service names it supports. The service names are then broadcast to other applications in the system, which use the names to connect to the server. The DDEML also ensures compatibility among DDE applications by requiring them to implement the DDE protocol in a consistent manner.
Existing applications using the message-based DDE protocol are fully compatible with those that use the DDEML; that is, an application using message-based DDE can establish conversations and perform transactions with applications using the DDEML. Instead of using DDE messages in your new application, take advantage of the DDEML and the many improvements it offers.
To use the API elements of the DDEML, you must include the DDEML.H header file in your source files, link with the USER32.LIB file, and ensure that the DDEML.DLL file resides in the system's path.
+$#A Basic Concepts
The concepts in this section are key to understanding DDE and the DDEML.
+$#A Client and Server Interaction
DDE always occurs between a client application and a server application. The DDE client application initiates the exchange by establishing a conversation with the server to send transactions to the server. A transaction is a request for data or services. The DDE server application responds to transactions by providing data or services to the client. For example, a graphics application might contain a bar graph representing a corporation's quarterly profits, but the data for the bar graph might be contained in a spreadsheet application. To obtain the latest profit figures, the graphics application (the client) could establish a conversation with the spreadsheet application (the server). The graphics application could then send a transaction to the spreadsheet application, requesting the latest profit figures.
A server can have many clients at the same time, and a client can request data from multiple servers. An application can also be both a client and a server. Either the client or the server can terminate the conversation at any time.
+$#A Transactions and the DDE Callback Function
The DDEML notifies an application about DDE activity that affects the application by sending transactions to the application's DDE callback function. A DDE transaction is similar to a message ¾ it is a named constant accompanied by other parameters that contain additional information about the transaction.
The DDEML passes a transaction to an application-defined DDE callback function that carries out an action appropriate to the type of transaction. For example, when a client application attempts to establish a conversation with a server application, the client calls the DdeConnect_win32_DdeConnect function. This function causes the DDEML to send an XTYP_CONNECT_win32_XTYP_CONNECT transaction to the server's DDE callback function. The callback function can allow the conversation by returning TRUE to the DDEML, or it can deny the conversation by returning FALSE. For a detailed discussion of transactions, see Transaction Management_win32_Transaction_Management.
+$#A Service Names, Topic Names, and Item Names
A DDE server uses a three-level hierarchy ¾ service name (called "application name" in previous DDE documentation), topic name, and item name ¾ to uniquely identify a unit of data the server can exchange during a conversation.
A service name is a string a server application responds to when a client attempts to establish a conversation with the server. A client must specify this service name to establish a conversation with the server. Although a server can respond to many service names, most servers respond to only one name.
A topic name is a string that identifies a logical data context. For servers that operate on file-based documents, topic names are typically filenames; for other servers, they are other application-specific strings. A client must specify a topic name along with a server's service name when it attempts to establish a conversation with a server.
An item name is a string that identifies a unit of data a server can pass to a client during a transaction. For example, an item name might identify an integer, a string, several paragraphs of text, or a bitmap.
The service, topic, and item names enable the client to establish a conversation with a server and to receive data from the server.
+$#A System Topic
The System topic provides a context for information of general interest to any DDE client. It is recommended that server applications support the System topic at all times. The System topic is defined in the DDEML.H header file as SZDDESYS_TOPIC.
To determine which servers are present and the kinds of information they can provide, a client application can request a conversation on the System topic upon starting, setting the device name to NULL. Such wildcard conversations are costly in terms of system performance, so they should be kept to a minimum. For more information about initiating DDE conversations, see Conversation Management_win32_Conversation_Management.
A server must support the following item names within the System topic and any other item names that are useful to a client.
Item Description
SZDDE_ITEM_ITEMLIST A list of the items supported under a non-System topic. (This list may vary from moment to moment and from topic to topic.)
SZDDESYS_ITEM_FORMATS A tab-delimited list of strings representing all clipboard formats potentially supported by the service application. Strings that represent predefined clipboard formats are equivalent to the CF_ values with the "CF_" prefix removed. For example, the CF_TEXT format is represented by the string "TEXT". These strings must be uppercase to further identify them as predefined formats. The list of formats must appear in the order of most rich in content to least rich in content.
For more information about clipboard formats and rendering data, see Clipboard_win32_Clipboard.
SZDDESYS_ITEM_HELP User-readable information of general interest. This item must contain, at a minimum, information on how to use the server application's DDE features. This information may include, but is not limited to, how to specify items within topics, what execute strings the server can perform, what poke transactions are allowed, and how to find help on other System topic items.
SZDDESYS_ITEM_RTNMSG Supporting detail for the most recently used WM_DDE_ACK_win32_WM_DDE_ACK message. This item is useful when more than 8 bits of application-specific return data are required.
SZDDESYS_ITEM_STATUS An indication of the current status of the server. Typically, this item supports only the CF_TEXT format and contains the Ready or Busy string.
SZDDESYS_ITEM_SYSITEMS A list of the items supported under the System topic by this server.
SZDDESYS_ITEM_TOPICS A list of the topics supported by the server at the current time. (This list may vary from moment to moment.)

These item names are values defined in the DDEML.H header file. To obtain string handles for these strings, an application must use the DDEML string-management functions, just as it would for any other string in a DDEML application. For more information about managing strings, see String Management_win32_String_Management.
+$#A DDEML and Threads
The DDEInitialize function registers an application with the DDEML, creating a DDEML instance. A DDEML instance is thread-based, associated with the thread that called DDEInitialize.
All DDEML function calls for objects belonging to a DDEML instance must be made from the same thread that called DDEInitialize to create the instance. If you call a DDEML function from a different thread, the function will fail. You cannot access a DDEML conversation from a thread other than the one that allocated the conversation.
+$#A Initialization
Before calling any other DDEML function, an application must call the DdeInitialize_win32_DdeInitialize function. DdeInitialize obtains an instance identifier for the application, registers the application's DDE callback function with the DDEML, and specifies the transaction filter flags for the callback function.
Each instance of an application or a dynamic-link library (DLL) must pass its instance identifier as the idInst parameter to any other DDEML function that requires it. The purpose of multiple DDEML instances is to support DLLs that must use the DDEML at the same time an application is using it. An application must not use more than one instance of the DDEML.
Transaction filters optimize system performance by preventing the DDEML from passing unwanted transactions to the application's DDE callback function. An application sets the transaction filters when it calls DdeInitialize. An application must specify a transaction filter flag for each type of transaction that it does not process in its callback function. An application can change its transaction filters with a subsequent call to DdeInitialize. For a complete list of transaction filter flags, see the description of DdeInitialize in the Microsoft Win32 Programmer's Reference, Volume 3. For more information about transactions, see Transaction Management_win32_Transaction_Management.
The following example shows how to initialize an application to use the DDEML.
DWORD idInst = 0;
HINSTANCE hinst;

.
.
.
DdeInitialize(&idInst, /* receives instance identifier */
(PFNCALLBACK) DdeCallback, /* address of callback function */
CBF_FAIL_EXECUTES | /* filter XTYPE_EXECUTE */
CBF_SKIP_ALLNOTIFICATIONS, /* filter notifications */
0);
.
.
.

An application must call the DdeUninitialize_win32_DdeUninitialize function when it is no longer going to use the DDEML. This function terminates any conversations currently open for the application and frees the DDEML resources the system allocated for the application.
+$#A Callback Function
An application that uses the DDEML must provide a callback function that processes the DDE events affecting the application. The DDEML notifies an application of such events by sending transactions to the application's DDE callback function. The transactions a callback function receives depend on which callback filter flags the application specified in DdeInitialize_win32_DdeInitialize and whether the application is a client, a server, or both. The following example shows the general structure of a callback function for a typical client application.
HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1,
hsz2, hdata, dwData1, dwData2)
UINT uType; /* transaction type */
UINT uFmt; /* clipboard data format */
HCONV hconv; /* handle of conversation */
HSZ hsz1; /* handle of string */
HSZ hsz2; /* handle of string */
HDDEDATA hdata; /* handle of global memory object */
DWORD dwData1; /* transaction-specific data */
DWORD dwData2; /* transaction-specific data */
{
switch (uType) {
case XTYP_REGISTER:
case XTYP_UNREGISTER:
.
.
.
return (HDDEDATA) NULL;

case XTYP_ADVDATA:
.
.
.
return (HDDEDATA) DDE_FACK;

case XTYP_XACT_COMPLETE:
.
.
.
return (HDDEDATA) NULL;

case XTYP_DISCONNECT:
.
.
.
return (HDDEDATA) NULL;

default:
return (HDDEDATA) NULL;
}
}

The uType parameter specifies the transaction type sent to the callback function by the DDEML. The values of the remaining parameters depend on the transaction type. The transaction types and the events that generate them are described in the following topics. For detailed information about each transaction type, see Transaction Management_win32_Transaction_Management.
+$#A String Management
To carry out a DDE task, many DDEML functions require access to strings. For example, a client must specify a service name and a topic name when it calls the DdeConnect_win32_DdeConnect function to request a conversation with a server. An application specifies a string by passing a string handle (HSZ) rather than a pointer in a DDEML function. A string handle is a doubleword value, assigned by the system, that identifies a string.
An application can obtain a string handle for a particular string by calling the DdeCreateStringHandle_win32_DdeCreateStringHandle function. This function registers the string with the system and returns a string handle to the application. The application can pass the handle to DDEML functions that must access the string. The following example obtains string handles for the System topic string and the service name string.
HSZ hszServName;
HSZ hszSysTopic;
.
.
.
hszServName = DdeCreateStringHandle(
idInst, /* instance identifier */
"MyServer", /* string to register */
CP_WINANSI); /* Windows ANSI code page */

hszSysTopic = DdeCreateStringHandle(
idInst, /* instance identifier */
SZDDESYS_TOPIC, /* System topic */
CP_WINANSI); /* Windows ANSI code page */
.
.
.

The idInst parameter in the preceding example specifies the instance identifier obtained by the DdeInitialize_win32_DdeInitialize function.
An application's DDE callback function receives one or more string handles during most DDE transactions. For example, a server receives two string handles during the XTYP_REQUEST_win32_XTYP_REQUEST transaction: one identifies a string specifying a topic name, and the other identifies a string specifying an item name. An application can obtain the length of the string that corresponds to a string handle and copy the string to an application-defined buffer by calling the DdeQueryString_win32_DdeQueryString function, as shown in the following example.
DWORD idInst;
DWORD cb;
HSZ hszServ;
PSTR pszServName;
.
.
.
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0,
CP_WINANSI) + 1;
pszServName = (PSTR) LocalAlloc(LPTR, (UINT) cb);
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI);
.
.
.

An instance-specific string handle cannot be mapped from string handle to string and back to string handle. For instance, although DdeQueryString_win32_DdeQueryString creates a string from a string handle and then DdeCreateStringHandle_win32_DdeCreateStringHandle creates a string handle from that string, the two handles are not the same, as shown in the following example.
DWORD idInst;
DWORD cb;
HSZ hszInst, hszNew;
PSZ pszInst;
.
.
.
DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI);
hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI);
/* hszNew != hszInst ! */
.
.
.

A string handle passed to an application's DDE callback function becomes invalid when the callback function returns. An application can save a string handle for use after the callback function returns by using the DdeKeepStringHandle_win32_DdeKeepStringHandle function.
When an application calls DdeCreateStringHandle_win32_DdeCreateStringHandle, the system enters the specified string into a string table and generates a handle that it uses to access the string. The system also maintains a usage count for each string in the string table.
When an application calls DdeCreateStringHandle and specifies a string that already exists in the table, the system increments the usage count rather than adding another occurrence of the string. (An application can also increment the usage count by using DdeKeepStringHandle.) When an application calls the DdeFreeStringHandle_win32_DdeFreeStringHandle function, the system decrements the usage count.
A string is removed from the table when its usage count equals zero. Because more than one application can obtain the handle of a particular string, an application must not free a string handle more times than it has created or retained the handle. Otherwise, the application can cause the string to be removed from the table, denying other applications access to the string.
The DDEML string-management functions are based on the Windows atom manager and are subject to the same size restrictions as are atoms.
+$#A Name Service
The DDEML makes it possible for a server application to register the service names that it supports and to prevent the DDEML from sending XTYP_CONNECT_win32_XTYP_CONNECT transactions for unsupported service names to the server's DDE callback function. The remaining topics in this section describe the name service.
+$#A Service Name Registration
By registering its service names with the DDEML, a server informs other DDE applications in the system that a new server is available. A server registers a service name by calling the DdeNameService_win32_DdeNameService function and specifying a string handle that identifies the name. In response, the DDEML sends an XTYP_REGISTER_win32_XTYP_REGISTER transaction to the callback function of each DDEML application in the system (except those that specified the CBF_SKIP_REGISTRATIONS filter flag in the DdeInitialize_win32_DdeInitialize function). The XTYP_REGISTER transaction passes two string handles to a callback function: the first identifies the string specifying the base service name, and the second identifies the string specifying the instance-specific service. A client typically uses the base service name in a list of available servers, so the user can select a server from the list. The client uses the instance-specific service name to establish a conversation with a specific instance of a server application, if more than one instance is running.
A server can use DdeNameService to unregister a service name. This function causes the DDEML to send XTYP_UNREGISTER_win32_XTYP_UNREGISTER transactions to the other DDE applications in the system, informing them that they can no longer use the name to establish conversations.
A server must call DdeNameService to register its service names soon after calling DdeInitialize. A server must unregister its service names by using DdeNameService just before calling the DdeUninitialize_win32_DdeUninitialize function.
+$#A Service Name Filter
In addition to registering service names, DdeNameService_win32_DdeNameService enables a server to turn its service name filter on or off. When a server turns off its service name filter, the DDEML sends the XTYP_CONNECT_win32_XTYP_CONNECT transaction to the server's DDE callback function whenever any client calls the DdeConnect_win32_DdeConnect function, regardless of the service name specified in the function. When a server turns on its service name filter, the DDEML sends the XTYP_CONNECT transaction to the server only when DdeConnect specifies a service name the server has specified in a call to DdeNameService.
By default, the service name filter is on when an application calls DdeInitialize_win32_DdeInitialize. This default prevents the DDEML from sending the XTYP_CONNECT transaction to a server before the server has created the string handles it needs. A server can turn off its service name filter by specifying the DNS_FILTEROFF flag in a call to DdeNameService. The DNS_FILTERON flag turns on the filter.
+$#A Conversation Management
A conversation between a client and a server is always established at the request of the client. When a conversation is established, each partner receives a handle that identifies the conversation. The partners use this handle in other DDEML functions to send transactions and manage the conversation. A client can request a conversation with a single server, or it can request multiple conversations with one or more servers.
The remaining topics in this section describe how an application establishes new conversations and gets information about existing conversations.
+$#A Single Conversations
A client application requests a single conversation with a server by calling the DdeConnect_win32_DdeConnect function and specifying string handles that identify the strings containing the service name of the server application and the topic name for the conversation. The DDEML responds by sending the XTYP_CONNECT_win32_XTYP_CONNECT transaction to the DDE callback function of each server application that either has registered a service name that matches the one specified in DdeConnect or has turned service name filtering off by calling DdeNameService_win32_DdeNameService. A server can also filter XTYP_CONNECT transactions by specifying the CBF_FAIL_CONNECTIONS filter flag in the DdeInitialize_win32_DdeInitialize function. During the XTYP_CONNECT transaction, the DDEML passes the service name and the topic name to the server. The server must examine the names and return TRUE if it supports the service name and topic name pair or FALSE if it does not.
If no server responds positively to the client's request to connect, the client receives NULL from DdeConnect and no conversation is established. If a server returns TRUE, a conversation is established and the client receives a conversation handle ¾ a doubleword value that identifies the conversation. The client uses the handle in subsequent DDEML calls to obtain data from the server. The server receives the XTYP_CONNECT_CONFIRM_win32_XTYP_CONNECT_CONFIRM transaction (unless the server specified the CBF_SKIP_CONNECT_CONFIRMS filter flag). This transaction passes the conversation handle to the server.
The following example requests a conversation on the System topic with a server that recognizes the service name MyServer. The hszServName and hszSysTopic parameters are previously created string handles.
HCONV hConv; /* conversation handle */
HWND hwndParent; /* parent window handle */
HSZ hszServName; /* service name string handle */
HSZ hszSysTopic; /* System topic string handle */
.
.

hConv = DdeConnect(
idInst, /* instance identifier */
hszServName, /* service name string handle */
hszSysTopic, /* System topic string handle */
(PCONVCONTEXT) NULL); /* use default context */

if (hConv == NULL) {
MessageBox(hwndParent, "MyServer is unavailable.",
(LPSTR) NULL, MB_OK);
return FALSE;
}
.
.
.

In the preceding example, DdeConnect_win32_DdeConnect causes the DDE callback function of the MyServer application to receive an XTYP_CONNECT_win32_XTYP_CONNECT transaction.
In the following example, the server responds to the XTYP_CONNECT transaction by comparing the topic name string handle the DDEML passed to the server with each element in the array of topic name string handles the server supports. If the server finds a match, it establishes the conversation.
#define CTOPICS 5

HSZ hsz1; /* string handle passed by DDEML */
HSZ ahszTopics[CTOPICS]; /* array of supported topics */
int i; /* loop counter */

.
. /* Use a switch statement to examine transaction types. */
.

case XTYP_CONNECT:
for (i = 0; i < CTOPICS; i++) {
if (hsz1 == ahszTopics[i])
return TRUE; /* establish a conversation */
}

return FALSE; /* topic not supported; deny conversation */

.
. /* Process other transaction types. */
.

If the server returns TRUE in response to the XTYP_CONNECT_win32_XTYP_CONNECT transaction, the DDEML sends an XTYP_CONNECT_CONFIRM_win32_XTYP_CONNECT_CONFIRM transaction to the server's DDE callback function. The server can obtain the handle for the conversation by processing this transaction.
A client can establish a wildcard conversation by specifying NULL for the service name string handle, the topic name string handle, or both in a call to DdeConnect_win32_DdeConnect. If at least one of the string handles is NULL, the DDEML sends the XTYP_WILDCONNECT_win32_XTYP_WILDCONNECT transaction to the callback functions of all DDE applications (except those that filter the XTYP_WILDCONNECT transaction). Each server application should respond by returning a data handle that identifies a null-terminated array of HSZPAIR_win32_HSZPAIR_str structures. If the server application has not called DdeNameService_win32_DdeNameService to register its service names and if filtering is on, the server does not receive XTYP_WILDCONNECT transactions. For more information about data handles, see Data Management_win32_Data_Management.
The array must contain one structure for each service name and topic name pair that matches the pair specified by the client. The DDEML selects one of the pairs to establish a conversation and returns to the client a handle that identifies the conversation. The DDEML sends the XTYP_CONNECT_CONFIRM transaction to the server (unless the server filters this transaction). The following example shows a typical server response to the XTYP_WILDCONNECT transaction.
#define CTOPICS 2

UINT uType;
HSZPAIR ahszp[(CTOPICS + 1)];
HSZ ahszTopicList[CTOPICS];
HSZ hszServ, hszTopic;
WORD i, j;

if (uType == XTYP_WILDCONNECT) {

/*
* Scan the topic list and create an array of HSZPAIR
* structures.
*/

j = 0;
for (i = 0; i < CTOPICS; i++) {
if (hszTopic == (HSZ) NULL ||
hszTopic == ahszTopicList[i]) {
ahszp[j].hszSvc = hszServ;
ahszp[j++].hszTopic = ahszTopicList[i];
}
}

/*
* End the list with an HSZPAIR structure that contains NULL
* string handles as its members.
*/

ahszp[j].hszSvc = NULL;
ahszp[j++].hszTopic = NULL;

/*
* Return a handle to a global memory object containing the
* HSZPAIR structures.
*/

return DdeCreateDataHandle(
idInst, /* instance identifier */
(LPBYTE) &ahszp, /* points to HSZPAIR array */
sizeof(HSZ) * j, /* length of the array */
0, /* start at the beginning */
(HSZ) NULL, /* no item name string */
0, /* return the same format */
0); /* let the system own it */
}

Either the client or the server can terminate a conversation at any time by calling the DdeDisconnect_win32_DdeDisconnect function. This function causes the callback function of the partner in the conversation to receive the XTYP_DISCONNECT_win32_XTYP_DISCONNECT transaction (unless the partner specified the CBF_SKIP_DISCONNECTS filter flag). Typically, an application responds to the XTYP_DISCONNECT transaction by using the DdeQueryConvInfo_win32_DdeQueryConvInfo function to obtain information about the conversation that terminated. After the callback function returns from processing the XTYP_DISCONNECT transaction, the conversation handle is no longer valid.
A client application that receives an XTYP_DISCONNECT transaction in its DDE callback function can attempt to reestablish the conversation by calling the DdeReconnect_win32_DdeReconnect function. The client must call DdeReconnect from within its DDE callback function.
+$#A Multiple Conversations
A client application can use the DdeConnectList_win32_DdeConnectList function to determine whether any servers of interest are available in the system. A client specifies a service name and topic name when it calls DdeConnectList, causing the DDEML to broadcast the XTYP_WILDCONNECT_win32_XTYP_WILDCONNECT transaction to the DDE callback functions of all servers that match the service name (except those that filter the transaction). A server's callback function should return a data handle that identifies a null-terminated array of HSZPAIR_win32_HSZPAIR_str structures. The array should contain one structure for each service name and topic name pair that matches the pair specified by the client. The DDEML establishes a conversation for each HSZPAIR structure filled by the server and returns a conversation list handle to the client. The server receives the conversation handle by way of the XTYP_CONNECT_win32_XTYP_CONNECT_CONFIRM transaction (unless the server filters this transaction).
A client can specify NULL for the service name, topic name, or both when it calls DdeConnectList. If the service name is NULL, all servers in the system that support the specified topic name respond. A conversation is established with each responding server, including multiple instances of the same server. If the topic name is NULL, a conversation is established on each topic recognized by each server that matches the service name.
A client can use the DdeQueryNextServer_win32_DdeQueryNextServer and DdeQueryConvInfo_win32_DdeQueryConvInfo functions to identify the servers that respond to DdeConnectList. DdeQueryNextServer returns the next conversation handle in a conversation list, and DdeQueryConvInfo fills a CONVINFO_win32_CONVINFO_str structure with information about the conversation. The client can keep the conversation handles that it needs and discard the rest from the conversation list.
The following example uses DdeConnectList to establish conversations with all servers that support the System topic and then uses the DdeQueryNextServer and DdeQueryConvInfo functions to obtain the servers' service name string handles and store them in a buffer.
HCONVLIST hconvList; /* conversation list */
DWORD idInst; /* instance identifier */
HSZ hszSystem; /* System topic */
HCONV hconv = NULL; /* conversation handle */
CONVINFO ci; /* holds conversation data */
UINT cConv = 0; /* count of conv. handles */
HSZ *pHsz, *aHsz; /* point to string handles */

/* Connect to all servers that support the System topic. */

hconvList = DdeConnectList(idInst, NULL, hszSystem, NULL, NULL);

/* Count the number of handles in the conversation list. */

while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL)
cConv++;

/* Allocate a buffer for the string handles. */

hconv = NULL;
aHsz = (HSZ *) LocalAlloc(LMEM_FIXED, cConv * sizeof(HSZ));

/* Copy the string handles to the buffer. */

pHsz = aHsz;
while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL) {
DdeQueryConvInfo(hconv, QID_SYNC, (PCONVINFO) &ci);
DdeKeepStringHandle(idInst, ci.hszSvcPartner);
*pHsz++ = ci.hszSvcPartner;
}

.
. /* Use the handles; converse with the servers. */
.

/* Free the memory and terminate the conversations. */

LocalFree((HANDLE) aHsz);
DdeDisconnectList(hconvList);

An application can terminate an individual conversation in a conversation list by calling the DdeDisconnect_win32_DdeDisconnect function. An application can terminate all conversations in a conversation list by calling the DdeDisconnectList_win32_DdeDisconnectList function. Both functions cause the DDEML to send XTYP_DISCONNECT_win32_XTYP_DISCONNECT transactions to each partner's DDE callback function. DdeDisconnectList sends an XTYP_DISCONNECT transaction for each conversation handle in the list.
A client can retrieve a list of the conversation handles in a conversation list by passing an existing conversation list handle to DdeConnectList_win32_DdeConnectList. The enumeration process removes the handles of terminated conversations from the list, and nonduplicate conversations that fit the specified service name and topic name are added.
If DdeConnectList specifies an existing conversation list handle, the function creates a new conversation list that contains the handles of any new conversations and the handles from the existing list.
If duplicate conversations exist, DdeConnectList attempts to prevent duplicate conversation handles in the conversation list. A duplicate conversation is a second conversation with the same server on the same service name and topic name. Two such conversations would have different handles, yet they would identify the same conversation.
+$#A Data Management
Because DDE uses memory objects to pass data from one application to another, the DDEML provides a set of functions that DDE applications can use to create and manage DDE objects.
All transactions that involve the exchange of data require the application supplying the data to create a local buffer containing the data and then to call the DdeCreateDataHandle_win32_DdeCreateDataHandle function. This function allocates a DDE object, copies the data from the buffer to the object, and returns a data handle. A data handle is a doubleword value that the DDEML uses to provide access to data in the DDE object. To share the data in a DDE object, an application passes the data handle to the DDEML, and the DDEML passes the handle to the DDE callback function of the application that is receiving the data transaction.
The following example shows how to create a DDE object and obtain a handle of the object. During the XTYP_ADVREQ_win32_XTYP_ADVREQ transaction, the callback function converts the current time to an ASCII string, copies the string to a local buffer, and then creates a DDE object that contains the string. The callback function returns the handle of the DDE object (HDDEDATA) to the DDEML, which passes the handle to the client application.
typedef struct tagTIME
{
INT hour; /* 0 - 11 hours for analog clock */
INT hour12; /* 12-hour format */
INT hour24; /* 24-hour format */
INT minute;
INT second;
INT ampm; /* 0 - AM , 1 - PM */
} TIME;

HDDEDATA EXPENTRY DdeCallback(uType, uFmt, hconv, hsz1, hsz2,
hdata, dwData1, dwData2)
UINT uType;
UINT uFmt;
HCONV hconv;
HSZ hsz1;
HSZ hsz2;
HDDEDATA hdata;
DWORD dwData1;
DWORD dwData2;
{

CHAR szBuf[32];

switch (uType) {

case XTYP_ADVREQ:
if ((hsz1 == hszTime && hsz2 == hszNow) &&
(uFmt == CF_TEXT)) {

/* Copy the formatted string to a buffer. */

itoa(tmTime.hour, szBuf, 10);
lstrcat(szBuf, ":");
if (tmTime.minute < 10)
lstrcat(szBuf, "0");
itoa(tmTime.minute, &szBuf[lstrlen(szBuf)], 10);
lstrcat(szBuf, ":");
if (tmTime.second < 10)
strcat(szBuf, "0");
itoa(tmTime.second, &szBuf[lstrlen(szBuf)], 10);
szBuf[lstrlen(szBuf)] = '\0';

/* Create a global object and return its data handle. */

return (DdeCreateDataHandle(
idInst,
(LPBYTE) szBuf, /* instance identifier */
lstrlen(szBuf) + 1, /* source buffer length */
0, /* offset from beginning */
hszNow, /* item name string */
CF_TEXT, /* clipboard format */
0)); /* no creation flags */
} else
return (HDDEDATA) NULL;

.
. /* Process other transactions. */
.

}

}

The receiving application obtains a pointer to the DDE object by passing the data handle to the DdeAccessData_win32_DdeAccessData function. The pointer returned by DdeAccessData provides read-only access. The application should use the pointer to review the data and then call the DdeUnaccessData_win32_DdeUnaccessData function to invalidate the pointer. The application can copy the data to a local buffer by using the DdeGetData_win32_DdeGetData function.
The following example obtains a pointer to the DDE object identified by the hData parameter, copies the contents to a local buffer, and then invalidates the pointer.
HDDEDATA hdata;
LPBYTE lpszAdviseData;
DWORD cbDataLen;
DWORD i;
char szData[32];

.
.
.
case XTYP_ADVDATA:
lpszAdviseData = DdeAccessData(hdata, &cbDataLen);
for (i = 0; i < cbDataLen; i++)
szData[i] = *lpszAdviseData++;
DdeUnaccessData(hdata);
return (HDDEDATA) TRUE;
.
.
.

Usually, when an application that created a data handle passes that handle to the DDEML, the handle becomes invalid in the creating application. This situation is not a problem if the application must share data with only a single application. If an application must share the same data with multiple applications, however, the creating application should specify the HDATA_APPOWNED flag in DdeCreateDataHandle_win32_DdeCreateDataHandle. Doing so gives ownership of the DDE object to the creating application and prevents the DDEML from invalidating the data handle. The application can then pass the data handle any number of times after calling DdeCreateDataHandle only once.
If an application specifies the HDATA_APPOWNED flag in the afCmd parameter of DdeCreateDataHandle, it must call the DdeFreeDataHandle_win32_DdeFreeDataHandle function to free the memory handle, regardless of whether it passed the handle to the DDEML. Before it terminates, an application must call DdeFreeDataHandle to free any data handle that it created but did not pass to the DDEML.
An application that has not yet passed the handle of a DDE object to the DDEML can add data to the object or overwrite data in the object by using the DdeAddData_win32_DdeAddData function. Typically, an application uses DdeAddData to fill an uninitialized DDE object. After an application passes a data handle to the DDEML, the DDE object identified by the handle cannot be changed; it can only be freed.
+$#A Transaction Management
After establishing a conversation with a server, a client can send transactions to obtain data and services from the server. The remaining topics in this section describe the types of transactions that clients can use to interact with a server.
+$#A Request Transaction
A client application can use the XTYP_REQUEST_win32_XTYP_REQUEST transaction to request a data item from a server application. The client calls the DdeClientTransaction_win32_DdeClientTransaction function, specifying XTYP_REQUEST as the transaction type and specifying the data item the application needs.
The DDEML passes the XTYP_REQUEST transaction to the server, specifying the topic name, item name, and data format requested by the client. If the server supports the requested topic, item, and format, the server should return a data handle that identifies the current value of the item. The DDEML passes this handle to the client as the return value from DdeClientTransaction. The server should return NULL if it does not support the topic, item, or format requested.
DdeClientTransaction uses the lpdwResult parameter to return a transaction-status flag to the client. If the server does not process the XTYP_REQUEST transaction, DdeClientTransaction returns NULL, and lpdwResult points to the DDE_FNOTPROCESSED or DDE_FBUSY flag. If the DDE_FNOTPROCESSED flag is returned, the client cannot determine why the server did not process the transaction.
If a server does not support the XTYP_REQUEST transaction, it should specify the CBF_FAIL_REQUESTS filter flag in the DdeInitialize_win32_DdeInitialize function. This flag prevents the DDEML from sending the transaction to the server.
+$#A Poke Transaction
A client can send unsolicited data to a server by using DdeClientTransaction_win32_DdeClientTransaction to send an XTYP_POKE_win32_XTYP_POKE transaction to a server's callback function.
The client application first creates a buffer that contains the data to send to the server and then passes a pointer to the buffer as a parameter to DdeClientTransaction. Alternatively, the client can use the DdeCreateDataHandle_win32_DdeCreateDataHandle function to obtain a data handle that identifies the data and then pass the handle to DdeClientTransaction. In either case, the client also specifies the topic name, item name, and data format when it calls DdeClientTransaction.
The DDEML passes the XTYP_POKE transaction to the server, specifying the topic name, item name, and data format that the client requested. To accept the data item and format, the server should return DDE_FACK. To reject the data, the server should return DDE_FNOTPROCESSED. If the server is too busy to accept the data, the server should return DDE_FBUSY.
When DdeClientTransaction returns, the client can use the lpdwResult parameter to access the transaction-status flag. If the flag is DDE_FBUSY, the client should send the transaction again later.
If a server does not support the XTYP_POKE_win32_XTYP_POKE transaction, it should specify the CBF_FAIL_POKES filter flag in DdeInitialize_win32_DdeInitialize. This flag prevents the DDEML from sending this transaction to the server.
+$#A Advise Transaction
A client application can use the DDEML to establish one or more links to items in a server application. When such a link has been established, the server sends periodic updates about the linked item to the client (typically, whenever the value of the item associated with the server application changes). Linking establishes an advise loop between the two applications that remains in place until the client ends it.
There are two kinds of advise loops: "hot" and "warm." In a hot advise loop, the server immediately sends a data handle that identifies the changed value. In a warm advise loop, the server notifies the client that the value of the item has changed but does not send the data handle until the client requests it.
A client can request a hot advise loop with a server by specifying the XTYP_ADVSTART_win32_XTYP_ADVSTART transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction. To request a warm advise loop, the client must combine the XTYPF_NODATA flag with the XTYP_ADVSTART transaction type. In either event, the DDEML passes the XTYP_ADVSTART transaction to the server's DDE callback function. The server's DDE callback function should examine the parameters that accompany the XTYP_ADVSTART transaction (including the requested format, topic name, and item name) and then return TRUE to allow the advise loop or FALSE to deny it.
After an advise loop has been established, the server application should call the DdePostAdvise_win32_DdePostAdvise function whenever the value of the item associated with the requested item name changes. This call results in an XTYP_ADVREQ_win32_XTYP_ADVREQ transaction being sent to the server's own DDE callback function. The server's DDE callback function should return a data handle that identifies the new value of the data item. The DDEML then notifies the client that the specified item has changed by sending the XTYP_ADVDATA_win32_XTYP_ADVDATA transaction to the client's DDE callback function.
If the client requested a hot advise loop, the DDEML passes the data handle for the changed item to the client during the XTYP_ADVDATA transaction. Otherwise, the client can send an XTYP_REQUEST_win32_XTYP_REQUEST transaction to obtain the data handle.
It is possible for a server to send updates faster than a client can process the new data. The speed of updates can be a problem for a client that must perform lengthy processing operations on the data. In this case, the client should specify the XTYPF_ACKREQ flag when it requests an advise loop. This flag causes the server to wait for the client to acknowledge that it has received and processed a data item before the server sends the next data item. Advise loops that are established with the XTYPF_ACKREQ flag are more robust with fast servers but may occasionally miss updates. Advise loops established without the XTYPF_ACKREQ flag are guaranteed not to miss updates as long as the client keeps up with the server.
A client can end an advise loop by specifying the XTYP_ADVSTOP_win32_XTYP_ADVSTOP transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction.
If a server does not support advise loops, it should specify the CBF_FAIL_ADVISES filter flag in the DdeInitialize_win32_DdeInitialize function. This flag prevents the DDEML from sending the XTYP_ADVSTART_win32_XTYP_ADVSTART and XTYP_ADVSTOP transactions to the server.
+$#A Execute Transaction
A client can use the XTYP_EXECUTE_win32_XTYP_EXECUTE transaction to cause a server to execute a command or a series of commands.
To execute a server command, the client first creates a buffer that contains a command string for the server to execute and then passes either a pointer to the buffer or a data handle identifying the buffer when it calls DdeClientTransaction_win32_DdeClientTransaction. Other required parameters include the conversation handle, the item name string handle, the format specification, and the XTYP_EXECUTE transaction type. An application that creates a data handle for passing execute data must specify NULL for the hszItem parameter of the DdeCreateDataHandle_win32_DdeCreateDataHandle function and zero for the uFmt parameter.
The DDEML passes the XTYP_EXECUTE transaction to the server's DDE callback function and specifies the format name, conversation handle, topic name, and data handle identifying the command string. If the server supports the command, it should use the DdeAccessData_win32_DdeAccessData function to obtain a pointer to the command string, execute the command, and then return DDE_FACK. If the server does not support the command or cannot complete the transaction, it should return DDE_FNOTPROCESSED. The server should return DDE_FBUSY if it is too busy to complete the transaction.
In general, a server's callback function should process the XTYP_EXECUTE transaction before returning with the following exceptions:
1. When the command passed with the XTYP_EXECUTE_win32_XTYP_EXECUTE transaction requests the server to terminate, the server should not terminate until its callback function returns from processing XTYP_EXECUTE.
2. If the server must perform an operation, such as processing a dialog box or a DDE transaction that might cause DDEML recursion problems, the server should return the CBR_BLOCK return code to block the execute transaction, perform the operation, and then resume processing the execute transaction.

When DdeClientTransaction_win32_DdeClientTransaction returns, the client can use the lpdwResult parameter to access the transaction status flag. If the flag is DDE_FBUSY, the client should send the transaction again later.
If a server does not support the XTYP_EXECUTE_win32_XTYP_EXECUTE transaction, it should specify the CBF_FAIL_EXECUTES filter flag in the DdeInitialize_win32_DdeInitialize function. Doing so prevents the DDEML from sending the transaction to the server.
+$#A Synchronous and Asynchronous Transactions
A client can send either synchronous or asynchronous transactions. In a synchronous transaction, the client specifies a time-out value that indicates the maximum amount of time it will wait for the server to process the transaction. DdeClientTransaction_win32_DdeClientTransaction does not return until the server processes the transaction, the transaction fails, or the time-out value expires. The client specifies the time-out value when it calls DdeClientTransaction.
During a synchronous transaction, the client enters a modal loop while waiting for the transaction to be processed. The client can still process user input but cannot send another synchronous transaction until DdeClientTransaction returns.
A client sends an asynchronous transaction by specifying the TIMEOUT_ASYNC flag in DdeClientTransaction. The function returns after the transaction has begun, passing a transaction identifier to the client. When the server finishes processing the asynchronous transaction, the DDEML sends an XTYP_XACT_COMPLETE_win32_XTYP_XACT_COMPLETE transaction to the client. One of the parameters that the DDEML passes to the client during the XTYP_XACT_COMPLETE transaction is the transaction identifier. By comparing this transaction identifier with the identifier returned by DdeClientTransaction, the client identifies which asynchronous transaction the server has finished processing.
A client can use the DdeSetUserHandle_win32_DdeSetUserHandle function as an aid in processing an asynchronous transaction. This function makes it possible for a client to associate an application-defined doubleword value with a conversation handle and a transaction identifier. The client can use the DdeQueryConvInfo_win32_DdeQueryConvInfo function during the XTYP_XACT_COMPLETE transaction to obtain the application-defined doubleword value. Because of this function, an application need not maintain a list of active transaction identifiers.
When a client successfully completes a request for data using a synchronous transaction, the DDEML has no way to tell when the client has finished using the data received. The client application must pass the data handle received to the DdeFreeDataHandle_win32_DdeFreeDataHandle function, notifying the DDEML that the handle will no longer be used. Data handles returned by synchronous transactions are effectively owned by the client.
If a server does not process an asynchronous transaction in a timely manner, the client can abandon the transaction by calling the DdeAbandonTransaction_win32_DdeAbandonTransaction function. The DDEML releases all resources associated with the transaction and discards the results of the transaction when the server finishes processing it. A time-out during a synchronous transaction effectively cancels the transaction.
The asynchronous transaction method is provided for applications that must send a high volume of DDE transactions while simultaneously performing a substantial amount of processing, such as performing calculations. The asynchronous method is also useful in applications that must stop processing DDE transactions temporarily so they can complete other tasks without interruption. In most other situations, an application should use the synchronous method.
Synchronous transactions are simpler to maintain and are faster than asynchronous transactions. However, only one synchronous transaction can be performed at a time, whereas many asynchronous transactions can be performed simultaneously. With synchronous transactions, a slow server can cause a client to remain idle while it is waiting for a response. Also, synchronous transactions cause the client to enter a modal loop that could bypass message filtering in the application's own message loop.
If the client has installed a hook procedure to filter messages (that is, specified the WH_MSGFILTER hook type in a call to the SetWindowsHookEx_win32_SetWindowsHookEx function), a synchronous transaction will not cause the system to bypass the hook procedure. When an input event occurs while the client is waiting for a synchronous transaction to end, the hook procedure receives an MSGF_DDEMGR hook code. The main danger of using a synchronous transaction modal loop is that a modal loop created by a dialog box can interfere with its operation. Asynchronous transactions should always be used when the DDEML is being used by a dynamic-link library (DLL).
+$#A Transaction Control
An application can suspend transactions to its DDE callback function ¾ either those transactions associated with a specific conversation handle or all transactions regardless of the conversation handle. This capability is useful when an application receives a transaction that requires lengthy processing. In such a case, the application can return the CBR_BLOCK return code to suspend future transactions associated with the transaction's conversation handle, so that the application is free to process other conversations.
When processing has been completed, the application calls the DdeEnableCallback_win32_DdeEnableCallback function to resume transactions associated with the suspended conversation. Calling DdeEnableCallback causes the DDEML to resend the transaction that resulted in the application suspending the conversation. Therefore, the application should store the result of the transaction in such a way that it can obtain and return the result without reprocessing the transaction.
An application can suspend all transactions associated with a specific conversation handle by specifying the handle and the EC_DISABLE flag in a call to DdeEnableCallback. By specifying a NULL handle, an application can suspend all transactions for all conversations.
When a conversation has been suspended, the DDEML saves transactions for the conversation in a transaction queue. When the application reenables the conversation, the DDEML removes the saved transactions from the queue and passes each transaction to the appropriate callback function. The capacity of the transaction queue is large, but an application should reenable a suspended conversation as soon as possible to avoid losing transactions.
An application can resume usual transaction processing by specifying the EC_ENABLEALL flag in DdeEnableCallback. For a more controlled resumption of transaction processing, the application can specify the EC_ENABLEONE flag. This flag removes one transaction from the transaction queue and passes it to the appropriate callback function; after that transaction has been processed, any conversations are again disabled.
If the EC_ENABLEONE flag and a conversation handle are specified in the call to DdeEnableCallback_win32_DdeEnableCallback, only that conversation is blocked after the transaction has been processed. If a NULL conversation handle is specified, all conversations are blocked after a transaction has been processed in any conversation.
+$#A Transaction Classes
The DDEML has four classes of transactions. Each class is identified by a constant that begins with the XCLASS_ prefix. The classes are defined in the DDEML header file. The class value is combined with the transaction-type value and is passed to the DDE callback function of the receiving application.
A transaction's class determines the return value that a callback function is expected to return if it processes the transaction. The following return values and transaction types are associated with each of the four transaction classes.
Class Return value Transaction
XCLASS_BOOL TRUE or FALSE XTYP_ADVSTART_win32_XTYP_ADVSTART XTYP_CONNECT_win32_XTYP_CONNECT
XCLASS_DATA A data handle, the CBR_BLOCK return code, or NULL XTYP_ADVREQ_win32_XTYP_ADVREQ XTYP_REQUEST_win32_XTYP_REQUEST XTYP_WILDCONNECT_win32_XTYP_WILDCONNECT
XCLASS_FLAGS A transaction flag: DDE_FACK, DDE_FBUSY, or DDE_FNOTPROCESSED XTYP_ADVDATA_win32_XTYP_ADVDATA XTYP_EXECUTE_win32_XTYP_EXECUTE XTYP_POKE_win32_XTYP_POKE
XCLASS_NOTIFICATION None XTYP_ADVSTOP_win32_XTYP_ADVSTOP XTYP_CONNECT_win32_XTYP_CONNECT_ CONFIRM XTYP_DISCONNECT_win32_XTYP_DISCONNECT XTYP_ERROR_win32_XTYP_ERROR XTYP_REGISTER_win32_XTYP_REGISTER XTYP_UNREGISTER_win32_XTYP_UNREGISTER XTYP_XACT_ COMPLETE

+$#A Transaction Types
Each DDE transaction type has a receiver and an associated activity that causes the DDEML to generate each type.
Transaction type Receiver Cause
XTYP_ADVDATA_win32_XTYP_ADVDATA Client A server responded to an XTYP_ADVREQ_win32_XTYP_ADVREQ transaction by returning a data handle.
XTYP_ADVREQ Server A server called the DdePostAdvise_win32_DdePostAdvise function, indicating that the value of a data item in an advise loop had changed.
XTYP_ADVSTART_win32_XTYP_ADVSTART Server A client specified the XTYP_ADVSTART transaction type in a call to the DdeClientTransaction_win32_DdeClientTransaction function.
XTYP_ADVSTOP_win32_XTYP_ADVSTOP Server A client specified the XTYP_ADVSTOP transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction.
XTYP_CONNECT_win32_XTYP_CONNECT Server A client called the DdeConnect_win32_DdeConnect function and specified a service name and topic name supported by the server.
XTYP_CONNECT_win32_XTYP_CONNECT_CONFIRM Server The server returned TRUE in response to an XTYP_CONNECT or XTYP_WILDCONNECT_win32_XTYP_WILDCONNECT transaction.
XTYP_DISCONNECT_win32_XTYP_DISCONNECT Client/Server A partner in a conversation called the DdeDisconnect_win32_DdeDisconnect function, causing both partners to receive this transaction.
XTYP_ERROR_win32_XTYP_ERROR Client/Server A critical error has occurred. The DDEML may not have sufficient resources to continue.
XTYP_EXECUTE_win32_XTYP_EXECUTE Server A client specified the XTYP_EXECUTE transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction.
XTYP_MONITOR_win32_XTYP_MONITOR DDE monitoring application A DDE event occurred in the system. For more information about DDE monitoring applications, see Monitoring Applications_win32_Monitoring_Applications.
XTYP_POKE_win32_XTYP_POKE Server A client specified the XTYP_POKE transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction.
XTYP_REGISTER_win32_XTYP_REGISTER Client/Server A server application used the DdeNameService_win32_DdeNameService function to register a service name.
XTYP_REQUEST_win32_XTYP_REQUEST Server A client specified the XTYP_REQUEST transaction type in a call to DdeClientTransaction_win32_DdeClientTransaction.
XTYP_UNREGISTER_win32_XTYP_UNREGISTER Client/Server A server application used DdeNameService_win32_DdeNameService to unregister a service name.
XTYP_WILDCONNECT_win32_XTYP_WILDCONNECT Server A client called the DdeConnect_win32_DdeConnect or DdeConnectList_win32_DdeConnectList function, specifying NULL for the service name, the topic name, or both.
XTYP_XACT_COMPLETE_win32_XTYP_XACT_COMPLETE Client An asynchronous transaction, sent when the client specified the TIMEOUT_ASYNC flag in a call to DdeClientTransaction_win32_DdeClientTransaction, has concluded.

+$#A Error Detection for DDEML Functions
Whenever a DDEML function fails, an application can call the DdeGetLastError_win32_DdeGetLastError function to determine the cause of the failure. DdeGetLastError returns an error value that specifies the cause of the most recent error.
+$#A Monitoring Applications
The application programming interface (API) elements of the DDEML can be used to create an application that monitors DDE activity in the system. Like any DDEML application, a DDE monitoring application contains a DDE callback function. The DDEML notifies a monitoring application's DDE callback function whenever a DDE event occurs, passing information about the event to the callback function. The application typically displays the information in a window or writes it to a file.
To receive notifications from the DDEML, an application must have registered as a DDE monitor by specifying the APPCLASS_MONITOR flag in a call to the DdeInitialize_win32_DdeInitialize function. In this same call, the application can specify one or more monitor flags to indicate the types of events for which the DDEML is to notify the application's callback function. The following monitor flags can be specified by an application:
Flag Description
MF_CALLBACKS Notifies the callback function whenever a transaction is sent to any DDE callback function in the system.
MF_CONV Notifies the callback function whenever a conversation is established or terminated.
MF_ERRORS Notifies the callback function whenever a DDEML error occurs.
MF_HSZ_INFO Notifies the callback function whenever a DDEML application creates, frees, or increments the usage count of a string handle or whenever a string handle is freed as a result of a call to the DdeUninitialize_win32_DdeUninitialize function.
MF_LINKS Notifies the callback function whenever an advise loop is started or ended.
MF_POSTMSGS Notifies the callback function whenever the system or an application posts a DDE message.
MF_SENDMSGS Notifies the callback function whenever the system or an application sends a DDE message.

The following example shows how to register a DDE monitoring application so that its DDE callback function receives notifications of all DDE events.
DWORD idInst;
PFNCALLBACK lpDdeProc;
hInst = hInstance;

if (DdeInitialize(
(LPDWORD) &idInst, /* instance identifier */
DDECallback, /* points to callback function */
APPCLASS_MONITOR | /* this is a monitoring application */
MF_CALLBACKS | /* monitor callback functions */
MF_CONV | /* monitor conversation data */
MF_ERRORS | /* monitor DDEML errors */
MF_HSZ_INFO | /* monitor data handle activity */
MF_LINKS | /* monitor advise loops */
MF_POSTMSGS | /* monitor posted DDE messages */
MF_SENDMSGS, /* monitor sent DDE messages */
0)) /* reserved */
return FALSE;

The DDEML informs a monitoring application of a DDE event by sending an XTYP_MONITOR_win32_XTYP_MONITOR transaction to the application's DDE callback function. During this transaction, the DDEML passes a monitor flag that specifies the type of DDE event that has occurred and a handle of a DDE object that contains detailed information about the event. The DDEML provides a set of structures that the application can use to extract the information from the DDE object. There is a corresponding structure for each type of DDE event.
Structure Description
MONCBSTRUCT_win32_MONCBSTRUCT_str Contains information about a transaction.
MONCONVSTRUCT_win32_MONCONVSTRUCT_str Contains information about a conversation.
MONERRSTRUCT_win32_MONERRSTRUCT_str Contains information about the latest DDE error.
MONLINKSTRUCT_win32_MONLINKSTRUCT_str Contains information about an advise loop.
MONHSZSTRUCT_win32_MONHSZSTRUCT_str Contains information about a string handle.
MONMSGSTRUCT_win32_MONMSGSTRUCT_str Contains information about a DDE message that was sent or posted.

The following example shows the DDE callback function of a DDE monitoring application that formats information about each string handle event and then displays the information in a window. The function uses the MONHSZSTRUCT_win32_MONHSZSTRUCT_str structure to extract the information from the DDE object.
HDDEDATA CALLBACK DDECallback(uType, uFmt, hconv, hsz1, hsz2,
hdata, dwData1, dwData2)
UINT uType;
UINT uFmt;
HCONV hconv;
HSZ hsz1;
HSZ hsz2;
HDDEDATA hdata;
DWORD dwData1;
DWORD dwData2;
{
LPVOID lpData;
CHAR *szAction;
CHAR szBuf[256];
DWORD cb;

switch (uType) {
case XTYP_MONITOR:

/* Obtain a pointer to the global memory object. */

if (lpData = DdeAccessData(hdata, &cb)) {

/* Examine the monitor flag. */

switch (dwData2) {
case MF_HSZ_INFO:

#define PHSZS ((MONHSZSTRUCT FAR *)lpData)

/*
* The global memory object contains
* string handle data. Use the MONHSZSTRUCT
* structure to access the data.
*/

switch (PHSZS->fsAction) {

/*
* Examine the action flags to
* determine the action performed on
* the handle.
*/

case MH_CREATE:
szAction = "Created";
break;

case MH_KEEP:
szAction = "Incremented";
break;

case MH_DELETE:
szAction = "Deleted";
break;

case MH_CLEANUP:
szAction = "Cleaned up";
break;

default:
DdeUnaccessData(hdata);
return (HDDEDATA) 0;
}

/* Write formatted output to a buffer. */

wsprintf(szBuf,
"Handle %s, Task: %x, Hsz: %lx(%s)",
(LPSTR) szAction, PHSZS->hTask,
PHSZS->hsz, (LPSTR) PHSZS->str);
.
. /* Display text or write to a file. */
.

break;

#undef PHSZS

.
. /* Process other MF_* flags. */
.

default:
break;
}
}

/* Free the global memory object. */

DdeUnaccessData(hdata);
break;

default:
break;
}
return (HDDEDATA) 0;
}

+$#A Dynamic Data Exchange Management Functions
Following are the functions used to manage dynamic data exchange.
DdeAbandonTransaction_win32_DdeAbandonTransactionDdeAccessData_win32_DdeAccessDataDdeAddData_win32_DdeAddDataDdeCallback_win32_DdeCallbackDdeClientTransaction_win32_DdeClientTransactionDdeCmpStringHandles_win32_DdeCmpStringHandlesDdeConnect_win32_DdeConnectDdeConnectList_win32_DdeConnectListDdeCreateDataHandle_win32_DdeCreateDataHandleDdeCreateStringHandle_win32_DdeCreateStringHandleDdeDisconnect_win32_DdeDisconnectDdeDisconnectList_win32_DdeDisconnectListDdeEnableCallback_win32_DdeEnableCallbackDdeFreeDataHandle_win32_DdeFreeDataHandleDdeFreeStringHandle_win32_DdeFreeStringHandleDdeGetData_win32_DdeGetDataDdeGetLastError_win32_DdeGetLastErrorDdeInitialize_win32_DdeInitializeDdeKeepStringHandle_win32_DdeKeepStringHandleDdeNameService_win32_DdeNameServiceDdePostAdvise_win32_DdePostAdviseDdeQueryConvInfo_win32_DdeQueryConvInfoDdeQueryNextServer_win32_DdeQueryNextServerDdeQueryString_win32_DdeQueryStringDdeReconnect_win32_DdeReconnectDdeSetUserHandle_win32_DdeSetUserHandleDdeUnaccessData_win32_DdeUnaccessDataDdeUninitialize_win32_DdeUninitialize

+$#AK Dynamic-Link Libraries
In Microsoft® Windows®, dynamic linking provides a way for a process to call a function that is not part of its executable code. The executable code for the function is located in a dynamic-link library (DLL), containing one or more functions that are compiled, linked, and stored separately from the processes using them. The Microsoft® Win32® application programming interface (API) is implemented as a set of dynamic-link libraries, so any process using the Win32 API uses dynamic linking.
+$#A About Dynamic-Link Libraries
Dynamic linking allows an executable module (either a .DLL or .EXE file) to include only the information needed at run time to locate the executable code for a DLL function. This type of linking differs from the more familiar static linking, which requires a copy of a library function's executable code in the executable module of each application using it.
There are two methods for calling a function in a DLL:
· Load-time dynamic linking occurs when an application's code makes an explicit call to a DLL function. This type of linking requires that the executable module of the application be built by linking with the DLL's import library, which supplies the information needed to locate the DLL function when the application starts. For more information, see Load-Time Dynamic Linking_win32_Load_Time_Dynamic_Linking.
· Run-time dynamic linking occurs when a program uses the LoadLibrary_win32_LoadLibrary and GetProcAddress_win32_GetProcAddress functions to get the starting address of a DLL function. This type of linking eliminates the need to link with an import library. For more information, see Using Run-Time Dynamic Linking_win32_Using_Run_Time_Dynamic_Linking.

A DLL attaches to a process when the process starts or when one of its threads calls the LoadLibrary function. When a DLL attaches to a process, the operating system maps the DLL module into the address space of the process, making the DLL's executable code available to the process. When a DLL detaches from a process at process termination or as a result of a call to the FreeLibrary_win32_FreeLibrary function, the DLL module is unmapped from the address space of the process.
Like any other function, a DLL function is executed in the context of the thread (and the thread's process) that calls it. Therefore, the following conditions apply:
· The threads of the process that called the DLL can use handles opened by a DLL function. Similarly, handles opened by any thread of the calling process can be used in the DLL function.
· The DLL uses the user stack of the calling thread and the address space of the calling process.
· Memory allocated by the DLL is in the address space of the calling process.
· The threads of the process that called the DLL can read from or write to the globally declared variables in the DLL.

+$#A Advantages of Dynamic Linking
Dynamic linking has the following advantages:
· Many processes can use a single DLL simultaneously, sharing a single copy of the DLL in memory. Doing this saves memory space and reduces swapping.
· When the functions in a DLL change, the applications that use them do not need to be recompiled or relinked as long as the functions' arguments and return values do not change. In contrast, statically linked object code requires that the application be relinked when the functions change.
· A DLL can provide after-market support. For example, a display driver DLL can be modified to support a display that was not available when the application was shipped.
· Programs written in different programming languages can call the same DLL function as long as the programs follow the function's calling convention. The programs and the DLL function must be compatible in the following ways: the order the function expects its arguments to be pushed onto the stack, whether the function or the application is responsible for cleaning up the stack, and whether any arguments are passed in registers.

A potential disadvantage to using DLLs is that the application is not self-contained; it depends on the existence of a separate DLL module. A process using load-time dynamic linking is terminated by the operating system if the DLL is not found at process startup. A process using run-time dynamic linking is not terminated in this situation, but capabilities provided by the DLL are not available to the program.
+$#A Dynamic-Link Library Creation
To create a DLL, first create one or more source code (.C) files and a module-definition (.DEF) file. The .C file contains the exported functions for the DLL. The .DEF file lists these functions and defines other attributes of the DLL.
The compiler uses the .C file to create an object code (.OBJ) file. The library manager creates an export (.EXP) file and an import library (.LIB) file from the .DEF file.
Finally, the linker uses the .OBJ, .EXP, and .LIB files to build the executable files for the DLL. The .OBJ and .EXP files provide information the linker needs to build the .DLL file. The .LIB file contains information the linker needs to resolve external references to DLL functions, so that a program can locate the specified DLL at run time. The .LIB file enables the linker to build an executable module (.EXE or .DLL) that calls a function in the DLL. For more information about .DEF files, see Module-Definition File_win32_Module_Definition_File.
Note that the information in this section is based on using Win32 software development tools. For more detailed information about creating DLLs, see the documentation included with your development tools.
+$#A Dynamic-Link Library Entry-Point Function
A DllEntryPoint_win32_DllEntryPoint function is an optional DLL function that the operating system calls to perform user-defined operations. DllEntryPoint is not a typical function; it is a reference to a function specified in the linker command line. A DLL is not required to have an entry-point function, but it can be useful for initializing and cleaning up processes and threads. The name of a DLL's entry-point function is specified by the -entry switch of the linker. For example, the following command line specifies LibMain as the entry-point function of MYDLL.DLL.
link -entry:LibMain -out:mydll.dll mydll.exp mydll.obj $(mylibs)

If an entry-point function is specified, the operating system causes it to be called whenever any of the following events occurs:
· The DLL attaches to a process. For processes using load-time dynamic linking, attachment occurs during initialization. For processes using run-time linking, the DLL attaches and the entry-point function is called before LoadLibrary_win32_LoadLibrary returns.
· The DLL detaches from a process. Detachment occurs when the process terminates or when the DLL is unloaded by a call to the FreeLibrary_win32_FreeLibrary function. If the process terminates as a result of the TerminateProcess_win32_TerminateProcess or TerminateThread_win32_TerminateThread function, the system does not call the entry-point function.
· The DLL attaches to a thread. Attachment occurs whenever a new thread is created in a process to which a DLL is attached.
· The DLL detaches from a thread. Detachment occurs whenever a thread of an attached process terminates, except as a result of the TerminateThread or TerminateProcess function. When a DLL detaches from a process, the entry-point function is called only once for the entire process rather than once for each existing thread of the process.

The system causes the entry-point function to be called, but it is called in the context of a process or thread attaching to or detaching from the DLL. This allows a DLL to use its entry-point function for allocating memory in the address space of the process or to open handles accessible to the process. When it is called to attach a new thread, the entry-point function can allocate memory for thread local storage (TLS) that is private to the new thread. For more information about thread local storage, see Processes and Threads_win32_Processes_and_Threads.
When a DLL's entry-point function is called for process attachment, it returns TRUE to indicate success. For processes using load-time linking, a return value of FALSE causes the process initialization to fail and the process terminates. For processes using run-time linking, a return value of FALSE causes the LoadLibrary_win32_LoadLibrary function to return NULL, indicating failure. The return value of the entry-point function is disregarded when it is called for any other reasons, such as process detaching, thread attaching, or thread detaching.
+$#A Module-Definition File
A module-definition (.DEF) file is a text file containing one or more module statements that describe various attributes of a DLL. The descriptions and examples in this section are based on the keywords and syntax used by the Win32 software development tools. For complete information about module-definition files, see the documentation included with your development tools.
A DLL requires a .DEF file to create an import library (.LIB) file and an export (.EXP) file. The linker uses the import library to build any executable module that uses the DLL and uses the export file to build the .DLL file.
A DLL's module-definition file must include a LIBRARY statement and an EXPORTS statement. The LIBRARY statement identifies the module-definition file as belonging to a DLL, and it must be the first statement in the file. The name specified in the LIBRARY statement identifies the library in the DLL's import library. The EXPORTS statement lists the names and, optionally, the ordinal values of the functions exported by the DLL. When ordinal values are specified, they must be in the range 1 through N, where N is the number of functions exported by the DLL.
The following .DEF file for a DLL exports three functions and associates an ordinal value with each function.
LIBRARY mydll
EXPORTS
Func_A @1
Func_B @2
Func_C @3

The .DEF file can contain additional statements, specifying other attributes of a DLL. For example, the SECTIONS statements can change the default scope of global and static variables. The following SECTIONS statement makes the DLL's initialized data shared, but its uninitialized variables are not shared.
SECTIONS
.data READ WRITE SHARED ; share initialized variables

+$#A Load-Time Dynamic Linking
Load-time dynamic linking occurs when an application's code explicitly calls a DLL function. When the source code is compiled or assembled, the DLL function call generates an external function reference in the object code. To resolve this external reference, the application must be linked with the import library for the DLL.
Finding an external function in an import library informs the linker that the code for that function is in a DLL. To resolve external references to DLLs, the linker simply adds information to the executable file that tells the system where to find the DLL code when the process starts up.
When the system starts a program that contains dynamically linked references, it uses the information in the program's executable file to locate the required DLLs. The system first searches the set of pre-installed DLLs, such as the performance library (KERNEL32.DLL) and the security library (USER32.DLL). The system then searches for the DLLs in the following sequence:
1. The directory where the executable module for the current process is located.
2. The current directory.
3. The windows system directory. The GetSystemDirectory_win32_GetSystemDirectory function retrieves the path of this directory.
4. The windows directory. The GetWindowsDirectory_win32_GetWindowsDirectory function retrieves the path of this directory.
5. The directories listed in the PATH environment variable.

Note that the LIBPATH environment variable is not used.
If it cannot locate a specified DLL, the system terminates the process and displays a dialog box that reports the error. Otherwise, the system maps the DLL modules into the process's address space.
If any of the DLLs has a DllEntryPoint_win32_DllEntryPoint function, the operating system calls the function. DllEntryPoint specifies a code that indicates the DLL is attaching to the process. If the entry-point function does not return TRUE, the system terminates the process and reports the error. For more information about the entry-point function, see Dynamic-Link Library Entry-Point Function_win32_Dynamic_Link_Library_Entry_Point_Function.
Finally, the system modifies the executable code of the process to provide starting addresses for the DLL functions.
Like the rest of a program's code, DLL code is mapped into the address space of the process when the process starts up and is loaded into memory only when needed. As a result, the PRELOAD and LOADONCALL code attributes used by .DEF files to control loading in previous versions of Windows no longer have meaning.
+$#A Run-Time Dynamic Linking
Run-time dynamic linking occurs when the process calls the LoadLibrary_win32_LoadLibrary function to specify the name of a DLL and the GetProcAddress_win32_GetProcAddress function to get the starting address of any function in the DLL. Run-time dynamic linking eliminates the need to link the process with an import library. Since the process does not explicitly call the DLL's functions, it does not generate external references to them.
If the call to LoadLibrary specifies a DLL module already mapped into the address space of the calling process, the function simply returns a handle of the DLL and increments the module's reference count. Otherwise, LoadLibrary attempts to locate the DLL using the same search sequence used for load-time dynamic linking (see Load-Time Dynamic Linking_win32_Load_Time_Dynamic_Linking). If the search succeeds, the system maps the DLL module into the address space of the process. If the DLL has a DllEntryPoint_win32_DllEntryPoint function, the operating system calls the function in the context of the thread that called LoadLibrary. The entry-point function is not called if the DLL is already attached to the process due to a previous call to LoadLibrary with no corresponding call to the FreeLibrary_win32_FreeLibrary function.
If the system cannot find the DLL or if the entry-point function returns FALSE, LoadLibrary returns NULL. If LoadLibrary succeeds, it returns a handle of the DLL module. The process can use this handle to identify the DLL in a call to the GetProcAddress or FreeLibrary function.
The GetModuleHandle_win32_GetModuleHandle function also returns a handle used in GetProcAddress or FreeLibrary. GetModuleHandle succeeds only if the DLL module is already mapped into the address space of the process by load-time linking or by a previous call to LoadLibrary. Unlike LoadLibrary, GetModuleHandle does not increment the module's reference count. The GetModuleFileName_win32_GetModuleFileName function retrieves the full path of the module associated with a handle returned by GetModuleHandle or LoadLibrary.
The process can use GetProcAddress_win32_GetProcAddress to get the starting handle returned by LoadLibrary_win32_LoadLibrary or the address of a function in the DLL using a DLL module handle returned by either LoadLibrary or GetModuleHandle. The function is specified by using its name or ordinal value as listed in the EXPORTS statement of the DLL's .DEF file.
When the DLL module is no longer needed, the process can call the FreeLibrary_win32_FreeLibrary function. This function decrements the module's reference count and unmaps it from the address space of the process if the reference count is zero.
Run-time dynamic linking enables the process to continue executing even if a DLL is not available. The process can then use an alternate method to accomplish its objective. For example, if a process is unable to locate one DLL, it can try to use another, or it can notify the user of an error. If the user can provide the full path of the missing DLL, the process can use this information to load the library even though it is not in the normal search path. This situation contrasts with load-time linking in which the operating system simply terminates the process if it cannot find the DLL.
It is not necessary for the developer to know the names of either the DLLs or the functions within a DLL. The process can get this information from a configuration file or from the user.
Run-time dynamic linking can cause problems if the DLL is using the DllEntryPoint_win32_DllEntryPoint function to perform initialization for each thread of a process, because threads existing when LoadLibrary_win32_LoadLibrary is called will not be initialized. For an example showing how to deal with this problem, see Using Thread Local Storage in a Dynamic-Link Library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library.
+$#A Dynamic-Link Library Data
Windows supports the use of the following DLL data:
· Global and static variables in a DLL module private to each process that uses the DLL.
· Global and static variables shared by all processes simultaneously using a DLL.
· Dynamically allocated memory private to each process that uses a DLL.
· Dynamically allocated memory shared by multiple processes.
· Static storage private to each thread of a multithreaded process.

The default scope of a DLL's variables is the same as that of any statically linked code. Global variables in a DLL source code file are global to each process using the DLL. Static variables create permanent storage for each process, limiting their scope to the block in which they are declared. As a result, each process has its own instance of a DLL's global and static variables by default.
Win32 development tools enable a DLL's .DEF file to override the default scope of global and static variables. For more information, see Module-Definition File_win32_Module_Definition_File and the documentation included with your development tools.
When a DLL allocates memory using any of the allocation functions (GlobalAlloc_win32_GlobalAlloc, LocalAlloc_win32_LocalAlloc, HeapAlloc_win32_HeapAlloc, VirtualAlloc_win32_VirtualAlloc, malloc, and other C run-time allocation functions), the memory is allocated in the address space of the calling process and is accessible only to the threads of that process. A DLL's .DEF file cannot be used to change the scope of dynamically allocated memory.
A DLL can use file mapping to allocate memory that can be shared among processes. For a general discussion of using file mapping to create named shared memory, see File Mapping_win32_File_Mapping. For an example that uses the DllEntryPoint_win32_DllEntryPoint function to set up shared memory using file mapping, see Using Shared Memory in a Dynamic-Link Library_win32_Using_Shared_Memory_in_a_Dynamic_Link_Library.
The thread local storage functions enable a DLL to allocate an index for storing and retrieving a different value for each thread of a multithreaded process. For example, a spreadsheet application can create a new instance of the same thread each time the user opens a new spreadsheet. A DLL providing the functions for various spreadsheet operations can use thread local storage to save information about the current state of each spreadsheet (row, column, and so on). For a general discussion of thread local storage, see Processes and Threads_win32_Processes_and_Threads. For an example that uses the DllEntryPoint function to set up thread local storage, see Using Thread Local Storage in a Dynamic-Link Library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library.
+$#A Using Dynamic-Link Libraries
· Creating a simple dynamic-link library_win32_Creating_a_Simple_Dynamic_Link_Library
· Using load-time dynamic linking_win32_Using_Load_Time_Dynamic_Linking
· Using run-time dynamic linking_win32_Using_Run_Time_Dynamic_Linking
· Using shared memory in a dynamic-link library_win32_Using_Shared_Memory_in_a_Dynamic_Link_Library
· Using thread local storage in a dynamic-link library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library

+$#A Creating a Simple Dynamic-Link Library
DLLs make common functions available to a number of processes or threads at once. The following file, MYPUTS.C, contains a simple string printing function. This DLL does not use a DllEntryPoint_win32_DllEntryPoint function because it has no initialization or cleanup functions to perform.
#include <windows.h>

/*
* A function that writes a null-terminated string to the
* console output.
*/

VOID myPuts(LPTSTR lpszMsg) {
DWORD cchWritten;
HANDLE hStdout;

/* Get a handle to the console output. */

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Write a null-terminated string to the console output. */

while (*lpszMsg)
WriteFile(hStdout, lpszMsg++, 1, &cchWritten, NULL);
}

The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.
LIBRARY myputs

EXPORTS
myPuts

The name specified in the LIBRARY statement identifies the DLL in the DLL's import library. Load-time dynamic linking enables the system to use this name for locating the DLL. If run-time dynamic linking is being used, the LoadLibrary_win32_LoadLibrary function does not use this name to locate the DLL. The EXPORTS statement indicates that other .EXE or .DLL modules can call the myPuts function.
The source code in MYPUTS.C is compiled to produce an object module (MYPUTS.OBJ). The library manager uses the module-definition file (MYPUTS.DEF) to create the import library (MYPUTS.LIB) and the export file (MYPUTS.EXP). The linker uses MYPUTS.EXP and MYPUTS.OBJ to produce the executable DLL module (MYPUTS.DLL). MYPUTS.LIB is not used in creating MYPUTS.DLL, but the linker uses it to create any executable module (.EXE or .DLL) that calls the myPuts function.
Following are some typical command lines used to compile and link a DLL. For more information about compiling and linking dynamic-link libraries and programs that use them, see the documentation included with your development tools.
cvars=-DWIN32 -D_MT -DSTRICT

# Compile myputs.c to create myputs.obj.
myputs.obj: myputs.c
$(cc) $(cflags) $(cvars) myputs.c

# Create the DLL's import library.
# Also create the myputs.exp export file.
myputs.lib: myputs.def
$(lib) -machine:i386 \
-def:myputs.def \
-out:myputs.lib

# Link myputs.obj and myputs.exp to create myputs.dll.
# myputs.lib is not used here but is included as a
# dependency to force creation of myputs.exp.
myputs.dll: myputs.obj myputs.lib
$(link) \
-out:myputs.dll \
myputs.exp myputs.obj $(conlibs)

# If the DLL has a DllEntryPoint function, substitute a line
# like this to specify the name of the entry-point function.
# $(link) -entry:LibMain \

+$#A Using Load-Time Dynamic Linking
Once you have created a DLL, you can use it in an application. The following file, LOADTIME.C, is the source code for a simple console process that uses the myPuts function from MYPUTS.DLL.
/*
* File: loadtime.c
* A simple program that uses myPuts() from myputs.dll
*/

#include <windows.h>

VOID myPuts(LPTSTR); /* a function from a DLL */

VOID main(VOID) {

myPuts("message printed using the DLL function\n");

}

Because LOADTIME.C calls the DLL function explicitly, the executable module for the application must be linked with the import library (MYPUTS.LIB). Following are some typical command lines used for compiling and linking a program that uses a DLL.
# Compile loadtime.c to create loadtime.obj.
loadtime.obj: loadtime.c
$(cc) $(cflags) $(cvars) loadtime.c

# Link loadtime.obj and myputs.lib to create loadtime.exe.
loadtime.exe: loadtime.obj myputs.lib
$(link) $(conflags) \
-out:loadtime.exe loadtime.obj myputs.lib $(conlibs)

+$#A Using Run-Time Dynamic Linking
You can use the same DLL in both load-time and run-time dynamic linking. If the system can find the specified DLL module, the following source code produces the same output as the load-time example in the previous section. The program uses the LoadLibrary_win32_LoadLibrary function to get a handle of MYPUTS.DLL. If LoadLibrary succeeds, the program uses the returned handle in the GetProcAddress_win32_GetProcAddress function to get the starting address of the DLL's myPuts function. After calling the DLL function, the program calls the FreeLibrary_win32_FreeLibrary function to detach the DLL from the process.
The following example illustrates an important difference between run-time and load-time dynamic linking. If the MYPUTS.DLL file is not available, the application using load-time dynamic linking simply terminates. The run-time dynamic linking example, however, can respond to the error.
/*
* File: runtime.c
* A simple program that uses LoadLibrary and
* GetProcAddress to access myPuts() from the myputs DLL.
*/

#include <stdio.h>
#include <windows.h>

typedef VOID (*MYPROC)(LPTSTR);

VOID main(VOID) {
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;

/* Get a handle to the DLL module. */

hinstLib = LoadLibrary("myputs");

/* If the handle is valid, try to get the function address. */

if (hinstLib != NULL) {
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts");

/* If the function address is valid, call the function. */

if (fRunTimeLinkSuccess = (ProcAdd != NULL))
(ProcAdd) ("message via DLL function\n");

/* Free the DLL module. */

fFreeResult = FreeLibrary(hinstLib);

}

/* If unable to call the DLL function, use an alternative. */

if (! fRunTimeLinkSuccess)
printf("message via alternative method\n");

}

Because the program uses run-time dynamic linking, it is not necessary to link with the DLL's import library when creating the program's executable module. The following lines are typical of the command lines used to compile and link a program using run-time linking.
# Compile runtime.c to create runtime.obj.
runtime.obj: runtime.c
$(cc) $(cflags) $(cvars) runtime.c

# Link runtime.obj to create loadtime.exe.
runtime.exe: runtime.obj
$(link) $(conflags) \
-out:runtime.exe runtime.obj $(conlibs)

+$#A Using Shared Memory in a Dynamic-Link Library
This section shows the use of a DLL's entry-point function to set up memory that can be shared by multiple processes. In this DLL, the name of the entry-point function is LibMain. The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.
LIBRARY myputs

EXPORTS
myPuts

The example uses file mapping to map a block of named shared memory into the address space of each process to which the DLL is attached. The entry-point function calls the CreateFileMapping_win32_CreateFileMapping function to create a file-mapping object whenever the DLL attaches to a new process. This function can be used to either create or open a named file-mapping object. The first process to which the DLL attaches creates the file-mapping object. Subsequent processes open a handle of the existing object. The GetLastError_win32_GetLastError function triggers initialization of the shared memory because it returns NO_ERROR to the first process and ERROR_ALREADY_EXISTS to subsequent processes.
The MapViewOfFile_win32_MapViewOfFile function maps the file-mapping object into the address space of each process, returning a pointer to the memory. Note that the shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of lpvMem, which is declared as a global variable so that it is available to the DLL's functions. The example assumes that the DLL's global data is not shared, so each attaching process has its own instance of lpvMem. By default, DLL data is not shared, but the default can be modified by the DLL's module-definition (.DEF) file (see Module-Definition File_win32_Module_Definition_File).
Any shared DLL memory persists only as long as the DLL is attached to a process. In this example, the shared memory is released when the last handle of the file-mapping object is closed. To create persistent shared memory, a DLL can create a detached process (see Processes and Threads_win32_Processes_and_Threads) when the DLL attaches to the first process. If this detached process uses the DLL and does not terminate, it has a handle of the file-mapping object that prevents the shared memory from being released.
/*
* File: dllshmem.c
*
* A DLL entry-point function sets up shared memory using
* named file mapping.
*/

#include <windows.h>
#include <memory.h>

#define SHMEMSIZE 4096

static LPVOID lpvMem = NULL; /* address of shared memory */

/*
* LibMain() is the DllEntryPoint function for this DLL.
*/

BOOL LibMain(HINSTANCE hinstDLL, /* DLL module handle */
DWORD fdwReason, /* reason called */
LPVOID lpvReserved) { /* reserved */

HANDLE hMapObject = NULL; /* handle to file mapping */
BOOL fInit, fIgnore;

switch (fdwReason) {

/*
* The DLL is attaching to a process due to process
* initialization or a call to LoadLibrary.
*/

case DLL_PROCESS_ATTACH:

/* Create a named file mapping object */

hMapObject = CreateFileMapping(
(HANDLE) 0xFFFFFFFF, /* use paging file */
NULL, /* no security attr. */
PAGE_READWRITE, /* read/write access */
0, /* size: high 32-bits */
SHMEMSIZE, /* size: low 32-bits */
"dllmemfilemap"); /* name of map object */
if (hMapObject == NULL)
return FALSE;

/* The first process to attach initializes memory. */

fInit = (GetLastError() != ERROR_ALREADY_EXISTS);

/* Get a pointer to the file-mapped shared memory. */

lpvMem = MapViewOfFile(
hMapObject, /* object to map view of */
FILE_MAP_WRITE, /* read/write access */
0, /* high offset: map from */
0, /* low offset: beginning */
0); /* default: map entire file */
if (lpvMem == NULL)
return FALSE;

/* Initialize memory if this is the first process. */

if (fInit)
memset(lpvMem, '\0', SHMEMSIZE);

break;

/* The attached process creates a new thread. */

case DLL_THREAD_ATTACH:
break;

/* The thread of the attached process terminates. */

case DLL_THREAD_DETACH:
break;

/*
* The DLL is detaching from a process due to
* process termination or a call to FreeLibrary.
*/

case DLL_PROCESS_DETACH:

/* Unmap shared memory from the process's address space. */

fIgnore = UnmapViewOfFile(lpvMem);

/* Close the process's handle to the file-mapping object. */

fIgnore = CloseHandle(hMapObject);

break;

default:
break;

}

return TRUE;
UNREFERENCED_PARAMETER(hinstDLL);
UNREFERENCED_PARAMETER(lpvReserved);

}

/*
* SetSharedMem sets the contents of shared memory.
*/

VOID SetSharedMem(LPTSTR lpszBuf) {
LPTSTR lpszTmp;

/* Get the address of the shared memory block. */

lpszTmp = (LPTSTR) lpvMem;

/* Copy the null-terminated string into shared memory. */

while (*lpszBuf)
*lpszTmp++ = *lpszBuf++;
*lpszTmp = '\0';
}

/*
* GetSharedMem gets the contents of shared memory.
*/

VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize) {
LPTSTR lpszTmp;

/* Get the address of the shared memory block. */

lpszTmp = (LPTSTR) lpvMem;

/* Copy from shared memory into the caller's buffer. */

while (*lpszTmp && --cchSize)
*lpszBuf++ = *lpszTmp++;
*lpszBuf = '\0';
}

+$#A Using Thread Local Storage in a Dynamic-Link Library
This section shows the use of a DLL's entry-point function to set up a thread local storage (TLS) index to provide private storage for each thread of a multithreaded process.
The entry-point function uses the TlsAlloc_win32_TlsAlloc function to allocate a TLS index whenever the DLL attaches to a process. Each thread of the process can then use this index to store a pointer to its own block of memory.
When the entry-point function is called with the DLL_PROCESS_ATTACH value, it peforms the following actions:
1. Uses the TlsAlloc function to allocate a TLS index.
2. Allocates a block of memory to be used exclusively by the initial thread of the process.
3. Uses the TLS index in a call to the TlsSetValue_win32_TlsSetValue function to store a pointer to the allocated memory.

Each time the process creates a new thread, the entry-point function is called with the DLL_THREAD_ATTACH value. The entry-point function then allocates a block of memory for the new thread and stores a pointer to it by using the TLS index. Each thread can use the TLS index in a call to TlsGetValue_win32_TlsGetValue to retrieve the pointer to its own block of memory.
When a thread terminates, the entry-point function is called with the DLL_THREAD_DETACH value and the memory for that thread is freed. When a process terminates, the entry-point function is called with the DLL_PROCESS_DETACH value and the TLS index is freed.
The TLS index is stored in a global variable, making it available to all of the DLL's functions. The following example assumes that the DLL's global data is not shared, because the TLS index is not necessarily the same for each process to which the DLL is attached.
static DWORD dwTlsIndex; /* address of shared memory */

/*
* LibMain() is the DllEntryPoint function for this DLL.
*/

BOOL LibMain(HINSTANCE hinstDLL, /* DLL module handle */
DWORD fdwReason, /* reason called */
LPVOID lpvReserved) {/* reserved */

LPVOID lpvData;
BOOL fIgnore;

switch (fdwReason) {

/*
* The DLL is attaching to a process due to process
* initialization or a call to LoadLibrary.
*/

case DLL_PROCESS_ATTACH:

/* Allocate a TLS index. */

if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF)
return FALSE;

/* No break: Initialize the index for first thread. */

/* The attached process creates a new thread. */

case DLL_THREAD_ATTACH:

/* Initialize the TLS index for this thread. */

lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData != NULL)
fIgnore = TlsSetValue(dwTlsIndex, lpvData);

break;

/* The thread of the attached process terminates. */

case DLL_THREAD_DETACH:

/* Release the allocated memory for this thread. */

lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);

break;

/*
* The DLL is detaching from a process due to
* process termination or call to FreeLibrary.
*/

case DLL_PROCESS_DETACH:

/* Release the allocated memory for this thread. */

lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);

/* Release the TLS index. */

TlsFree(dwTlsIndex);
break;

default:
break;

}

return TRUE;
UNREFERENCED_PARAMETER(hinstDLL);
UNREFERENCED_PARAMETER(lpvReserved);

}

When a process uses load-time linking with this DLL, the entry-point function is sufficient to manage the thread local storage. Problems can occur with a process that uses run-time linking because the TLS memory is not allocated for threads that exist before the LoadLibrary_win32_LoadLibrary function is called to attach the process. The following example solves this problem by checking the value returned by the TlsGetValue_win32_TlsGetValue function and allocating memory if the value indicates that the TLS slot for this thread is not set.
LPVOID lpvData;

/* Retrieve a data pointer for the current thread. */

lpvData = TlsGetValue(dwTlsIndex);

/* If NULL, allocate memory for this thread. */

if (lpvData == NULL) {
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData != NULL)
TlsSetValue(dwTlsIndex, lpvData);
}

+$#A Dynamic-Link Library Functions
Following are the functions used in dynamic linking.
DisableThreadLibraryCalls_win32_DisableThreadLibraryCallsDllEntryPoint_win32_DllEntryPointFreeLibrary_win32_FreeLibraryFreeLibraryAndExitThread_win32_FreeLibraryAndExitThreadGetModuleFileName_win32_GetModuleFileNameGetModuleHandle_win32_GetModuleHandleGetProcAddress_win32_GetProcAddressLoadLibrary_win32_LoadLibraryLoadLibraryEx_win32_LoadLibraryExLoadModule_win32_LoadModule
Obsolete or Deleted Functions
FreeModule_win32_FreeModuleFreeProcInstance_win32_FreeProcInstanceGetCodeHandle_win32_GetCodeHandleGetInstanceData_win32_GetInstanceDataGetModuleUsage_win32_GetModuleUsageMakeProcInstance_win32_MakeProcInstance

+$#AK Errors
Well-written applications include code that allows them to recover gracefully from unexpected errors. When an error occurs, the application may need to request user intervention, or it may be able to recover without help. In extreme cases, the application may log the user off or shut down the system.
+$#A About Errors
Microsoft® Windows® includes a special error-handling mechanism for exceptions, such as divide-by-zero errors. For more information about exception handling, see Structured Exception Handling_win32_Structured_Exception_Handling.
+$#A Notifying the User
To notify the user that some kind of error has occurred, many applications simply produce a sound by using the Beep_win32_Beep or MessageBeep_win32_MessageBeep function or flash the window by using the FlashWindow_win32_FlashWindow function. An application can also use these functions to call attention to an error and then display a message box or an error message containing details about the error.
+$#A Logging Off or Shutting Down
An application can use the ExitWindows_win32_ExitWindows function to log the current user off. The ExitWindowsEx_win32_ExitWindowsEx function can also perform the following tasks:
· Shut down the system.
· Shut down the system and restart it.
· Log off and force all running applications to terminate.

Logging off stops all processes associated with the security context of the process that called the exit function, logs the user off the system, and displays the Logon dialog box.
Shutting down brings the system to a condition in which it is safe to turn off the computer. All file-system buffers are flushed to the disk, and a message box is displayed informing the user that the computer can be turned off. If the reboot option is specified, the computer restarts.
The calling process must have the SE_SHUTDOWN_NAME privilege to shut down or to shut down and restart. For an example that enables this privilege and shuts down the system, see Shutting Down_win32_Shutting_Down.
+$#A Last-Error Code
When an error occurs, most functions in the Microsoft® Win32® application programming interface (API) return an error code, usually FALSE, NULL, 0xFFFFFFFF, or -1. Most functions in the Win32 API also set an internal error code called the last-error code. When a function succeeds, the last-error code is not reset. The error code is maintained separately for each running thread; an error in one thread does not overwrite the last-error code in another thread. An application can retrieve the last-error code by using the GetLastError_win32_GetLastError function; the error code may tell more about what actually occurred to make the function fail.
The SetLastError_win32_SetLastError function sets the error code for the current thread. The SetLastErrorEx_win32_SetLastErrorEx function also allows the caller to set an error type indicating the severity of the error. These functions are intended primarily for dynamic-link libraries (DLLs), so they can emulate the behavior of the Win32 API.
The Win32 API defines a set of error codes that can be set as last-error codes or be returned by these functions..
Error codes are 32-bit values (bit 31 is the most significant bit). Bit 29 is reserved for application-defined error codes; no Win32 API error code has this bit set. Developers who define an error code for an application should set this bit to indicate that the error code has been defined by an application and to ensure that the error code does not conflict with any system-defined error codes.
+$#A Error Message Boxes
Some errors, such as a drive-not-ready error (when the door of the floppy disk drive is open), cause Windows to display a message box informing the user that an error has occurred. Applications that handle these errors without user intervention can use the SetErrorMode_win32_SetErrorMode function to suppress error message boxes.
+$#A Using Errors
· Notifying the user of errors - example_win32_Notifying_the_User_of_Errors_Example
· Logging off_win32_Logging_Off
· Shutting down_win32_Shutting_Down
· Retrieving the last-error code_win32_Retrieving_the_Last_Error_Code

+$#A Notifying the User of Errors - Example
The following example flashes a window and plays the system exclamation sound.
FlashWindow(hwnd, TRUE); /* invert the title bar */
Sleep(500); /* wait a bit */
FlashWindow(hwnd, TRUE); /* invert again */

/* Play the system exclamation sound. */

MessageBeep(MB_ICONEXCLAMATION);

+$#A Logging Off
The ExitWindows_win32_ExitWindows function logs the current user off. All applications must agree to terminate before the user logs off. If any application returns FALSE when it processes the WM_QUERYENDSESSION_win32_WM_QUERYENDSESSION message, the user is not logged off. If your application handles the WM_QUERYENDSESSION message, you can allow the user to cancel the log-off operation, even if another application or Windows originated the end-session request.
The following example logs off the current user, unless the user selects the No button in the message box displayed when the application receives the WM_QUERYENDSESSION message.
ExitWindows(0, 0); /* log the current user off */

case WM_QUERYENDSESSION: /* in application's MainWndProc */
{
int i = MessageBox(NULL, "Shutdown?",
"Received WM_QUERYENDSESSION", MB_YESNO);

/*
* Return TRUE to allow shutdown, FALSE to stop.
*/

return i == IDYES;
break;
}

+$#A Shutting Down
The ExitWindowsEx_win32_ExitWindowsEx function can be used to shut down the system. Shutting down flushes file buffers to disk and brings the system to a condition in which it is safe to turn off the computer. The calling process must have the SE_SHUTDOWN_NAME privilege to shut down the system.
The following example enables the SE_SHUTDOWN_NAME privilege and then shuts down the system.
HANDLE hToken;
TOKEN_PRIVILEGES tkp;

/* Get a token for this process. */

if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
error("OpenProcessToken");

/* Get the LUID for the shutdown privilege. */

LookupPrivilegeValue(NULL, TEXT("SE_SHUTDOWN_NAME"),
&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get the shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES)NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)
error("AdjustTokenPrivileges");

/* Shut down the system and force all applications to close. */

if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0))
error("ExitWindowsEx");

For more information about setting security privileges, see Security_win32_Security.
+$#A Retrieving the Last-Error Code
When a function in the Win32 API fails, it sets the last-error code. If your application needs more details about an error, it can retrieve the last-error code.
The following example shows an error-handling function used by the other examples in this topic.
void error(LPSTR lpszFunction)
{
CHAR szBuf[80];
DWORD dw = GetLastError();

sprintf(szBuf, "%s failed: GetLastError returned %u\n",
lpszFunction, dw);

MessageBox(NULL, szBuf, "ERROR", MB_OK);
ExitProcess(dw);
}

+$#A Error Functions
Following are the functions used with errors.
Beep_win32_BeepExitWindows_win32_ExitWindowsExitWindowsEx_win32_ExitWindowsExFlashWindow_win32_FlashWindowGetLastError_win32_GetLastErrorMessageBeep_win32_MessageBeepSetErrorMode_win32_SetErrorModeSetLastError_win32_SetLastErrorSetLastErrorEx_win32_SetLastErrorEx

+$#AK Event Logging
When an error occurs in an application written for Microsoft® Windows®, the system administrator or support personnel must determine what caused the error, attempt to recover any lost data, and prevent the error from recurring. If applications record important events such as low-memory conditions or excessive disk retries, the record of this information, called an event log, can be used to help determine what conditions may have caused the error and the context in which the error occurred. (Event logging can also be applied to the Windows operating system and to system services.) By periodically viewing the event log, the system administrator may be able to identify problems before they cause serious damage.
Many applications record errors and events in various proprietary error logs. These proprietary logs have different formats, apply different user interfaces for their display, and cannot be merged to provide a complete report. But all of these logs store similar information and present the logged data in similar ways.
Event logging in Windows provides a standard, centralized way for applications (and Windows) to record important software and hardware events. It also supplies a standard user interface for viewing the logs and a programming interface for examining the logs. Event logging provides a means to merge events from various sources into a single informative story.
+$#A Event Logging Model
Event logs store records of significant events on behalf of the Windows operating system and the applications that run with Windows. Generally, applications should log only information that could be useful in diagnosing a hardware or software problem.
Events are classified as information, warnings, and errors. All event classifications have well-defined common data and can optionally include event-specific data. For example, information can assert that a service has started. Warnings are used for recoverable problems such as low disk-space conditions. Errors are used for nonrecoverable conditions that might cause an application to fail. In addition to logging event classifications, the security service logs successes and failures in audits.
+$#A Logging Guidelines
Because logging functions are general purpose, developers must decide what information is appropriate to log. It is important to note that logging consumes resources: disk space and logging time. The event log is not intended to be used as a trace facility.
You should log information about events that can be used to diagnose problems after they have occurred. Logging low-memory conditions, for example, can provide information about a problem that the system administrator can solve by adding more memory to the computer.
Following are more examples in which event logging is helpful:
· A disk driver encounters a bad sector on the disk. It may be able to read from or write to the sector after some retries, but the sector will probably go bad eventually. The disk driver should log a warning if it can proceed; otherwise, it should log an error.
· A file system finds a bad sector and fixes it. A large number of events of this type might indicate that the disk is about to fail. The file system logs a warning event.
· A device driver encounters a hardware problem, such as a disk controller time-out, a power failure in a parallel port, or a data error from a network or serial card. Logging information about these events can help in diagnosing hardware problems. The device driver logs the hardware problem.
· An Application encounters resource problems. An application or a device driver may get into a low-memory situation (caused by a code bug, for example) that degrades performance. Logging an event when memory allocation fails might later provide a clue about what went wrong. The application logs the resource problem.
· A server application (a database or a communications server, for example) records events such as a user logging on, opening a database, or starting a file transfer. The server can also log important information as it runs, such as the number of resources the application is using, errors it encounters (cannot access file, host process disconnected, and so on), a corruption in the database (not able to find a leaf node down a certain path in a tree), or whether a file transfer was successful or not. All of this information can be valuable to a support person (or to the developer of the application). The application records such events and logs them as information events.

+$#A Logging Performance and Size
Typically, how much disk space does an event log require? What is the overhead for an application that makes a call to log an event? The answers to these questions depend on how much information the application chooses to log. This is why it is important to log only essential information and to place event logging calls in an error path in the application, rather than place them in the main path, where the performance hit would be more severe.
+$#A Event Logging Management Information
Event logging management information is stored in the Services key of the configuration database and can be modified by a system administrator.
The structure of the configuration information is as follows:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
EventLog
Application
Security
System

The EventLog key contains several subkeys, called logfiles. The default logfiles are Application, Security, and System. Each logfile subkey can contain subkeys, called sources. You cannot use a source name that has been used as a logfile name, and source names should not be hierarchical. (The backslash character [\] cannot be used in a registry key.) Each source entry contains information specific to the source of the event, as shown in the following table.
Value Description
EventMessageFile Specifies the path for the event identifier_win32_Event_Identifier message file. This value has the type REG_EXPAND_SZ.
CategoryMessageFile Specifies the path for the category message file. The event category_win32_Category and event identifier message strings can be in the same file. This value has the type REG_EXPAND_SZ.
ParameterMessageFile Specifies the path for the event source's parameter message file. This file contains language-independent strings that are to be inserted into the event description_win32_Description strings. You can use the same message file for parameter, category, and event identifier message strings. This value has the type REG_EXPAND_SZ.
CategoryCount Specifies the number of categories supported. This value has the type REG_DWORD.
TypesSupported Specifies a bitmask of supported types. This value has the type REG_DWORD.

When an application uses the RegisterEventSource_win32_RegisterEventSource or OpenEventLog_win32_OpenEventLog function to get a handle of an event log, the event logging service searches for the specified source name in the registry. For example, the Application logfile might have configured sources of Microsoft® SQL Server™ and Microsoft® Excel. If an application uses RegisterEventSource or OpenEventLog with a source name of Application, SQL, or Excel, the event logging service returns a handle to the Application logfile.
An application can use the Application event log without adding a new source key to the registry. If the application calls RegisterEventSource, passing a source name that cannot be found in the registry, the event logging service uses the Application logfile by default. However, because there is not a message or category string file, the event viewer will not be able to map the event identifier_win32_Event_Identifier or category to a replacement string. For this reason, the recommended procedure is to add a unique source name for the application to the registry. This allows you to specify message files for the event identifier and category in your events. Applications and services should add their source names to the Application logfile. Device drivers should add their source name to the System logfile.
An event viewer application uses the OpenEventLog function to open the event log for an event source. The event viewer can then use the ReadEventLog function to read event records from the log. ReadEventLog returns a buffer containing an EVENTLOGRECORD structure and additional information that describes a logged event. The EventID member of the EVENTLOGRECORD is the identifier of a description string in the source's event message file. The event viewer uses the LoadLibrary function to load the file indicated by the source's EventMessageFile registry value. The viewer then uses the FormatMessage function to retrieve the description string from the loaded module.
The description string may contain insertion string placeholders, such as %n, where %1 indicates the first insertion string, and so on. In this case, the buffer returned by ReadEventLog contains the insertion strings. The NumStrings member of the EVENTLOGRECORD indicates the number of insertion strings. The StringOffset member of the EVENTLOGRECORD indicates the location of the first insertion string in the buffer.
An insertion string may also contain placeholders of the form %%n, where n is the identifier of a string in the source's parameter message file. In this case, the event viewer uses LoadLibrary and FormatMessage to retrieve the insertion string from the file indicated by the source's ParameterMessageFile registry value.
For more information about using the registry, see Registry_win32_Registry. For more information about creating and using message files, see your message compiler documentation.
+$#A Event Logging Operations
In the following table are the five operations that can be performed on the event logs.
Operation Function
Backup BackupEventLog_win32_BackupEventLog
Clear ClearEventLog_win32_ClearEventLog
Query GetOldestEventLogRecord_win32_GetOldestEventLogRecord, GetNumberOfEventLogRecords_win32_GetNumberOfEventLogRecords
Read ReadEventLog_win32_ReadEventLog
Write ReportEvent_win32_ReportEvent

The OpenEventLog_win32_OpenEventLog, OpenBackupEventLog_win32_OpenBackupEventLog, RegisterEventSource_win32_RegisterEventSource, DeregisterEventSource_win32_DeregisterEventSource, and CloseEventLog_win32_CloseEventLog functions also open and close event log handles; this provides an object-oriented model for the function set, as well as some performance gain when performing multiple operations on the logs.
The OpenEventLog and ReportEvent functions take an optional server name as a parameter so that the operations can be performed on the remote server. OpenEventLog should be used for reading or performing some administrative operation (backup, clear, query) on the log, and RegisterEventSource should be used for writing to logs.
+$#A Event Log Entry
This guide describes the parameters supplied for logged events.
+$#A Source
Source is the name of the software that logs the event. For many applications, Source may be simply the application name, or it may reflect a subcomponent of a larger application.
+$#A Event Identifier
Event identifiers are looked up within a message file to locate a description string that can be presented to the user. Support personnel also use event identifiers; if a user can identify the source and the event identifier, support personnel will know exactly which event has occurred. Event identifiers are unique to a particular source.
+$#A Type
Windows NT™ defines five types of event logging. Each event must be of a single type, because types cannot be combined.
Event type Description
Information Information events are used to note infrequent but significant successful operations. For example, when Microsoft SQL Server successfully loads, it may be appropriate to log a "SQL Server has started" information event. Note that while this is appropriate for major server services, it is generally inappropriate for a desktop application (Microsoft Excel, for example) to log an event each time it is started. Information events should not be used as a trace facility.
Warning Warning events are used to indicate problems that are not significant, but that may foretell of future errors or other problems. Resource consumption is a good candidate for a Warning event. For example, a warning could be logged if disk space is low. Errors that are recovered without loss of function or data can be classified as warnings.
Error Error events are used to indicate significant problems that have occurred and that the user should know about. Errors usually indicate a loss of functionality or data. For example, if a service cannot be loaded as the system boots, an Error event can be logged.
Success Audit Success Audit events are security events that occur when an audited access attempt is successful. For example, a successful logon attempt is a Success Audit event.
Failure Audit Failure Audit events are security events that occur when an audited access attempt fails. For example, a failed attempt to open a file is a Failure Audit event.

+$#A Category
Categories are used to help organize events so that they can be filtered in the Event Viewer. Each Source_win32_Source can define its own numbered Categories and the text strings to which they are mapped. The Categories must be numbered consecutively beginning with the number 1. With Event Viewer, it is possible to filter by Source and within Source, by Category. The security system uses several categories: Logon/Logoff, File System Access, Privileged Actions, Change in Security Policy. Categories for other parts of the system are developed over time as it becomes clear which events are being logged. An application can define its own categories.
+$#A Strings
Strings are optional language-independent (nonlocalized) strings used to fill in values for placeholders in description strings. Because the strings are not localized, it is critical that this field be used only to store language-independent strings such as numeric values or string tokens (filenames, user names, and so on). String length must not exceed 32 kilobytes - 1 characters.
It is not acceptable to use several strings to "paste" together a larger description or to use nontoken strings. The insertion string should be treated as data, not text. For example, the following example is not recommended:
Str1$ = successfully
Description = "User was %1 added to database."

Using these two strings to form the string, "User was successfully added to database." (the alternative being Str1$ = "not") is not acceptable for three reasons:
1. The strings "successfully" and "not" should be localized.
2. Even if the insertion strings are obtained from language-dependent message files, this is done when the event is logged, not when it is viewed. When the event is viewed, the language may be wrong.
3. Such substitutions of adverbs and adjectives will not work in many other languages. The preceding example should use two separate events.

The following example shows an appropriate use of strings:
Str1$ = "c:\testapp.c".
Description = "Access denied. Attempted to open the file %1"

+$#A Description
Each event is associated with a description located in a message file that is registered by the Source_win32_Source. The description should help the user understand what went wrong and suggest what actions to take. Focus the description toward users solving their own problems, not toward support people or developers. Make the description clear and concise and avoid culture-specific phrases. If your event includes private data in the Data field, include at the end of the description an explanation about what the Data field contains. For example, the network software often notes: "(The SMB is the event data)." As a convention, use parentheses around such remarks, as indicated in this example. Several applications can share the same message file, and the message file can contain descriptions for both the event identifier_win32_Event_Identifier and the event category_win32_Category.
The description strings are indexed by event identifier, enabling Event Viewer to display event-specific text for any event based on the event identifier. All descriptions are localized and language dependent. Description strings can optionally contain placeholders for insertion strings. Each placeholder is represented by a percent sign and an index number for the string to substitute. For example, the placeholder %1 is replaced by the first insertion string, %5 is replaced by the fifth insertion string, and so on.
+$#A Data
Each event can have event-specific data associated with it. Event Viewer has no information of any of the events, and only displays extra data in a combined hexadecimal and text dump format. Use event-specific data sparingly, including it only if you are sure it will be useful to a support technician or a developer. You might include, for example, data containing a structure filled with information that could help a technician debug an application failure.
Examples of event-specific data are found in many network events; network control blocks (NCBs) are included as event-specific data. In all cases, the last part of the description string should include a note about what information is available as event-specific data. Third-party applications can use the Data field to store information the application can process independently of Event Viewer. For example, you could write your own viewer specific to your events, or write a program that scans the logfiles and makes reports, including information from the event-specific data.
+$#A The EVENTLOGRECORD Structure
The ReadEventLog_win32_ReadEventLog function uses the EVENTLOGRECORD_win32_EVENTLOGRECORD_str structure to format event log information. This structure contains information relating to the content of the event record. The actual event record information follows this structure in the buffer returned by the ReadEventLog function.
+$#A Special Cases
Each call to an event logging function is considered to be an atomic operation. For reading the event log, only whole event records are returned. For logging events, each event record is guaranteed to be written as a complete record in the log. The following list describes how the event logging service handles special conditions:
· Multiple calls written to the same log. The event logging service serializes the operations; the event logs are written sequentially in the logfile.
· A call to read and a call to write are made at the same time. What happens depends on where the current read operation is being carried out in the file. If the read position is at the end of the file, either the read operation fails with an "end-of-file" status (if the write operation has not been done), or it returns the new record that the write operation has just written to the log.
· Clearing the log before reading. The read operation returns an "end-of-file" status after the file is cleared.
· Clearing the log before writing. The clear operation truncates the logfile; the write operation then writes the new record at the beginning of the log.

+$#A Using Event Logging
· Adding a source to the registry_win32_Adding_a_Source_to_the_Registry.
· Reporting an event_win32_Reporting_an_Event.
· Querying the event log_win32_Querying_the_Event_Log.
· Reading the event log_win32_Reading_the_Event_Log.

You can use the Windows Registration Database Editor (REGEDIT.EXE) to view the event log registry keys, and you can use Event Viewer to view the event logs.
+$#A Adding a Source to the Registry
You can use the default Application event log without adding your source name to the registry. However, Event Viewer will not be able to map your event identifier_win32_Event_Identifier codes to message strings unless you register your source and provide a message-file name.
You can add a new source name to the registry by opening a new registry subkey under the Application key and adding registry values to the new subkey. The following code sample opens a new source name called SamplApp and adds to the new subkey a message-file name and a bitmask of supported types.
HKEY hk;
DWORD dwData;
UCHAR szBuf[80];

/*
* Add your source name as a subkey under the Application
* key in the EventLog service portion of the registry.
*/

if (RegCreateKey(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\
\\EventLog\\Application\\SamplApp",
&hk))
ErrorExit("could not create registry key");

/* Set the Event ID message-file name. */

strcpy(szBuf, "%SystemRoot%\\System\\SamplApp.dll");

/* Add the Event ID message-file name to the subkey. */

if (RegSetValueEx(hk, /* subkey handle */
"EventMessageFile", /* value name */
0, /* must be zero */
REG_EXPAND_SZ, /* value type */
(LPBYTE) szBuf, /* address of value data */
strlen(szBuf) + 1)) /* length of value data */
ErrorExit("could not set event message file");

/* Set the supported types flags. */

dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE;

if (RegSetValueEx(hk, /* subkey handle */
"TypesSupported", /* value name */
0, /* must be zero */
REG_DWORD, /* value type */
(LPBYTE) &dwData, /* address of value data */
sizeof(DWORD))) /* length of value data */
ErrorExit("could not set supported types");

RegCloseKey(hk);

+$#A Reporting an Event
After you have added a source name to the registry, use the RegisterEventSource_win32_RegisterEventSource function to get a handle of the Application event log, as in the following example, which gets the handle and then adds an event to the logs.
LPSTR aszMsg[] = {
"SamplApp",
};

HANDLE h;

h = RegisterEventSource(NULL, /* uses local computer */
"SamplApp"); /* source name */
if (h == NULL)
ErrorExit("could not register event source");

if (!ReportEvent(h, /* event log handle */
EVENTLOG_ERROR_TYPE, /* event type */
0, /* category zero */
0x1003, /* event identifier */
NULL, /* no user security identifier */
1, /* one substitution string */
0, /* no data */
(LPTSTR *) aszMsg, /* address of string array */
NULL)) /* address of data */
ErrorExit("could not report event");

DeregisterEventSource(h);

+$#A Querying the Event Log
The following code sample displays the number of event records currently in the Application event log and the System event log.
/* Open the System log. */

h = OpenEventLog(NULL, /* uses local computer */
"System"); /* source name */
if (h == NULL)
ErrorExit("could not open System event log");

/* Get the number of records in the System event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))
ErrorExit
("could not get number of records");

printf("There are %d records in the System event log.\n", cRecords);

CloseEventLog(h);

/* Open the Application log. */

h = OpenEventLog(NULL, /* uses local computer */
"Application"); /* source name */
if (h == NULL)
ErrorExit("could not open Application event log");

/* Get the number of records in the Application event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))
ErrorExit("could not get number of records");

printf("There are %d records in the Application event log.\n",
cRecords);

CloseEventLog(h);

+$#A Reading the Event Log
The following code sample reads all the records in the Application logfile and displays the event identifier_win32_Event_Identifier, event type, and source name for each event log entry.
EVENTLOGRECORD *pevlr;
BYTE bBuffer[BUFFER_SIZE];
DWORD dwRead, dwNeeded, cRecords, dwThisRecord = 0;

/* Open the Application event log. */

h = OpenEventLog(NULL, /* uses local computer */
"Application"); /* source name */
if (h == NULL)
ErrorExit("could not open Application event log");

pevlr = (EVENTLOGRECORD *) &bBuffer;

/*
* Opening the event log positions the file pointer
* for this handle at the beginning of the log.
*
* Read records sequentially until there
* are no more.
*/

while (ReadEventLog(h, /* event log handle */
EVENTLOG_FORWARDS_READ | /* reads forward */
EVENTLOG_SEQUENTIAL_READ, /* sequential read */
0, /* ignored for sequential reads */
pevlr, /* address of buffer */
BUFFER_SIZE, /* size of buffer */
&dwRead, /* count of bytes read */
&dwNeeded)) { /* bytes in next record */

while (dwRead > 0) {

/*
* Print the event ID, type, and source name.
* The source name is just past the end of the
* formal structure.
*/

printf("%02d Event ID: 0x%08X ",
dwThisRecord++, pevlr->EventID);
printf("EventType: %d Source: %s\n",
pevlr->EventType, (LPSTR) ((LPBYTE) pevlr +
sizeof(EVENTLOGRECORD)));

dwRead -= pevlr->Length;
pevlr = (EVENTLOGRECORD *)
((LPBYTE) pevlr + pevlr->Length);
}

pevlr = (EVENTLOGRECORD *) &bBuffer;
}

CloseEventLog(h);

+$#A Event Logging Functions
The following functions are used with event logging.
BackupEventLog_win32_BackupEventLogClearEventLog_win32_ClearEventLogCloseEventLog_win32_CloseEventLogDeregisterEventSource_win32_DeregisterEventSourceGetNumberOfEventLogRecords_win32_GetNumberOfEventLogRecordsGetOldestEventLogRecord_win32_GetOldestEventLogRecordNotifyChangeEventLog_win32_NotifyChangeEventLogOpenBackupEventLog_win32_OpenBackupEventLogOpenEventLog_win32_OpenEventLogReadEventLog_win32_ReadEventLogRegisterEventSource_win32_RegisterEventSourceReportEvent_win32_ReportEvent

+$#AK File Mapping
This Windows 95 overview discusses the file-mapping functions in the Microsoft® Win32® application programming interface (API). These functions allow a process to create file-mapping objects and file views to easily access and share data.
+$#A File Mapping Platform Differences
The overviews for file mapping in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.
+$#A About File Mapping
{ewl msdncd, EWGraphic, group10747 0 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview About File Mapping_win32_About_File_Mapping_nt.
File mapping is the association of a file's contents to a portion of a process's virtual address space. The operating system creates a file-mapping object to maintain this association. A file view is the portion of virtual address space that is used to access the file's contents. Processes read and write to the file view using pointers just as they would with dynamically allocated memory. Also, the VirtualProtect_win32_VirtualProtect function operates on file views just like any other virtual memory.
Windows reads data from the file into the file view and writes it back out to the file as needed. In effect, file mapping is a way to tell the system to back a specified part of a process's virtual address space with a specified file or with the system pagefile.
Mapping a file allows a process to access files on disk in the same way as dynamically allocated memory or to share memory between two or more processes. The file-mapping functions in the Microsoft® Win32® application programming interface (API) allow a process to create file-mapping objects and file views to easily access and share data.
+$#A Accessing Files Using the CreateFile Function
The first step in mapping a file is to open the file by calling the CreateFile_win32_CreateFile function. The file should be opened with exclusive access to ensure that other processes cannot write to the portion of the file that is mapped. An easy way to obtain exclusive access is to specify zero in the fdwShareMode parameter of CreateFile.
The CreateFileMapping_win32_CreateFileMapping function uses the handle returned by CreateFile to create a file-mapping object. CreateFileMapping enables you to specify an object name, the number of bytes to be mapped from the file, and the read-write access to the mapped memory. CreateFileMapping fails if the access flags conflict with those specified when the file was opened by using CreateFile. For example, to read and write to the file, specify the GENERIC_READ and GENERIC_WRITE values in the fdwAccess parameter of CreateFile and specify the PAGE_READWRITE value in the fdwProtect parameter of CreateFileMapping.
A file mapping can map a file as large as the file system allows. The dwMaximumSizeHigh and dwMaximumSizeLow parameters of CreateFileMapping allow you to specify the number of bytes to be mapped from the file. The size of the file mapping is independent of the size of the file being mapped.
However, if the mapping is larger than the file, the system grows the file before CreateFileMapping returns. If the file mapping is smaller than the file, only the specified number of bytes from the file is mapped.
When you do not want the size of the file to change (for example, when mapping read-only files), call CreateFileMapping and specify zero for both dwMaximumSizeHigh and dwMaximumSizeLow. Doing this creates a file-mapping object exactly the same size as the file. Otherwise, you must calculate or estimate the size of the finished file because file-mapping objects are static in size; once created they cannot grow or shrink.
To actually map the data from the file to memory, you must create a view of the file. The MapViewOfFile_win32_MapViewOfFile and MapViewOfFileEx_win32_MapViewOfFileEx functions use the file-mapping object handle returned by CreateFileMapping_win32_CreateFileMapping to create a view (or a copy) of the file or a portion of the file in the process's virtual address space. These functions fail if the access flags conflict with those specified when the file-mapping object was created by CreateFileMapping. MapViewOfFileEx works exactly like MapViewOfFile except that it allows a process to specify the base address of the view of the file in the process's virtual address space.
MapViewOfFile returns a pointer to the file view. An application uses this pointer to read and write data to the file mapping in the same way as it would access dynamic memory. By dereferencing a pointer in the range of addresses specified in MapViewOfFile, an application can read and write data to the file. The actual reading and writing to the file on disk is handled by the system. Data is not actually transferred at the time the file-mapping object is written to. Instead, much of the file input and output (I/O) is cached to improve general system performance. Applications may override this behavior by calling the FlushViewOfFile_win32_FlushViewOfFile function to force the system to perform disk transactions immediately.
An application may create multiple file views from the same file-mapping object. A file view can be a different size than the file-mapping object from which it is derived, but it must fit within the file-mapping object. The offset specified by the dwOffsetHigh and dwOffsetLow parameters of MapViewOfFile must be a multiple of the allocation granularity of the system. To obtain the system's memory allocation granularity, use the GetSystemInfo_win32_GetSystemInfo function, which fills in the members of a SYSTEM_INFO_win32_SYSTEM_INFO_str structure.
When done with the file-mapping object, the process should destroy all file views in its address space by using the UnmapViewOfFile_win32_UnmapViewOfFile function for each file view. This function invalidates the pointer to the process's virtual address space. If any of the pages of the file view have changed since the view was mapped, the system writes the changed pages of the file "lazily" to disk. To commit the data to disk immediately, call FlushViewOfFile before unmapping the view.
Both the file-mapping object and the file must be closed by calling CloseHandle_win32_CloseHandle to first close the file-mapping object and then the file.
After closing a file-mapping object and unmapping all views, if necessary, a process can adjust the size of the file by using the SetFilePointer_win32_SetFilePointer and SetEndOfFile_win32_SetEndOfFile functions. For example, if the mapping is larger than the desired size of the file, a process can set the file pointer to the desired size of the file.
+$#A Sharing Files and Memory
File mapping can be used to share a file or memory between two or more processes. To do so, all of the processes must use the same file-mapping object to create identical views of the file or memory. Each process's view resides in the process's own virtual address space. If one process updates its view, the other process sees the update immediately.
To share a file the first process creates or opens a file by using CreateFile_win32_CreateFile. Next, it creates a file-mapping object by using CreateFileMapping_win32_CreateFileMapping and specifying a name for the file-mapping object. To share memory that is not associated with a file, a process must use the CreateFileMapping function and specify (HANDLE)0xFFFFFFFF instead of an existing file handle. The corresponding file-mapping object accesses memory backed by the system paging file. You must specify a size greater than zero when you specify (HANDLE)0xFFFFFFFF in a call to CreateFileMapping.
The easiest way for other processes to obtain a handle of the file-mapping object created by the first process is to use the OpenFileMapping_win32_OpenFileMapping function and specify the object's name. If the file-mapping object does not have a name, the process must obtain a handle to it through inheritance or duplication. For more information on inheritance and duplication, see Processes and Threads_win32_Processes_and_Threads, and Handles and Objects_win32_Handles_and_Objects.
Processes that share files or memory must create file views by using the MapViewOfFile_win32_MapViewOfFile or MapViewOfFileEx_win32_MapViewOfFileEx function just as they would when accessing a file. They must coordinate their access using semaphores, mutexes, events, or some other mutual exclusion technique. For more information on synchronization, see Synchronization_win32_Synchronization.
A shared file-mapping object will not be destroyed until all processes that use it close their handles to it by using CloseHandle_win32_CloseHandle and call UnMapViewOfFile on all file views.
+$#A Coherence
Coherence guarantees that the data accessible in a file view is an identical copy of the file's contents on disk. Although the CreateFileMapping_win32_CreateFileMapping function works with remote files, it does not keep them coherent. For example, if two computers both map a file as writable, and both change the same page, each computer will see only its own write operations to the page. When the data gets updated on the disk, it is not merged.
+$#A Using File Mapping
{ewl msdncd, EWGraphic, group10747 1 /a "SDK.BMP"} Windows 95 Overview For more information, see the Windows NT overview Using File Mapping_win32_Using_File_Mapping_nt.
The code samples in this guide illustrate how two processes might access an existing file as named shared memory.
As discussed earlier, the processes must synchronize their access to the memory. For more information on synchronization, see Synchronization_win32_Synchronization.
In the following code, the first process calls CreateFileMapping_win32_CreateFileMapping function to create a file-mapping object. When a corresponding file view is created, the process has read-write access to the memory.
hMapFile = CreateFileMapping(hFile, // current file handle
NULL, // default security
PAGE_READWRITE, // read-write access
0, // max. object size
0, // size of hFile
"MyFileMappingObject"); // name of map. object

if (hMapFile == NULL) {
ErrorHandler("Could not create file-mapping object.");
}

The following code uses the file-mapping object handle returned by CreateFileMapping_win32_CreateFileMapping to create a view of the file in the process's address space.
lpMapAddress = MapViewOfFile(hMapFile, // handle to map. object
FILE_MAP_ALL_ACCESS, // read-write access
0, // max. object size
0, // size of hFile
0); // map entire file

if (lpMapAddress == NULL) {
ErrorHandler("Could not map view of file.");
}

The MapViewOfFile_win32_MapViewOfFile function returns a pointer to the file view. The process can use the pointer to access the memory for whatever it needs to do.
In the next example, a second process uses the OpenFileMapping_win32_OpenFileMapping function to share the same memory. As in the original process, the MapViewOfFile function returns a pointer to the file view.
hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, // r-w access
FALSE, // do not inherit
"MyFileMappingObject"); // name of mapping obj.

if (hMapFile == NULL) {
ErrorHandler("Could not open file-mapping object.");
}

lpMapAddress = MapViewOfFile(hMapFile, // handle to map. obj.
FILE_MAP_ALL_ACCESS, // read-write access
0, // max. object size
0, // size of hFile
0); // map entire file

if (lpMapAddress == NULL) {
ErrorHandler("Could not map view of file.");
}

Note that MapViewOfFile_win32_MapViewOfFile may return different addresses in different processes. To specify the location of the file view within a process's address space, use the MapViewOfFileEx_win32_MapViewOfFileEx function rather than MapViewOfFile.
The following example uses the UnmapViewOfFile_win32_UnmapViewOfFile function to invalidate the pointer to the mapped memory. This destroys the file view in the process's address space. If any pages of the file view have changed since the file view was mapped, UnmapViewOfFile also copies the changed pages to the file on disk.
if (!UnmapViewOfFile(lpMapAddress)) {
ErrorHandler("Could not unmap view of file.");
}

When a process is finished using a file mapping and has umapped all views, it must close the file mapping object's handle by calling CloseHandle_win32_CloseHandle.
CloseHande(hMapFile);

The FlushViewOfFile_win32_FlushViewOfFile function copies the specified number of bytes of the file view to the physical file but does not destroy the file view:
if (!FlushViewOfFile(lpMapAddress, dwBytesToFlush)) {
ErrorHandler("Could not flush memory to disk.");
}

+$#A File Mapping Functions
The following functions are used with file mapping.
CreateFileMapping_win32_CreateFileMappingFlushViewOfFile_win32_FlushViewOfFileMapViewOfFile_win32_MapViewOfFileMapViewOfFileEx_win32_MapViewOfFileExOpenFileMapping_win32_OpenFileMappingUnmapViewOfFile_win32_UnmapViewOfFile

+$#AK File Mapping (Windows NT)
This Windows NT™ overview discusses the file-mapping functions in the Microsoft® Win32® application programming interface (API). These functions allow a process to create file-mapping objects and file views to easily access and share data.
+$#A About File Mapping
{ewl msdncd, EWGraphic, group10748 0 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview About File Mapping_win32_About_File_Mapping.
File mapping is the copying of a file's contents to a process's virtual address space. The copy of the file's contents is called the file view, and the internal structure the operating system uses to maintain the copy is called the file-mapping object.
Another process can create an identical file view in its own virtual address space by using the first process's file-mapping object to create the view. This enables the processes to share data. Any process that has the name or a handle of a file-mapping object can create a file view.
Microsoft® Windows NT™ file-mapping functions create file-mapping objects and file views necessary for this method of data sharing.
+$#A Sharing a File
If two (or more) processes need to share a file, one process should open the file for exclusive access by using the CreateFile_win32_CreateFile function. The CreateFileMapping_win32_CreateFileMapping function uses the handle returned by CreateFile to create a file-mapping object. Processes that have the name or a handle of the file-mapping object can share the file's data. The file handle should remain open until the processes are no longer sharing the file; this prevents nonsharing processes from reading from or writing to the file.
CreateFileMapping enables you to specify an object name, the number of bytes to be mapped from the file, and the read-and-write access to the mapped memory. CreateFileMapping fails if the access flags conflict with those specified when the file was opened by using CreateFile. CreateFileMapping returns a handle of the file-mapping object. This handle is used to create a "view of the file," or "file view," in the process's virtual address space. A file view is a copy of the entire file or a portion of the file, depending on the number of bytes requested.
The process that created the file-mapping object uses the MapViewOfFile_win32_MapViewOfFile function with the file-mapping object handle to create a file view, or copy of the file, in the process's virtual address space. MapViewOfFile returns a pointer to the file view. This function fails if the access flags conflict with those specified when the mapping object was created with CreateFileMapping.
To share the data in the file view, another process must create an identical file view in its own virtual address space. To do this, the process must first obtain a handle of the same file-mapping object and then create its own file view by using MapViewOfFile. The easiest way to obtain a handle of the file-mapping object is to use the OpenFileMapping_win32_OpenFileMapping function and specify the object's name. If the file-mapping object does not have a name, the process must obtain a handle of it through inheritance or duplication. For more information on inheritance and duplication, see Processes and Threads_win32_Processes_and_Threads and Handles and Objects_win32_Handles_and_Objects.
MapViewOfFile returns a pointer to the file view. To ensure that multiple processes are sharing the same data through their own pointers, all views of a file must be derived from the same file-mapping object. If this is the case, the file views are said to be coherent, or identical. Typically, file views are not coherent if they are derived from different file-mapping objects, even if the mapping objects refer to the same file.
Because the file view is basically a block of memory and not a file, processes read from and write to the file view by using pointers just as though the block were dynamically allocated. You do not need to use the ReadProcessMemory_win32_ReadProcessMemory and WriteProcessMemory_win32_WriteProcessMemory functions.
If one or more of the processes has write access to the shared data, processes must synchronize their access to the shared data by using a semaphore. This ensures that the file views remain coherent in all processes. For more information on synchronization, see Synchronization_win32_Synchronization.
Some applications require that a file be mapped to the same address in multiple processes. To place the file view at a specific location within the process's address space, you should use the MapViewOfFileEx_win32_MapViewOfFileEx function and specify the desired address. If there is not enough space at the specified address, the function fails.
Although CreateFileMapping works with remote files, it does not keep them coherent. For example, if two machines both map a file as writable, and both change the same page, each machine will only see its own writes to the page. When the data gets updated on the disk, it is not merged.
+$#A Sharing a Memory Block
If two (or more) processes need to share a memory block not associated with a file, one process must use the CreateFileMapping_win32_CreateFileMapping function and specify (HANDLE)0xFFFFFFFF instead of an existing file handle. The corresponding file-mapping object accesses memory from the operating-system paging file. Processes sharing the operating-system paging file must create file views by using the MapViewOfFile_win32_MapViewOfFile or MapViewOfFileEx_win32_MapViewOfFileEx function just as when sharing a named file.
+$#A Closing a File View
When a process has finished using the shared memory, it should destroy the file view in its address space by using the UnmapViewOfFile_win32_UnmapViewOfFile function. This invalidates the pointer to the shared memory. If any of the pages of the file view changed since the view was mapped, this function copies the changed pages to the physical file.
To copy a specified number of bytes from the file view to the physical file, you should use the FlushViewOfFile_win32_FlushViewOfFile function. FlushViewOfFile does not destroy the file view.
For information on other ways to share data among applications, see Dynamic Data Exchange Management Library_win32_Dynamic_Data_Exchange_Management_Library.
+$#A Using File Mapping
{ewl msdncd, EWGraphic, group10748 1 /a "SDK.BMP"} Windows NT Overview For more information, see the Windows 95 overview Using File Mapping_win32_Using_File_Mapping.
The code samples in this guide illustrate how two processes might access an existing file as named shared memory.
If one or both processes write to the shared memory, the processes must use a semaphore to synchronize their access to the memory. For more information on synchronization, see Synchronization_win32_Synchronization.
In the following code, the CreateFileMapping_win32_CreateFileMapping function creates a file-mapping object. When a corresponding file view is created, the process has read-and-write access to the memory.
hMapFile = CreateFileMapping(hFile, /* current file handle */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
PAGE_READWRITE, /* read-write access */
0, /* max. object size is */
0, /* size of hFile */
"MyFileMappingObject"); /* name of map. object */

if (hMapFile == NULL) {
ErrorHandler("Could not create file-mapping object.");
}

The following code uses the file-mapping object handle returned by CreateFileMapping to create a view of the file in the process's address space.
lpMapAddress = MapViewOfFile(hMapFile, /* handle to map. object */
FILE_MAP_ALL_ACCESS, /* read-write access */
0, /* max. object size is */
0, /* size of hFile */
0); /* map entire file */

if (lpMapAddress == NULL) {
ErrorHandler("Could not map view of file.");
}

The MapViewOfFile_win32_MapViewOfFile function returns a pointer to the file view. The process can use the pointer to access the memory for whatever it needs to do.
In the next example, a second process uses the OpenFileMapping_win32_OpenFileMapping function to share the same memory. As in the original process, the MapViewOfFile function returns a pointer to the file view.
hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, /* r-w access */
FALSE, /* do not inherit */
"MyFileMappingObject"); /* name of mapping obj. */

if (hMapFile == NULL) {
ErrorHandler("Could not open file-mapping object.");
}

lpMapAddress = MapViewOfFile(hMapFile, /* hndl. to map. obj. */
FILE_MAP_ALL_ACCESS, /* read-write access */
0, /* max. object size is */
0, /* size of hFile */
0); /* map entire file */

if (lpMapAddress == NULL) {
ErrorHandler("Could not map view of file.");
}

Note that MapViewOfFile returns different addresses in different processes. To specify the location of the file view within a process's address space, use the MapViewOfFileEx_win32_MapViewOfFileEx function, rather than MapViewOfFile.
The following example uses the UnmapViewOfFile_win32_UnmapViewOfFile function to invalidate the pointer to the mapped memory. This destroys the file view in the process's address space. If any pages of the file view changed since the view was mapped, UnmapViewOfFile also copies the changed pages to the physical file.
if (!UnmapViewOfFile(lpMapAddress)) {
ErrorHandler("Could not unmap view of file.");
}

The FlushViewOfFile_win32_FlushViewOfFile function copies the specified number of bytes of the file view to the physical file but does not destroy the file view:
if (!FlushViewOfFile(lpMapAddress, dwBytesToFlush)) {
ErrorHandler("Could not flush memory to disk.");
}

+$#A File Mapping Functions
The following functions are used with file mapping.
CreateFileMapping_win32_CreateFileMappingFlushViewOfFile_win32_FlushViewOfFileMapViewOfFile_win32_MapViewOfFileMapViewOfFileEx_win32_MapViewOfFileExOpenFileMapping_win32_OpenFileMappingUnmapViewOfFile_win32_UnmapViewOfFile

+$#AK File Systems
Applications based on the Microsoft® Win32® application programming interface (API) rely on file systems to store and retrieve information on mass storage devices. File systems provide the underlying support that applications need to create and access files and directories on the individual volumes associated with the devices.
+$#A About File Systems
Depending on the configuration of a computer, a Win32-based application may have access to volumes managed by any of the following file systems:
· New Technology file system (NTFS)
· File allocation table (FAT) file system
· Protected-mode FAT file system
· High-performance file system (HPFS)

For example, applications that connect to network drives may encounter file systems that range from FAT to NTFS. Because different volumes may be managed by different file systems, it is important to understand the differences between file systems and to prepare applications to work effectively with all available file systems. The following sections describe these differences and present guidelines for handling them within applications.
+$#A Guidelines
Each file system consists of one or more drivers and supporting dynamic-link libraries that define the data formats and features of the file system. These determine the conventions used for filenames, the level of security and recoverability available, and the general performance of input and output (I/O) operations. Before you access files and directories on a given volume, you should determine the capabilities of the file system by using the GetVolumeInformation_win32_GetVolumeInformation function. The function returns values, such as the maximum length of filenames, that you can use to adapt your application to work effectively with the file system.
In general, you should always avoid using static buffers for filenames and paths. Instead, use the values returned by GetVolumeInformation to allocate buffers as you need them. If you must use static buffers, reserve 256 characters for filenames and 260 characters for paths. These are the maximums currently recommended for Win32-based applications.
+$#A The New Technology File System
The New Technology file system (NTFS) organizes data on fixed disks. This file system supports object-oriented applications by treating all files as objects with user- and system-defined attributes. NTFS provides all the capabilities of the FAT and HPFS file systems without many of their limitations. Accessing files under NTFS is often faster than accessing similar files under the FAT or HPFS file systems.
NTFS is a fully recoverable file system. It is designed to restore consistency to a disk after a CPU failure, system crash, or I/O error. NTFS allows the operating system to recover without your having to use disk-checking utilities. However, NTFS provides these utilities in case recovery fails or corruption occurs outside the control of the file system. NTFS also includes features not present in HPFS or FAT, such as security, Unicode filenames, automatic creation of MS-DOS® aliases, multiple data streams, and unique functionality specific to the POSIX subsystem. For more information about security, see Security_win32_Security.
NTFS follows the filename conventions described in the HPFS section, but it also supports Unicode filenames.
NTFS cannot manipulate a file's extended attributes if the file was created on HPFS.
+$#A The File Allocation Table File System
The FAT file system organizes data on fixed disks and floppy disks. The distinguishing feature of the FAT file system is its filename convention. The filename convention consists of a filename (up to eight characters), a separating period (.), and a filename extension (up to three characters).
The main advantage of FAT volumes is that they are accessible by MS-DOS, Microsoft® Windows®, and OS/2 systems. FAT is also the only file system currently supported on floppy disks and other removable media.
Valid FAT filenames have the following form:
[[drive:]][[directory\]]filename[[.extension]]
The drive parameter must name an existing drive and can be any letter from A through Z. The drive letter must be followed by a colon (:).
The directory parameter specifies the directory that contains the file's directory entry. This value must be followed by a backslash (\) to separate it from the filename. If the specified directory is not in the current directory, directory must include the names of all directories in the file's path, separated by backslashes. The root directory is specified by using a backslash at the beginning of the name. For example, if the directory ABCD is in the directory SAMPLE and SAMPLE is in the root directory, the correct directory specification is \SAMPLE\ABCD. A directory name consists of any combination of up to eight letters, digits, or the following special characters:
$ % ' - _ @ { } ~ ` ! # ( )
A directory name can also have an extension that is any combination of up to three letters, digits, or special characters, preceded by a period (.).
The filename and extension parameters specify the file. Filename can be any combination of up to eight letters, digits, or the special characters previously listed; extension can be any combination of up to three letters, digits, or special characters, all preceded by a period. Filename can also include embedded (preceded and followed by one or more letters, digits, or special characters just noted) spaces. For example, the string "disk 1" is a legal value for filename.
FAT volumes do not distinguish between uppercase and lowercase letters.
+$#A The Protected-Mode FAT File System
The protected-mode FAT file system organizes data on fixed and floppy disks. Protected-mode FAT is compatible with the FAT file system, using file allocation tables and directory entries to store information about the contents of a disk. Protected-mode FAT also supports long filenames, storing these names and other information such as the date and time the file was last accessed in the FAT structures.
Protected-mode FAT allows filenames of up to 255 characters, including the terminating null character. This is similar to the New Technology file system (NTFS) which allows filenames of up to 256 characters.
Protected-mode FAT allows paths of up to 260 characters, including the terminating null character.
+$#A The High-Performance File System
HPFS organizes data on fixed disks. Filenames under HPFS can be any practical length (up to 254 characters) and can contain characters that are not valid for the FAT file system, such as spaces and periods. In many cases, accessing files under HPFS is faster than accessing similar files under the FAT file system.
Blank spaces can be used anywhere in an HPFS filename or directory name, but blank spaces and periods at the end of a filename are ignored. There is no requirement that HPFS filenames have extensions; however, many applications still create and use them.
The following special characters can also be used in HPFS filenames:
, + = [ ] ; _
An HPFS filename can be all uppercase, all lowercase, or mixed case. The case is preserved for directory listings but is ignored in file searches and all other system operations. Therefore, in a given directory, there cannot be more than one file with the same name when the only difference is case.

+$#AK Files
A file is the basic unit of storage that enables a computer to distinguish one set of information from another.
+$#A About Files
In Microsoft® Windows®, files are stored on storage media, such as disks or tapes, and can be organized into groups called directories. The Windows file input and output (I/O) functions enable applications to create, open, modify, and delete files and directories. They also enable applications to obtain system information, such as what disk drives are present.
+$#A System Organization
A volume is a storage device, such as a fixed disk or floppy disk, formatted to store directories and files. Each volume has a root directory. Directories and files on the volume are organized in a tree structure that starts at the root directory. Each directory entry identifies the name, attributes, location, and size of the file or subdirectory.
A large volume can be divided into more than one logical volume, also called a partition. To the user and to the operating system, each partition appears to be a separate volume.
A file system is operating system software that manages the low-level organization of files on a volume. Windows supports the following file systems:
· File allocation table (FAT)
· High-performance file system (HPFS)
· New Technology file system (NTFS)
The type of file system defines the filename conventions on a volume and may also provide specific file system features, such as security, recoverability, and high I/O performance. Each volume can use a different file system.
For more information on the FAT, HPFS, and NTFS file systems, see File Systems_win32_File_Systems.
+$#A Accessing Files
The first time a file function accesses a volume and whenever a volume is placed in a floppy-disk drive, Windows examines the volume to determine the appropriate file system. Thereafter, Windows manages all I/O to that volume through the device driver supporting the file system. If an error occurs, the driver returns it to Windows, which then passes it back to the application.
The Windows file functions enable applications to access files regardless of the underlying file system. However, capabilities may vary depending on the file system and/or operating system in use. For example, the CreateFile_win32_CreateFile function includes a security parameter that provides no security benefits for an application written for Windows running with current versions of MS-DOS.
The Windows file functions that create, open, and delete files and directories identify them by their names. These functions store or search for the file or directory in the current directory on the current disk drive, unless the name explicitly specifies a path to a different directory, disk drive, or both.
+$#A Filename Conventions
Although each file system can have specific rules about the formation of individual components in a directory or filename, all file systems follow the same general conventions for combining components. For example, the FAT file system requires file and directory names to have the 8.3-character filename format, the HPFS file system allows names to be up to 254 characters, and the NTFS file system allows names to be up to 255 characters. All three file systems use the backslash (\) character to separate directory names and the filename when forming a path.
General rules for applications creating names for directories and files or processing names supplied by the user include the following:
· Use any character in the current code page for a name, but do not use a path separator, a character in the range 0 through 31, or any character explicitly disallowed by the file system. A name can contain characters in the extended character set (128-255).
· Use the backslash (\), the forward slash (/), or both to separate components in a path. No other character is acceptable as a path separator.
· Use a period (.) as a directory component in a path to represent the current directory.
· Use two consecutive periods (..) as a directory component in a path to represent the parent of the current directory.
· Use a period (.) to separate components in a directory name or filename.
· Do not use the following characters in directory names or filenames, because they are reserved for Windows:
< > : " / \ |
· Do not use reserved words, such as aux, con, and prn, as filenames or directory names.
· Process a path as a null-terminated string. The maximum length for a path is given by MAX_PATH.
The wide (Unicode) versions of the CreateDirectory_win32_CreateDirectory, FindFirstFile_win32_FindFirstFile, GetFileAttributes_win32_GetFileAttributes, and SetFileAttributes_win32_SetFileAttributes functions permit paths that exceed the MAX_PATH length if the path has the "\\?\" or "\\?\UNC\" prefix. These prefixes direct the functions to turn off path parsing. Use the "\\?\" prefix with paths for local storage devices and the "\\?\UNC\" prefix with paths having the Universal Naming Convention format.
· Do not assume case sensitivity. Consider names such as OSCAR, Oscar, and oscar to be the same.

By following the rules listed in this section, an application can create valid names for files and directories regardless of the file system in use.
Long Filenames on Windows NT FAT Partitions
You can create or open long filenames on Windows NT FAT partitions.
The operating system stores the long filenames on disk in Unicode. This means that the original long file name is always preserved, even if it contains extended characters, and regardless of the code page that is active during a disk read or write operation.
The legal character set for these long filenames is the NTFS legal character set, less one character: the colon (':') that NTFS uses for opening alternate file streams. This means that you can freely copy files between NTFS and FAT partitions without losing any file name information.
The operating system stores the long filenames on disk masquerading as extra volume label directory entries. These entries are not available from the DOS dir command, but they are available from the Windows NT dir command. When you create a long filename on a Windows NT FAT partition, the operating system creates a corresponding short 8.3 form of the name, just as it does on an NTFS partition.
+$#A File Operations
This section describes the file I/O operations and operating system information provided by the file functions in the Microsoft® Win32® application programming interface (API).
+$#A Creating and Opening Files with the CreateFile Function
The CreateFile_win32_CreateFile function can create a new file or open an existing file. When an application uses this function, it must specify whether it will read from the file, write to the file, or both. The application must also specify what action to take whether or not the file exists. For example, an application can specify that CreateFile always be used to create the file. As a result, the function creates the file if it does not exist and overwrites the file if it does exist.
CreateFile also enables an application to specify whether it wants to share the file for reading, writing, both, or neither. A file that is not shared cannot be opened more than once by the first application nor by another application until the first application has closed.
Windows assigns a unique identifier, called a file handle, to each file that is opened or created. An application can use the file handle in functions that read from, write to, and describe the file. It is valid until the file is closed. When an application starts, it inherits all open file handles from the process that started it, if the handles are inheritable. For more information about processes, see Processes and Threads_win32_Processes_and_Threads.
For information about the standard input, standard output, and standard error file handles, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support.
An application should check the return value of CreateFile_win32_CreateFile before attempting to use the handle to access the file. If an error occurs, the application can use the GetLastError_win32_GetLastError function to get extended error information.
+$#A Closing and Deleting Files
To use operating system resources efficiently, an application should close files when they are no longer needed by using the CloseHandle_win32_CloseHandle function. If a file is open when an application terminates, Windows closes it automatically.
The DeleteFile_win32_DeleteFile function can be used to delete a file. The file must, however, be closed before any attempt to delete it.
+$#A Copying and Moving Files
Before a file can be copied, it must be closed or opened only for reading. No thread can have the file opened for writing. To copy an existing file to a new one, use the CopyFile_win32_CopyFile function. Applications can specify whether CopyFile fails if the new file already exists.
A file must also be closed before an application can move it. The MoveFile_win32_MoveFile function copies an existing file to a new location and deletes the original. Applications frequently use this function to rename files.
+$#A Reading from and Writing to a File
Every open file has a file pointer that specifies the next byte to be read or the location to receive the next byte written. When a file is opened for the first time, Windows places the file pointer at the beginning of the file. As each byte is read or written, Windows advances the file pointer. An application can also move the file pointer by using the SetFilePointer_win32_SetFilePointer function.
An application reads from and writes to a file by using the ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions. These functions require a handle of a file to be opened for reading and writing, respectively. ReadFile and WriteFile read and write a specified number of bytes at the location indicated by the file pointer. The data is read and written exactly as given; the functions do not format the data.
When the file pointer reaches the end of a file and the application attempts to read from the file, no error occurs, but no bytes are read. Therefore, reading zero bytes without an error means the program has reached the end of the file.
An application can truncate or extend a file by using the SetEndOfFile_win32_SetEndOfFile function. This function sets the end of file to the current position of the file pointer. A WriteFile of zero bytes does nothing.
When an application writes to a file, Windows usually collects the data being written in an internal buffer and writes the data to the disk on a regular basis.
An application can force the operating system to write the contents of the buffer to the disk by using the FlushFileBuffers_win32_FlushFileBuffers function. Alternatively, an application can specify that write operations are to bypass the internal buffer and write directly to the disk by setting a flag when the file is created or opened by using the CreateFile_win32_CreateFile function.
If there is data in the internal buffer when the file is closed, Windows does not automatically write the contents of the buffer to the disk before closing the file. If the application does not force the operating system to write the buffer to disk before closing the file, the caching algorithm determines when the buffer is written.
Locking and Unlocking Files
Although Windows allows more than one application to open a file and write to it, applications must not write over each other's work. An application can prevent this problem by temporarily locking a region in a file. The LockFile_win32_LockFile function locks a specified range of bytes in a file. The range may extend beyond the current end of the file. Locking part of a file prevents all other processes from reading or writing anywhere in the specified area. Attempts to read from or write to a region locked by another process always fail. The application unlocks the region by using the UnlockFile_win32_UnlockFile function. An application should unlock all locked areas before closing a file.
Synchronizing Input and Output
Asynchronous input and output is a type of I/O that allows some I/O functions to return immediately, even though an I/O request is still pending. Asynchronous I/O enables an application to continue with other processing and wait for the I/O to be completed at a later time. Asynchronous I/O is also called overlapped I/O.
The ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions enable an application to specify an OVERLAPPED_win32_OVERLAPPED_str structure that indicates where to position the file pointer before the read or write operation. The handle of the file being read from or written to must have been opened with the FILE_FLAG_OVERLAPPED flag. The OVERLAPPED structure can also contain an event handle on which the WaitForSingleObject_win32_WaitForSingleObject or WaitForMultipleObjects_win32_WaitForMultipleObjects function can wait.
An application can also wait on the file handle to synchronize the completion of an I/O operation, but doing so requires extreme caution. Each time an I/O operation is started, the operating system sets the file handle to the nonsignaled state. Each time an I/O operation is completed, the operating system sets the file handle to the signaled state. Therefore, if an application starts two I/O operations and waits on the file handle, there is no way to determine which operation is finished when the handle is set to the signaled state. If an application must perform multiple asynchronous I/O operations on a single file, it should wait on the event handle in the OVERLAPPED structure for each I/O operation, rather than on the file handle.
The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions enable an application to specify a routine to execute when the asynchronous I/O request is completed.
For more information about synchronization, see Synchronization_win32_Synchronization.
+$#A Searching for Files
An application can search the current directory for all filenames that match a given pattern by using the FindFirstFile_win32_FindFirstFile, FindNextFile_win32_FindNextFile, and FindClose_win32_FindClose functions. The pattern must be a valid filename and can include wildcard characters.
FindFirstFile creates a handle that FindNextFile uses to search for other files with the same pattern. Both functions also return information about the file that was found in a WIN32_FIND_DATA_win32_WIN32_FIND_DATA_str structure. This information includes the filename, size, attributes, and time. The FindClose function destroys the handle created by FindFirstFile.
An application can search for a single file on a specific path by using the SearchPath_win32_SearchPath function.
+$#A Creating Temporary Files
Applications can create temporary filenames with the GetTempFileName_win32_GetTempFileName function. The GetTempPath_win32_GetTempPath function retrieves the path to the directory where temporary files are to be created.
+$#A Getting Information About Files
Nine characteristics called attributes may be associated with a file. The attributes can be one or more of the following values.
Value Meaning
FILE_ATTRIBUTE_ARCHIVE The file is an archive file. Applications use this value to mark files for backup or removal.
FILE_ATTRIBUTE_DIRECTORY The file is a directory.
FILE_ATTRIBUTE_HIDDEN The file is hidden. It is not included in an ordinary directory listing.
FILE_ATTRIBUTE_NORMAL The file has no other attributes. This value is valid only if used alone.
FILE_ATTRIBUTE_READONLY The file is read-only. Applications can read from the file but cannot write to it or delete it.
FILE_ATTRIBUTE_SYSTEM The file is part of the operating system or is used exclusively by it.
FILE_ATTRIBUTE_TEMPORARY The file is being used for temporary storage. Applications can write to the file only if absolutely necessary. Most of the file's data remains in memory without being flushed to the media, because the file will soon be deleted.
FILE_ATTRIBUTE_ATOMIC_WRITE Reserved; do not use.
FILE_ATTRIBUTE_XACTION_WRITE Reserved; do not use.

An application can retrieve and set a file's attributes by using the GetFileAttributes_win32_GetFileAttributes and SetFileAttributes_win32_SetFileAttributes functions. Applications cannot, however, use SetFileAttributes to set the directory attribute.
The GetFileType_win32_GetFileType function returns the type of a file: disk, character (such as a console), pipe, or unknown. The GetFileSize_win32_GetFileSize function returns the size of a file. Applications can retrieve the full path of a file by using the GetFullPathName_win32_GetFullPathName function.
Applications can retrieve and set the date and time a file was created, last modified, or last accessed by using the GetFileTime_win32_GetFileTime and SetFileTime_win32_SetFileTime functions. For more information about file times, see Time_win32_Time.
+$#A Directory Operations
When an application creates a new file, the operating system adds it to the specified directory. Each directory can have any number of files, up to the physical limit of the disk. An application can create new directories and delete existing directories by using the CreateDirectory_win32_CreateDirectory and RemoveDirectory_win32_RemoveDirectory functions. An application cannot delete a directory unless it is empty.
The directory at the end of the active path is called the current directory; it is the directory in which the active application started, unless explicitly changed. An application can determine which directory is current by using the GetCurrentDirectory_win32_GetCurrentDirectory function. An application can change the current directory by using the SetCurrentDirectory_win32_SetCurrentDirectory function.
On the Windows NT platform, you can obtain a handle to a directory by calling the CreateFile_win32_CreateFile function with the FILE_FLAG_BACKUP_SEMANTICS flag set. You can pass a directory handle to some functions in place of a file handle.
An application can monitor the contents of a directory and its subdirectories by using the FindFirstChangeNotification_win32_FindFirstChangeNotification, FindNextChangeNotification_win32_FindNextChangeNotification, and FindCloseChangeNotification_win32_FindCloseChangeNotification functions. Waiting for a change notification is similar to having a read operation pending against a directory and, if necessary, its subdirectories. When something changes within the directory being watched, the read operation is completed. For example, an application can use these functions to update a directory listing whenever a filename within the monitored directory changes.
An application can specify a set of conditions that trigger a change notification by using the FindFirstChangeNotification function. The conditions include changes to filenames, directory names, attributes, file size, time of last write, and security. This function also returns a handle that can be waited on by using the WaitForSingleObject_win32_WaitForSingleObject or WaitForMultipleObjects_win32_WaitForMultipleObjects function. If the wait condition is satisfied, FindNextChangeNotification can be used to provide a notification handle to wait on subsequent changes.
The FindCloseChangeNotification function closes the notification handle.
+$#A Getting System Information
Windows provides several functions that applications can use to obtain information about the file system, operating system, and hardware.
The GetVolumeInformation_win32_GetVolumeInformation function retrieves information about the file system on a given volume. This information includes the volume name, volume serial number, file system name, file system flags, maximum length of a filename, and so on.
The GetSystemDirectory_win32_GetSystemDirectory and GetWindowsDirectory_win32_GetWindowsDirectory functions retrieve the paths to the Windows system directory and the Windows directory, respectively.
The GetDiskFreeSpace_win32_GetDiskFreeSpace function retrieves organizational information about a disk, including the number of bytes per sector, the number of sectors per cluster, the number of free clusters, and the total number of clusters.
The GetDriveType_win32_GetDriveType function indicates whether the disk referenced by the specified disk drive is a removable, fixed, CD-ROM, RAM, or network drive.
The GetLogicalDrives_win32_GetLogicalDrives function identifies the drives currently present on the operating system. The GetLogicalDriveStrings_win32_GetLogicalDriveStrings function retrieves a null-terminated string for each drive present on the operating system. Use these strings whenever a root directory is required.
+$#A File Compression
Windows NT release 3.51 supports file compression on an individual file basis for volumes that use the NTFS file system.
+$#A Compression Attribute
On volumes that use the NTFS file system, each file and directory has a compression attribute. Other file systems may also implement a compression attribute for individual files and directories.
You can determine whether a file system supports a compression attribute for files and directories by calling the GetVolumeInformation_win32_GetVolumeInformation function and examining the FS_FILE_COMPRESSION bit flag.
If a file's compression attribute is set, all of the data in the file is compressed. If the attribute is clear, none of the data in the file is compressed. There is no partially compressed state. The compression attribute is a simple Boolean indicator of compression state.
A directory's compression attribute provides a default compression attribute for newly created files and subdirectories. When you call CreateFile_win32_CreateFile or CreateDirectory_win32_CreateDirectory to create a new file or directory, the new file or directory inherits the compression attribute of its parent directory.
+$#A Compression State
Each file and directory on a volume that supports compression for individual files and directories has a compression state. Windows NT release 3.51 provides such support for volumes that use the NTFS file system.
Whereas the compression attribute of a file or directory indicates simply whether the file or directory is compressed or not compressed, the compression state also specifies the format of any compressed data.
Compression state is encoded as a 16-bit value. A compression state value of zero indicates that a file is not compressed. A value of one indicates that a file is compressed, using the default compression format for its parent directory. Any other value indicates that a file is compressed, using the compression format encoded by the compression state value.
For directories, the compression state provides a default compression state for newly created files and subdirectories. In other words, new files and directories inherit the compression state of their parent directory.
+$#A Determining Compression Attribute and State
Use the GetFileAttributes_win32_GetFileAttributes function to determine the compression attribute of a file or directory.
Use the FSCTL_GET_COMPRESSION DeviceIoControl_win32_DeviceIoControl operation to determine the compression state of a file or directory.
+$#A Setting Compression Attribute and State
On volumes whose file system supports the compression of files or directories, such as NTFS volumes under Windows NT release 3.51, new files and subdirectories inherit the compression attribute and compression state of their parent directory.
Use the FSCTL_SET_COMPRESSION DeviceIoControl_win32_DeviceIoControl operation to set the compression state of a file or directory. This operation also sets the compression attribute of the file or directory.
Setting the compression state of a file to a nonzero value compresses the file, using the compression format encoded by the compression state value. Setting a file's compression state to zero decompresses the file. These are synchronous operations. The file is compressed or decompressed immediately when you set its compression state.
Setting a directory's compression state does not cause any immediate compression or decompression. Instead, setting a directory's compression state sets a default compression state that will be given to all newly created files and subdirectories.
+$#A Obtaining the Size of a Compressed File
Use the GetCompressedFileSize_win32_GetCompressedFileSize function to obtain the compressed size of a file. If the file is compressed, its compressed size will be less than its uncompressed size. Use the GetFileSize_win32_GetFileSize).function to determine the uncompressed size of a file.
+$#A Using Files
The Windows file functions enable applications to access files regardless of the underlying file system. However, their functionality may vary depending on the operating system in use. If you have questions about a specific function, consult the reference information for that function.
For functions that manipulate files, the filenames may be relative to the current directory. A filename is relative to the current directory if it does not begin with a disk designator or directory name separator, such as a backslash (\). If the filename begins with a disk designator, it is a fully qualified path.
The examples in this section illustrate how to perform the following tasks:
· Creating and opening files_win32_Creating_and_Opening_Files
· Reading, writing, and locking files_win32_Reading_Writing_and_Locking_Files
· Creating and using a temporary file_win32_Creating_and_Using_a_Temporary_File
· Searching for files and changing file attributes_win32_Searching_for_Files_and_Changing_File_Attributes
· Monitoring changes in a directory or directory tree_win32_Monitoring_Changes_in_a_Directory_or_Directory_Tree
· Testing for the end of the file_win32_Testing_for_the_End_of_the_File
· Retrieving a file pointer_win32_Retrieving_a_File_Pointer

+$#A Creating and Opening Files
You can use the CreateFile_win32_CreateFile function to create a new file or open an existing file. You must specify the filename, preferred access, share mode, creation instructions, and attributes.
The following example returns a handle of the current console input buffer.
HANDLE hReadWriteInput;

hReadWriteInput = CreateFile("CONIN$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, /* share with cmd. */
NULL,
OPEN_EXISTING, /* open already created console */
0, /* normal file open */
NULL
);

For more information about consoles, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support.
In the following example, CreateFile_win32_CreateFile opens an existing file for reading.
HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", /* open MYFILE.TXT */
GENERIC_READ, /* open for reading */
FILE_SHARE_READ, /* share for reading */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
OPEN_EXISTING, /* existing file only */
FILE_ATTRIBUTE_NORMAL, /* normal file */
(HANDLE) NULL); /* no attr. template */

if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open file."); /* process error */
}

In this example, CreateFile_win32_CreateFile succeeds only if a file named MYFILE.TXT already exists in the current directory. An application should check the return value of CreateFile before attempting to use the handle to access the file. If an error occurs, the application should use the GetLastError_win32_GetLastError function to get extended error information and respond accordingly.
A file must be closed before it can be deleted. The following lines close and delete the MYFILE.TXT file.
CloseHandle(hFile);
DeleteFile("MYFILE.TXT");

In the following example, CreateFile_win32_CreateFile creates a new file and opens it for writing.
HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", /* create MYFILE.TXT */
GENERIC_WRITE, /* open for writing */
0, /* do not share */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
CREATE_ALWAYS, /* overwrite existing */
FILE_ATTRIBUTE_NORMAL | /* normal file */
FILE_FLAG_OVERLAPPED, /* asynchronous I/O */
(HANDLE) NULL); /* no attr. template */
if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open file."); /* process error */
}

In addition to the standard attributes (read only, hidden, system, and so on), you can also specify security attributes by including a pointer to a SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure as the fourth parameter. However, the underlying operating system must support security for these attributes for them to have any effect. For more information about security, see Security_win32_Security.
+$#A Reading, Writing, and Locking Files
The ReadFile_win32_ReadFile function requires a file handle that is open for reading, or reading and writing. ReadFile copies a specified number of bytes, up to the end of the file, to a specified buffer. It returns the actual number of bytes read in a variable specified by its fourth parameter.
The WriteFile_win32_WriteFile function requires a file handle that is open for writing, or writing and reading. WriteFile copies a specified number of bytes, up to the end of the buffer, to a specified file. It returns the actual number of bytes written in a variable specified by its fourth parameter.
The following example illustrates a possible flow for using callback completion asynchronous I/O.
Completion Routine:
VOID IoCompletionRoutine(DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
/* If an I/O error occurred, display the error and then exit. */

if (dwErrorCode) {
printf("FATAL I/O Error %ld I/O Context %lx.%lx\n",
dwErrorCode, lpOverlapped, lpOverlapped->hEvent);
ExitProcess(dwErrorCode);
}
LocalFree(lpOverlapped);
}

Main Thread:
VOID IoWorkerThread(VOID)
{
HANDLE HandleVector[2];
DWORD CompletionStatus;
PIOREQUEST IoRequestPacket;
LPOVERLAPPED Overlapped;
BOOL IoOperationStatus;

HandleVector[0] = IoWorkerListLock;
HandleVector[1] = IoWorkerListSemaphore;

for(;;) {

/* Do an alertable wait on the handle vector. Both objects */
/* being signaled at the same time means that there is an */
/* I/O request in the queue and the caller has exclusive */
/* access to the queue. */

CompletionStatus = WaitForMultipleObjectsEx(2, HandleVector,
TRUE, INFINITE, TRUE);

/* If the wait failed, error out. */

if (CompletionStatus == 0xFFFFFFFF) {
printf("FATAL WAIT ERROR %ld\n", GetLastError());
ExitProcess(1);
}
/* If an I/O completion occurred, wait for another */
/* I/O request or I/O completion. */

if (CompletionStatus != WAIT_IO_COMPLETION) {

/* The wait was satisfied. Ownership of the I/O */
/* request queue is exclusive, and there is something in */
/* the queue. To insert something in the queue, the */
/* inserter gets the list lock (mutex), inserts an entry, */
/* signals the list semaphore, and finally releases the */
/* list lock. */

IoRequestPacket = RemoveHeadList(&IoRequestList);

ReleaseMutex(IoWorkerListLock);

/* Allocate an overlapped structure. */

Overlapped = LocalAlloc(LMEM_ZEROINIT,
sizeof(OVERLAPPED));

if (!Overlapped) {
printf("FATAL allocation error\n");
ExitProcess(1);
}

Overlapped->Offset = IoRequestPacket->Offset;
Overlapped->OffsetHigh = IoRequestPacket->OffsetHigh;
Overlapped->hEvent =
IoRequestPacket->dwAdditionalIoContext;

if (IoRequestPacket->bRead) {
IoOperationStatus =
ReadFileEx(IoRequestPacket->hFile,
IoRequestPacket->lpBuffer,
IoRequestPacket->dwTransferCount,
Overlapped, IoCompletionRoutine);
}
else {
IoOperationStatus =
WriteFileEx(IoRequestPacket->hFile,
IoRequestPacket->lpBuffer,
IoRequestPacket->dwTransferCount,
Overlapped,
IoCompletionRoutine);
}

/* Test to see if the I/O was queued successfully. */

if (!IoOperationStatus) {
printf("FATAL I/O Error %ld I/O Context %lx.%lx\n",
GetLastError(), Overlapped, Overlapped->hEvent);
ExitProcess(1);
}

/* The I/O queued successfully. Go back into the */
/* alertable wait for I/O completion or for */
/* more I/O requests. */

}
}
}

The SetFilePointer_win32_SetFilePointer function moves the file pointer a specified number of bytes, relative to the beginning or end of the file, or the file pointer's current position. If a positive number of bytes is specified, SetFilePointer moves the file pointer toward the beginning of the file; a negative value moves the pointer toward the end of the file.
The following example appends one file to the end of another file. The application opens two files by using CreateFile_win32_CreateFile: ONE.TXT is opened for reading, and TWO.TXT is opened for writing. Then the application appends the contents of ONE.TXT to the end of TWO.TXT by reading and writing 4K blocks by using ReadFile_win32_ReadFile and WriteFile_win32_WriteFile. Before writing to the second file, the application sets the second file's pointer to the end of the file by using SetFilePointer and locks the area to be written by using LockFile_win32_LockFile. This prevents another process from accessing the area while the write is in progress. After each write operation, UnlockFile_win32_UnlockFile unlocks the locked area.
HANDLE hFile;

HANDLE hAppend;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char buff[4096];

/* Open the existing file. */

hFile = CreateFile("ONE.TXT", /* open ONE.TXT */
GENERIC_READ, /* open for reading */
0, /* do not share */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
OPEN_EXISTING, /* existing file only */
FILE_ATTRIBUTE_NORMAL, /* normal file */
(HANDLE) NULL); /* no attr. template */
if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open ONE."); /* process error */
}

/*
* Open the existing file, or if the file does not exist,
* create a new file.
*/

hAppend = CreateFile("TWO.TXT", /* open TWO.TXT */
GENERIC_WRITE, /* open for writing */
0, /* do not share */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
OPEN_ALWAYS, /* open or create */
FILE_ATTRIBUTE_NORMAL, /* normal file */
(HANDLE) NULL); /* no attr. template */
if (hAppend == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open TWO."); /* process error */
}

/*
* Append the first file to the end of the second file.
* Lock the second file to prevent another process from
* accessing it while writing to it. Unlock the
* file when writing is finished.
*/

do {
if (ReadFile(hFile, (LPSTR) buff, 4096, &dwBytesRead, NULL)) {
dwPos = SetFilePointer(hAppend, 0, (LPLONG) NULL, FILE_END);
LockFile(hAppend, dwPos, 0, dwPos + dwBytesRead, 0);
WriteFile(hAppend, (LPSTR) buff, dwBytesRead,
&dwBytesWritten, NULL);
UnlockFile(hAppend, dwPos, 0, dwPos + dwBytesRead, 0);
}
} while (dwBytesRead == 4096);

/* Close both files. */

CloseHandle(hFile);
CloseHandle(hAppend);

+$#A Creating and Using a Temporary File
The following example copies one file to another. The second file is an uppercase version of the first file.
The application opens the ORIGINAL.TXT file by using CreateFile_win32_CreateFile. The application then obtains a temporary filename with the GetTempFileName_win32_GetTempFileName function and uses CreateFile to create the temporary file. The application reads 4K blocks into a buffer, converts the buffer contents to uppercase, and writes the converted buffer to the temporary file. When all of ORIGINAL.TXT has been written to the temporary file, the application closes both files and renames the temporary file to ALLCAPS.TXT by using the MoveFile_win32_MoveFile function.
HANDLE hFile;

HANDLE hTempFile;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char szTempName[MAX_PATH];

char buffer[4096];

/* Open the existing file. */

hFile = CreateFile("ORIGINAL.TXT", /* filename */
GENERIC_READ, /* open for reading */
0, /* do not share */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
OPEN_EXISTING, /* existing file only */
FILE_ATTRIBUTE_NORMAL, /* normal file */
(HANDLE) NULL); /* no attr. template */
if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open file."); /* process err. on fail*/
}

/* Create a temporary file. */

GetTempFileName("\\TEMP", /* dir. for temp. files */
"NEW", /* temp. filename prefix */
0, /* create unique name w/ sys. time */
(LPTSTR) szTempName); /* buffer for name */
hTempFile = CreateFile((LPTSTR) szTempName, /* filename */
GENERIC_READ | GENERIC_WRITE, /* open for read-write */
0, /* do not share */
(LPSECURITY_ATTRIBUTES) NULL, /* no security */
CREATE_ALWAYS, /* overwrite existing */
FILE_ATTRIBUTE_NORMAL, /* normal file */
(HANDLE) NULL); /* no attr. template */
if (hTempFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not create temporary file.");
}

/*
* Read 4K blocks to the buffer.
* Change all characters in the buffer to uppercase.
* Write the buffer to the temporary file.
*/

do {
if (ReadFile(hFile, (LPSTR) buffer, 4096,
&dwBytesRead, NULL)) {

CharUpperBuff((LPTSTR) buffer, dwBytesRead);

WriteFile(hTempFile, (LPSTR) buffer, dwBytesRead,
&dwBytesWritten, NULL);
}
} while (dwBytesRead == 4096);

/* Close both files. */

CloseHandle(hFile);
CloseHandle(hTempFile);

/* Move the temporary file to the new text file. */

if (!MoveFile(szTempName, "ALLCAPS.TXT")) {
ErrorHandler("Could not move temp. file.");
}

+$#A Searching for Files and Changing File Attributes
The following example copies all text files in the current directory to a new directory of read-only files named \TEXTRO. Files in the new directory are changed to read only, if necessary.
The application uses the GetCurrentDirectory_win32_GetCurrentDirectory function to retrieve the current directory path. This function is also used to return to the current directory after changing to the \TEXTRO directory.
The application then creates the \TEXTRO directory by using the CreateDirectory_win32_CreateDirectory function.
The application searches the current directory for all .TXT files by using the FindFirstFile_win32_FindFirstFile and FindNextFile_win32_FindNextFile functions. Each .TXT file is copied to the \TEXTRO directory. After a file is copied, the GetFileAttributes_win32_GetFileAttributes function determines whether the file is read only. If the file is not read only, the application changes directories to \TEXTRO and converts the copied file to read only by using the SetFileAttributes_win32_SetFileAttributes function.
After all .TXT files in the current directory have been copied, the application closes the search handle by using the FindClose_win32_FindClose function.
WIN32_FIND_DATA FileData;
HANDLE hSearch;
DWORD dwAttrs;
char szDirPath[] = "c:\\TEXTRO\\";
char szNewPath[MAX_PATH];
char szHome[MAX_PATH];

BOOL fFinished = FALSE;

/* Create a new directory. */

if (!CreateDirectory(szDirPath, NULL)) {
ErrorHandler("Couldn't create new directory.");
}

/* Start searching for .TXT files in the current directory. */

hSearch = FindFirstFile("*.txt", &FileData);
if (hSearch == INVALID_HANDLE_VALUE) {
ErrorHandler("No .TXT files found.");
}

/*
* Copy each .TXT file to the new directory
* and change it to read only, if not already.
*/

while (!fFinished) {
lstrcpy(szNewPath, szDirPath);
lstrcat(szNewPath, FileData.cFileName);
if (CopyFile(FileData.cFileName, szNewPath, FALSE)) {
dwAttrs = GetFileAttributes(FileData.cFileName);
if (!(dwAttrs & FILE_ATTRIBUTE_READONLY)) {
SetFileAttributes(szNewPath,
dwAttrs | FILE_ATTRIBUTE_READONLY);
}
}
else {
ErrorHandler("Couldn't copy file.");
}

if (!FindNextFile(hSearch, &FileData))
if (GetLastError() == ERROR_NO_MORE_FILES) {
MessageBox(hwnd, "No more .TXT files.",
"Search completed.", MB_OK);
fFinished = TRUE;
}
else {
ErrorHandler("Couldn't find next file.");
}
}

/* Close the search handle. */

if (!FindClose(hSearch)) {
ErrorHandler("Couldn't close search handle.");
}

+$#A Monitoring Changes in a Directory or Directory Tree
The following example monitors the directory tree starting at C:\ for directory name changes. It also monitors the C:\WINDOWS directory for filename changes.
The example uses the FindFirstChangeNotification_win32_FindFirstChangeNotification function to create two notification handles and the WaitForMultipleObjects_win32_WaitForMultipleObjects function to wait on the handles. Whenever a directory is created or deleted in the tree starting at C:\ , the example updates the entire directory tree. Whenever a file is created or deleted in the C:\WINDOWS directory, the example refreshes the WINDOWS directory. The FindNextChangeNotification_win32_FindNextChangeNotification function restarts the change notification each time the example processes a change.
DWORD dwWaitStatus;
HANDLE dwChangeHandles[2];

/*
* Watch the C:\WINDOWS directory for file creation and
* deletion.
*/

dwChangeHandles[0] = FindFirstChangeNotification(
"C:\\WINDOWS", /* directory to watch */
FALSE, /* do not watch the subtree */
FILE_NOTIFY_CHANGE_FILE_NAME); /* watch filename changes */

if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
ExitProcess(GetLastError());

/*
* Watch the C:\ subtree for directory creation and
* deletion.
*/

dwChangeHandles[1] = FindFirstChangeNotification(
"C:\\", /* directory to watch */
TRUE, /* watch the subtree */
FILE_NOTIFY_CHANGE_DIR_NAME); /* watch dir. name changes */

if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)
ExitProcess(GetLastError());

/*
* Change notification is set. Now wait on both notification
* handles and refresh accordingly.
*/

while (TRUE) {

/* Wait for notification. */

dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,
FALSE, INFINITE);

switch (dwWaitStatus) {

case WAIT_OBJECT_0:

/*
* A file was created or deleted in C:\WINDOWS.
* Refresh this directory and restart the
* change notification. RefreshDirectory is an
* application-defined function.
*/

RefreshDirectory("C:\\WINDOWS")
if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)
ExitProcess(GetLastError());
break;

case WAIT_OBJECT_0 + 1:

/*
* A directory was created or deleted in C:\.
* Refresh the directory tree and restart the
* change notification. RefreshTree is an
* application-defined function.
*/

RefreshTree("C:\\");
if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)
ExitProcess(GetLastError());
break;

default:
ExitProcess(GetLastError());
}
}

+$#A Testing for the End of the File
The ReadFile_win32_ReadFile function checks for the end-of-file condition (eof) differently for synchronous and asynchronous read operations. When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets the variable pointed to by lpNumberOfBytesRead to zero. An asynchronous read operation can encounter the end of a file during the initiating call to ReadFile or during subsequent asynchronous operation.
The test for end-of-file during a synchronous read operation is simple, as shown in the following example:
// attempt a synchronous read operationbResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);// check for eofif (bResult && nBytesRead == 0, ) { // we're at the end of the file}
The test for end-of-file during an asynchronous read operation is more difficult. There are three end-of-file indicators for asynchronous read operations:
· ReadFile_win32_ReadFile returns FALSE and GetLastError_win32_GetLastError returns ERROR_HANDLE_EOF.
· ReadFile returns FALSE and GetLastError returns ERROR_IO_PENDING.
· GetOverLappedResult returns FALSE and GetLastError returns ERROR_HANDLE_EOF.

The following example shows how to test for an end-of-file during an asynchronous read operation:
// attempt to initiate an asynchronous read operationbResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);// if there was a problem ...if (!bResult) { switch (dwError = GetLastError()) { case ERROR_HANDLE_EOF: { // we're at the end of the file } case ERROR_IO_PENDING: { // we're at the end of the file } // other error cases }} . . .// check on an asynchronous read operationbResult = GetOverlappedResult(hFile, &gOverlapped, &nBytesRead, &gOverlapped) ;// if there was a problem ...if (!bResult) { switch (dwError = GetLastError()) { case ERROR_HANDLE_EOF: { // we're at the end of the file } // other error cases }}
+$#A Retrieving a File Pointer
The following example retrieves the current position of the file pointer in the file specified by the hFile value.
HANDLE hFile;

DWORD dwCurrentFilePosition;

dwCurrentFilePosition = SetFilePointer(
hFile, /* must have GENERIC_READ and/or GENERIC_WRITE */
0, /* do not move pointer */
NULL, /* hFile is not large enough to need this pointer */
FILE_CURRENT); /* provides offset from current position */

+$#A File Functions
Following are the functions used with files.
_hread_win32__hread_hwrite_win32__hwrite_lclose_win32__lclose_lcreat_win32__lcreat_llseek_win32__llseek_lopen_win32__lopen_lread_win32__lread_lwrite_win32__lwriteAreFileApisANSI_win32_AreFileApisANSICopyFile_win32_CopyFileCreateDirectory_win32_CreateDirectoryCreateDirectoryEx_win32_CreateDirectoryExCreateFile_win32_CreateFileCreateIoCompletionPort_win32_CreateIoCompletionPortDefineDosDevice_win32_DefineDosDeviceDeleteFile_win32_DeleteFileFileIOCompletionRoutine_win32_FileIOCompletionRoutineFindClose_win32_FindCloseFindCloseChangeNotification_win32_FindCloseChangeNotificationFindFirstChangeNotification_win32_FindFirstChangeNotificationFindFirstFile_win32_FindFirstFileFindNextChangeNotification_win32_FindNextChangeNotificationFindNextFile_win32_FindNextFileFlushFileBuffers_win32_FlushFileBuffersGetBinaryType_win32_GetBinaryTypeGetCurrentDirectory_win32_GetCurrentDirectoryGetDiskFreeSpace_win32_GetDiskFreeSpaceGetDriveType_win32_GetDriveTypeGetFileAttributes_win32_GetFileAttributesGetFileInformationByHandle_win32_GetFileInformationByHandleGetFileSize_win32_GetFileSizeGetFileType_win32_GetFileTypeGetFullPathName_win32_GetFullPathNameGetLogicalDrives_win32_GetLogicalDrivesGetLogicalDriveStrings_win32_GetLogicalDriveStringsGetQueuedCompletionStatus_win32_GetQueuedCompletionStatusGetShortPathName_win32_GetShortPathNameGetTempDrive_win32_GetTempDriveGetTempFileName_win32_GetTempFileNameGetTempPath_win32_GetTempPathGetVolumeInformation_win32_GetVolumeInformationLockFile_win32_LockFileLockFileEx_win32_LockFileExMoveFile_win32_MoveFileMoveFileEx_win32_MoveFileExOpenFile_win32_OpenFileQueryDosDevice_win32_QueryDosDeviceReadFile_win32_ReadFileReadFileEx_win32_ReadFileExRemoveDirectory_win32_RemoveDirectorySearchPath_win32_SearchPathSetCurrentDirectory_win32_SetCurrentDirectorySetEndOfFile_win32_SetEndOfFileSetFileApisToANSI_win32_SetFileApisToANSISetFileApisToOEM_win32_SetFileApisToOEMSetFileAttributes_win32_SetFileAttributesSetFilePointer_win32_SetFilePointerSetHandleCount_win32_SetHandleCountSetVolumeLabel_win32_SetVolumeLabelUnlockFile_win32_UnlockFileUnlockFileEx_win32_UnlockFileExWriteFile_win32_WriteFileWriteFileEx_win32_WriteFileEx

+$#AK Handles and Objects
An object is an internal structure that represents a system resource, such as a file, a thread, or a graphic image. An application cannot directly access the internal structure of an object or the system resource that an object represents. Instead, an application must obtain an object handle and use this handle to examine or modify the system resource (or both). In the Microsoft® Win32® application programming interface (API), handles are usually implemented as indirect pointers, but this is not always the case.
+$#A About Handles and Objects
Windows uses objects and handles to regulate access to system resources for two main reasons. First, the use of objects ensures that developers are not writing code specifically to low-level, internal structures. This enables Microsoft to add or change functionality of the operating system, as long as the original calling conventions are maintained. When subsequent versions of the operating system are released, applications will gain this new functionality with little or no additional development.
Second, the use of objects enables developers to take advantage of Win32 security. Each object has its own access-control list (ACL) that specifies the types of actions processes can perform on the object. The operating system examines an object's ACL each time an application attempts to create a handle to the object. For more information about security, see Security_win32_Security.
For most objects, the Win32 API provides functions that create the object, create an object handle, close the object handle, and destroy the object. These tasks may be combined or unnecessary, depending on the type of object and the situation. For example, an application could create an event object. Other applications could open the event and each would have a unique handle to the same event object. In this scenario, as the applications finish using the object, each closes its handle. When there are no open handles to the event object, the operating system removes the object from memory.
In contrast, an application could obtain the existing window-object handle. In this instance, when the window object is no longer needed, the application must remove the object from memory, which invalidates the window handle.
When a process terminates, the system automatically closes handles and deletes objects created by the process. However, when a thread terminates, the system usually does not close handles or delete objects. The only exceptions are window, hook, window position, and dynamic data exchange (DDE) conversation objects that are deleted when the creating thread terminates.
Handles and objects consume memory. Therefore, to preserve system performance, an application should close handles and delete objects as soon as they are no longer needed. Applications that do not do this can slow the operating system, due to excessive use of the paging file.
Windows provides three categories of objects: user, graphics device interface (GDI), and kernel, as shown in the following tables. The system uses user objects to support window management, GDI objects to support graphics, and kernel objects to support memory management, process execution, and interprocess communications (IPC). For information about creating and using a specific object, refer to the associated overview.
Windows User Objects
User object Overview
Accelerator table Keyboard Accelerators_win32_Keyboard_Accelerators
Caret Carets_win32_Carets
Cursor Cursors_win32_Cursors
Dynamic data exchange conversation Dynamic Data Exchange Management Library_win32_Dynamic_Data_Exchange_Management_Library
Desktop Security_win32_Security
Hook Hooks_win32_Hooks
Icon Icons_win32_Icons
Menu Menus (Windows NT)_win32_Menus_Windows_NT_
Window Windows_win32_Windows
Window position Windows_win32_Windows
Window station Windows_win32_Windows

Windows GDI Objects
GDI object Overview
Bitmap Bitmaps_win32_Bitmaps
Brush Brushes_win32_Brushes
Font Fonts and Text_win32_Fonts_and_Text
Palette Colors_win32_Colors
Pen Pens_win32_Pens
Extended pen Pens_win32_Pens
Region Regions_win32_Regions
Device context Device Contexts_win32_Device_Contexts
Memory device context Device Contexts_win32_Device_Contexts
Metafile Metafiles_win32_Metafiles
Metafile device context Metafiles_win32_Metafiles
Enhanced metafile Metafiles_win32_Metafiles
Enhanced-metafile device context Metafiles_win32_Metafiles

Windows Kernel Objects
Kernel object Overview
Process Processes and Threads_win32_Processes_and_Threads
Thread Processes and Threads_win32_Processes_and_Threads
File Files_win32_Files
File mapping File Mapping_win32_File_Mapping
Event Synchronization_win32_Synchronization
Semaphore Synchronization_win32_Synchronization
Mutex Synchronization_win32_Synchronization
Pipe (named and anonymous) Pipes_win32_Pipes
Mailslot Mailslots_win32_Mailslots
Communications device Communications_win32_Communications
Heap Memory Management_win32_Memory_Management
Module Dynamic-Link Libraries_win32_Dynamic_Link_Libraries
Update resource Resources_win32_Resources
Find file Files_win32_Files
Event log Event Logging_win32_Event_Logging
Change notification Files_win32_Files
Token Security_win32_Security
Service object Printing and Print Spooler_win32_Printing_and_Print_Spooler
LZ file Data Decompression Library_win32_Data_Decompression_Library

+$#A Handle Limitations
Some objects, such as user and GDI objects, support only one handle at a time. The system provides the handle when an application creates the object and invalidates the handle when the application destroys the object. Other objects, such as some kernel objects, support multiple handles to a single object. The operating system automatically removes the object from memory after the last handle to the object is closed.
The total number of open handles in the operating system is limited only by the amount of memory available to the operating system. However, a single process may have no more than 16,384 open GDI object handles. The per-process limit on kernel handles is 2^30. There is no per-process limit on user handles, but there is a systemwide limit of 65,536.
+$#A User and Graphics Device Interface Objects
User and GDI objects support only one handle per object. Processes cannot inherit or duplicate handles to user or GDI objects.
Handles to user objects are public to all processes. That is, any process can use the user object handle, provided that the process has access to the object as defined by user security. For more information about security, see Security_win32_Security.
Handles to GDI objects are private to a process. That is, only the process that created the GDI object can use the object handle.
In the following illustration, an application creates a window object. The CreateWindow_win32_CreateWindow function creates the window object and returns an object handle.
{ewc msdncd, EWGraphic, group10750 0 /a "SDK_01.bmp"}
After the window object has been created, the application can use the window handle to display or change the window. The handle remains valid until the window object is destroyed.
In the next illustration, the application destroys the window object. The DestroyWindow_win32_DestroyWindow function removes the window object from memory, which invalidates the window handle.
{ewc msdncd, EWGraphic, group10750 1 /a "SDK_02.bmp"}
The following table lists the Windows user objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or simply return the existing object handle. The destructor functions remove the object from memory, which invalidates the object handle.
Note that the window station and desktop objects are created and maintained by the system; applications cannot delete these objects.
User Objects
Object Creator function Destructor function
Accelerator table CreateAcceleratorTable_win32_CreateAcceleratorTable DestroyAcceleratorTable_win32_DestroyAcceleratorTable
Cursor CreateCursor_win32_CreateCursor, LoadCursor_win32_LoadCursor, GetCursor_win32_GetCursor, SetCursor_win32_SetCursor DestroyCursor_win32_DestroyCursor
DDE conversation DdeConnect_win32_DdeConnect, DdeConnectList_win32_DdeConnectList, DdeQueryNextServer_win32_DdeQueryNextServer, DdeReconnect_win32_DdeReconnect DdeDisconnect_win32_DdeDisconnect, DdeDisconnectList_win32_DdeDisconnectList
Desktop GetThreadDesktop_win32_GetThreadDesktop -
Hook SetWindowsHook_win32_SetWindowsHook, SetWindowsHookEx_win32_SetWindowsHookEx UnhookWindowsHook_win32_UnhookWindowsHook, UnhookWindowsHookEx
Menu CreateMenu_win32_CreateMenu, CreatePopupMenu_win32_CreatePopupMenu, GetMenu_win32_GetMenu, GetSubMenu_win32_GetSubMenu, GetSystemMenu_win32_GetSystemMenu, LoadMenu_win32_LoadMenu, LoadMenuIndirect_win32_LoadMenuIndirect DestroyMenu_win32_DestroyMenu
Window CreateWindow_win32_CreateWindow, CreateWindowEx_win32_CreateWindowEx, CreateDialogParam_win32_CreateDialogParam, CreateDialogIndirectParam_win32_CreateDialogIndirectParam, CreateMDIWindow_win32_CreateMDIWindow, FindWindow_win32_FindWindow, GetWindow_win32_GetWindow, GetClipboardOwner_win32_GetClipboardOwner, GetDesktopWindow_win32_GetDesktopWindow, GetDlgItem_win32_GetDlgItem, GetForegroundWindow_win32_GetForegroundWindow, GetLastActivePopup_win32_GetLastActivePopup, GetOpenClipboardWindow_win32_GetOpenClipboardWindow, GetTopWindow_win32_GetTopWindow, WindowFromDC_win32_WindowFromDC, WindowFromPoint_win32_WindowFromPoint, and others DestroyWindow_win32_DestroyWindow
Window position BeginDeferWindowPos_win32_BeginDeferWindowPos EndDeferWindowPos_win32_EndDeferWindowPos
Window station GetProcessWindowStation_win32_GetProcessWindowStation -

The following table lists the Windows GDI objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or simply return the existing object handle. The destructor functions remove the object from memory, which invalidates the object handle.
GDI Objects
Object Creator function Destructor function
Bitmap CreateBitmap_win32_CreateBitmap, CreateBitmapIndirect_win32_CreateBitmapIndirect, CreateCompatibleBitmap_win32_CreateCompatibleBitmap, CreateDIBitmap_win32_CreateDIBitmap, CreateDIBSection_win32_CreateDIBSection, CreateDiscardableBitmap_win32_CreateDiscardableBitmap DeleteObject_win32_DeleteObject
Brush CreateBrushIndirect_win32_CreateBrushIndirect, CreateDIBPatternBrush_win32_CreateDIBPatternBrush, CreateDIBPatternBrushPt_win32_CreateDIBPatternBrushPt, CreateHatchBrush_win32_CreateHatchBrush, CreatePatternBrush_win32_CreatePatternBrush, CreateSolidBrush_win32_CreateSolidBrush DeleteObject_win32_DeleteObject
Font CreateFont_win32_CreateFont, CreateFontIndirect_win32_CreateFontIndirect DeleteObject_win32_DeleteObject
Palette CreatePalette_win32_CreatePalette DeleteObject_win32_DeleteObject
Pen CreatePen_win32_CreatePen, CreatePenIndirect_win32_CreatePenIndirect DeleteObject_win32_DeleteObject
Extended pen ExtCreatePen_win32_ExtCreatePen DeleteObject_win32_DeleteObject
Region CombineRgn_win32_CombineRgn, CreateEllipticRgn_win32_CreateEllipticRgn, CreateEllipticRgnIndirect_win32_CreateEllipticRgnIndirect, CreatePolygonRgn_win32_CreatePolygonRgn, CreatePolyPolygonRgn_win32_CreatePolyPolygonRgn, CreateRectRgn_win32_CreateRectRgn, CreateRectRgnIndirect_win32_CreateRectRgnIndirect, CreateRoundRectRgn_win32_CreateRoundRectRgn, ExtCreateRegion_win32_ExtCreateRegion, PathToRegion_win32_PathToRegion DeleteObject_win32_DeleteObject
Device context (DC) CreateDC_win32_CreateDC, GetDC_win32_GetDC DeleteDC_win32_DeleteDC, ReleaseDC_win32_ReleaseDC
Memory DC CreateCompatibleDC_win32_CreateCompatibleDC DeleteDC_win32_DeleteDC
Metafile CloseMetaFile_win32_CloseMetaFile, CopyMetaFile_win32_CopyMetaFile, GetMetaFile_win32_GetMetaFile, SetMetaFileBitsEx_win32_SetMetaFileBitsEx DeleteMetaFile_win32_DeleteMetaFile
Metafile DC CreateMetafile CloseMetaFile_win32_CloseMetaFile
Enhanced metafile CloseEnhMetaFile_win32_CloseEnhMetaFile, CopyEnhMetaFile_win32_CopyEnhMetaFile, GetEnhMetaFile_win32_GetEnhMetaFile, SetEnhMetaFileBits_win32_SetEnhMetaFileBits DeleteEnhMetaFile_win32_DeleteEnhMetaFile
Enhanced-metafile DC CreateEnhMetaFile_win32_CreateEnhMetaFile CloseEnhMetaFile_win32_CloseEnhMetaFile

+$#A Kernel Objects
Kernel object handles are process specific. That is, a process must either create a kernel object or open an existing object to obtain an object handle. Any process can create a new handle to an existing kernel object (even one created by another process), provided that the process knows the name of the object and has access to the object as defined by user security. For more information about security, see Security_win32_Security.
Processes can inherit or duplicate handles to the following types of kernel objects:
· Processes
· Threads
· Files (including file-mapping objects)
· Events
· Semaphores
· Mutexes
· Pipes (named and anonymous)
· Mailslots
· Communications devices

All other kernel objects are private to the process that created them; object handles cannot be duplicated or inherited.
A child process inherits an open handle from its parent process if inheritance was specified when the original handle was created and if the child process was created with the "inherit handles" flag set. An inherited handle is valid only in the context of the child process.
The DuplicateHandle_win32_DuplicateHandle function duplicates a handle into the current process or into another process. If an application duplicates one of its handles into another process, the duplicated handle is valid only in the context of the other process.
A duplicated or inherited handle is a unique value, but it refers to the same object as the original handle.
Kernel object handles include access rights that indicate the actions the application wants to perform on the object. An application specifies access rights when it creates an object or obtains an existing object handle. Each type of kernel object supports its own set of access rights. For example, event handles can have "set" or "wait" access (or both), file handles can have "read" or "write" access (or both), and so on. For more information about access rights, see Security_win32_Security, and topics that describe individual objects.
In the following illustration, an application creates an event object. The CreateEvent_win32_CreateEvent function creates the event object and returns an object handle.
{ewc msdncd, EWGraphic, group10750 2 /a "SDK_03.bmp"}
After the event object has been created, the application can use the event handle to set or wait on the event. The handle remains valid until the application closes the handle or terminates.
Most kernel objects support multiple handles to a single object. For example, the application in the preceding illustration could obtain additional event object handles by using the OpenEvent_win32_OpenEvent function, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10750 3 /a "SDK_04.bmp"}
This method enables an application to have handles with different access rights. For example, Handle 1 might have "set" and "wait" access to the event, and Handle 2 might have only "wait" access.
If another process knows the event name and has security access to the object, it can create its own event object handle by using OpenEvent, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10750 4 /a "SDK_05.bmp"}
The original application could also duplicate one of its handles into the same process or into another process by using the DuplicateHandle_win32_DuplicateHandle function, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10750 5 /a "SDK_06.bmp"}
The following illustration shows how a child process inherits a handle created by its parent process.
{ewc msdncd, EWGraphic, group10750 6 /a "SDK_07.bmp"}
A child process of an application automatically inherits handles created by its parent process if inheritance was specified when the handles were created and if the child process was created with the "inherit handles" flag set. Note that in the preceding illustration, Handle 2 is not equivalent to Handle 1 but does refer to the same event object.
For more information about inheritance, see Processes and Threads_win32_Processes_and_Threads and Files_win32_Files
An object remains in memory as long as at least one object handle exists. In the following illustration, the applications use the CloseHandle_win32_CloseHandle function to close their event object handles. When there are no event handles, the system removes the object from memory, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10750 7 /a "SDK_08.bmp"}
Occasionally, an object remains in memory after all object handles have been closed. For example, a thread could create an event object and wait on the event handle. While the thread is waiting, another thread could close the same event object handle. The event object remains in memory, without any event object handles, until the event object is set to the signaled state and the wait operation is completed. At this time, the system removes the object from memory.
Windows manages file objects somewhat differently from other kernel objects. File objects contain the file pointer ¾ the pointer to the next byte to be read or written in a file. Whenever an application creates a new file handle, the system creates a new file object. Therefore, more than one file object can refer to a single file on disk, as shown in the next illustration.
{ewc msdncd, EWGraphic, group10750 8 /a "SDK_09.bmp"}
Only through duplication or inheritance can more than one file handle refer to the same file object, as shown in the following illustration.
{ewc msdncd, EWGraphic, group10750 9 /a "SDK_10.bmp"}
The following table lists each of the Windows kernel objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or create a new existing object handle. The destructor functions close the object handle. When an application closes the last handle to a kernel object, the system removes the object from memory.
Kernel Objects
Object Creator function Destructor function
Process CreateProcess_win32_CreateProcess, OpenProcess_win32_OpenProcess, GetCurrentProcess_win32_GetCurrentProcess CloseHandle_win32_CloseHandle, TerminateProcess_win32_TerminateProcess
Thread CreateThread_win32_CreateThread, CreateRemoteThread_win32_CreateRemoteThread, GetCurrentThread_win32_GetCurrentThread CloseHandle_win32_CloseHandle, TerminateThread_win32_TerminateThread
File CreateFile_win32_CreateFile CloseHandle_win32_CloseHandle, DeleteFile_win32_DeleteFile
File mapping CreateFileMapping_win32_CreateFileMapping, OpenFileMapping_win32_OpenFileMapping CloseHandle_win32_CloseHandle
Event CreateEvent_win32_CreateEvent, OpenEvent_win32_OpenEvent CloseHandle_win32_CloseHandle
Semaphore CreateSemaphore_win32_CreateSemaphore, OpenSemaphore_win32_OpenSemaphore CloseHandle_win32_CloseHandle
Mutex CreateMutex_win32_CreateMutex, OpenMutex_win32_OpenMutex CloseHandle_win32_CloseHandle
Pipe CreatePipe_win32_CreatePipe CloseHandle_win32_CloseHandle
Named pipe CreateNamedPipe_win32_CreateNamedPipe CloseHandle_win32_CloseHandle, DisconnectNamedPipe_win32_DisconnectNamedPipe
Mailslot CreateMailslot_win32_CreateMailslot CloseHandle_win32_CloseHandle
Communications device GetStdHandle_win32_GetStdHandle CloseHandle_win32_CloseHandle
Heap HeapCreate_win32_HeapCreate HeapDestroy_win32_HeapDestroy
Module LoadLibrary_win32_LoadLibrary, GetModuleHandle_win32_GetModuleHandle FreeLibrary_win32_FreeLibrary
Update resource BeginUpdateResource_win32_BeginUpdateResource EndUpdateResource_win32_EndUpdateResource
Find file FindFirstFile_win32_FindFirstFile FindClose_win32_FindClose
Event log OpenEventLog_win32_OpenEventLog, RegisterEventSource_win32_RegisterEventSource, OpenBackupEventLog_win32_OpenBackupEventLog CloseEventLog_win32_CloseEventLog
Change notification FindFirstChangeNotification_win32_FindFirstChangeNotification FindCloseChangeNotification_win32_FindCloseChangeNotification

+$#A Handles and Objects Functions
CloseHandle_win32_CloseHandleDuplicateHandle_win32_DuplicateHandleGetHandleInformation_win32_GetHandleInformationSetHandleInformation_win32_SetHandleInformation

+,
+$#AK Help
Online help can come in a variety of forms, from detailed conceptual information to quick definitions. This overview discusses elements of the Win32 application programming interface (API) that support online help.
+$#A About Help
An important element of a user-friendly application is readily available online help. Microsoft® Windows® provides functions and messages that when used in conjunction with the Windows Help application make it easy to implement online help in your application. This overview discusses elements of the Win32 application programming interface (API) that support online help. It describes how to use these elements to give users a means to request help and explains how to use the Windows Help application to display help.
+$#A Help Requests
Most Windows-based applications provide online help information in a variety of forms, ranging from conceptual help that explains the purpose of an application's features to pop-up help that provides quick definitions of individual elements in the application's user interface. You use Win32 functions and messages to give users a variety of ways to request access to this information. The following sections describe these help requests.
+$#A Help Menu
Most applications provide user access to help information by including a Help menu in the main window. When the user selects an item from a Help menu, the corresponding window procedure receives a WM_COMMAND_win32_WM_COMMAND message that identifies the selected item. The application responds by displaying the appropriate help information, such as a list of help topics, an index, or an introduction to the application.
+$#A Help from the Keyboard
Windows provides user access to help information from the keyboard by notifying the application whenever the user presses the F1 key. The system sends WM_HELP to the window that has the keyboard focus when the user presses the F1 key. If the window is child window (for example, a control in a dialog box), the DefWindowProc_win32_DefWindowProc function passes the message to the parent window. If a menu is active when F1 is pressed, the system sends the message to the window associated with the menu. The application responds by displaying help information associated with the window, control, or menu that has the focus or is active. For example, if the user selects a control in a dialog box and presses F1, the application displays help information for that control.
The lParam parameter of WM_HELP is a pointer to a HELPINFO_win32_HELPINFO_str structure that contains detailed information about the item for which help is requested. You use this information to determine the help topic to display. The HELPINFO structure also includes the coordinates of the mouse cursor at the time the user pressed the F1 key. You can use this information to provide help based on the location of the mouse cursor.
+$#A Help from the Mouse
Windows provides user access to help information from the mouse by notifying the application whenever the user clicks the right mouse button or clicks a window, control, or menu after clicking the Question (?) button. The application responds by displaying help information associated with the given window, control, or menu.
The system sends a WM_CONTEXTMENU_win32_WM_CONTEXTMENU message when the user clicks the right mouse button. The window that was clicked receives the message. If the window is a child window, such as a control, the DefWindowProc_win32_DefWindowProc function passes the message to the parent window. The WM_CONTEXTMENU message specifies the coordinates of the mouse cursor. The x-coordinate is in the low-order word of the lParam parameter, and the y-coordinate is in the high-order word. If the user clicked a control, the wParam parameter is the handle of the control that received the button click.
The system sends a WM_HELP_win32_WM_HELP message when the user clicks an item in a window after clicking the Question (?) button that appears in the title bar of the window. You can add a Question button to a title bar by specifying the WS_EX_CONTEXTHELP style in the CreateWindowEx_win32_CreateWindowEx function when creating the window. The lParam parameter of WM_HELP is a pointer to a HELPINFO_win32_HELPINFO_str structure that contains detailed information about the item for which help is requested, including the coordinates of the mouse cursor at the time the user clicked the mouse button.
The Question button is recommended for use in dialog boxes only. In the past, applications have provided user access to help information about a dialog box by providing a Help button in the dialog box. This method is no longer recommended. Use the Question button instead.
+$#A Help Display and Windows Help
Once an application receives a request for help, it should display the appropriate help information. Because the Windows Help application supports a variety of ways to display help and a user interface that is consistent with that used by Windows to display its own help information, it is recommended that applications use Windows Help rather than other ways to display help. To direct Windows Help to display help information, an application uses the WinHelp_win32_WinHelp function, specifying details such as the information to display and the form of the window in which to display it. The following sections explain how to use WinHelp to display help information.
+$#A Help Files
To view help information, you must specify a help file when calling the WinHelp_win32_WinHelp function. The help file must have the Windows Help (.HLP) file format and have one or more topics. Each topic is a distinct unit of information, such as a conceptual description, a set of instructions, a picture, a glossary definition, and so on. Topics must be uniquely identified so that Windows Help can locate the topic whenever it is requested. Internally, Windows Help uses topic identifiers to locate topics, but applications most often use context identifiers (unique integer values) to specify topics to display. The help file author must explicitly map context identifiers to topic identifiers in the [MAP] section of the project file used to build the help file.
When you specify a help file but you do not specify a path, WinHelp looks for the help file in the Help directory or in a directory specified by the PATH environment variable. In addition, WinHelp can find a help file whose name is listed in the following registry location:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Help

To take advantage of the registry, you must create a value name that has the same name as your help file. The value assigned to that name must be the directory where the help file resides.
If WinHelp_win32_WinHelp cannot find the given help file, it displays a dialog box that allows the user to specify the location of the help file. Because WinHelp saves the location information in the registry, WinHelp does not ask again for the location of the same help file.
For more information about how to author and build a help file, see the documentation provided with your development tools.
+$#A Starting Windows Help
The following example uses the WinHelp_win32_WinHelp function to start the Windows Help application and open the Windows NT Help file to its Contents topic.
HWND hwnd; // main window handle
BOOL bResult // for checking boolean function result

bResult = WinHelp(hWnd, "WINNT.HLP", HELP_CONTENTS, 0L);

This next example opens the Windows NT user Help file, searches the file for the topic associated with a keyword string, and then displays the topic.
HWND hwnd; // main window handle
BOOL bResult // for checking boolean function result

bResult = WinHelp(hWnd, "WINNT.HLP", HELP_KEY,
(DWORD) "finding topics");

+$#A Help Topics Dialog Box
You can display the Help Topics dialog box by calling the WinHelp_win32_WinHelp function with the HELP_FINDER command. The Help Topics dialog box lets the user select topics to display by viewing the titles of the topics, the keywords associated with the topics, or the words and phrases found in the topics. Applications typically display the Help Topics dialog box when the user chooses a command, such as Help Topics, from the Help menu. An application may also display this dialog box if the user presses the F1 key when no specific window, control, or menu in the application has the focus or is active.
In the past, applications have used the HELP_CONTENTS and HELP_INDEX commands with the WinHelp function to display the Contents topic and the keyword index of the help file. These commands are no longer recommended. Use the HELP_FINDER command instead.
+$#A Information Topics
You can display a specific topic by calling the WinHelp_win32_WinHelp function with the HELP_CONTEXT command and specifying the context identifier for the topic. Applications typically use the HELP_CONTEXT command in response to user requests for topics containing conceptual information or procedural help rather than information about a specific control or menu. In such cases, the user may continue to browse the help file looking for related information before returning to the application.
The HELP_CONTEXT command invokes a regular instance of Windows Help, enabling the user to access other topics in the help file. It typically displays the main help window, which includes a title bar, system menu, minimize and maximize buttons, main menu, optional navigation bar, sizing border, and a client area. The text of the selected topic appears in the client area, and the user can navigate through the help file by using hot links or navigation buttons in the main window. The regular instance of Windows Help can also be used to display help in one or more secondary windows instead of the main window.
+$#A Pop-up Topics
You can display a pop-up topic that contains information for a specific control or menu by calling the WinHelp_win32_WinHelp function with the HELP_WM_HELP or HELP_CONTEXTMENU command. These commands display a topic in a pop-up window near the corresponding control or menu. To let the user return immediately to work in the application, the pop-up window is destroyed as soon as the user presses a key or clicks the left mouse button.
You use the HELP_WM_HELP command when processing WM_HELP messages for control windows. Because most controls pass the WM_HELP message to the DefWindowProc_win32_DefWindowProc function, the corresponding dialog box procedure (or parent window procedure) processes this message. Rather than give a specific context identifier, the dialog box procedure must pass an array of control and context identifier pairs to WinHelp along with the control handle specified in the hItemHandle member of HELPINFO_win32_HELPINFO_str structure passed with the WM_HELP message. The function determines the identifier of the control for which the WM_HELP message was generated and uses the matching context identifier to display the appropriate topic.
You use the HELP_CONTEXTMENU command when processing WM_CONTEXTMENU messages. Because most controls pass the WM_CONTEXTMENU message to the DefWindowProc function, the corresponding dialog box procedure (or parent window procedure) processes this message. Again, the procedure specify an array of control and context identifier pairs and the handle in wParam when calling WinHelp_win32_WinHelp so that the function can pick the appropriate context identifier from the array and display the appropriate topic. Unlike the HELP_WM_HELP command, HELP_CONTEXTMENU first displays a What's This? command in a menu. If the user chooses the command, WinHelp displays the topic. Otherwise, the request is canceled.
You can also display pop-up topics by using the HELP_CONTEXTPOPUP command and specifying a context identifier of the topic. This command is similar to the HELP_CONTEXT command but invokes the pop-up instance of Windows Help used by HELP_WM_HELP and HELP_CONTEXTMENU. Applications can use this command in response to WM_HELP_win32_WM_HELP messages to display help for menus and for windows that are not controls in a dialog box. To use this command most effectively, the application should assign context identifiers to these menus and windows.
You can assign a context identifier to any window or menu in the application. When the user's help requests generates a WM_HELP message, the system includes the context identifier in the HELPINFO_win32_HELPINFO_str structure that it passes to the parent window along with the WM_HELP message. The parent window can then pass the context identifier to WinHelp to display the requested help topic.
You use the SetWindowContextHelpId_win32_SetWindowContextHelpId function to assign a context identifier to a window or control and the SetMenuContextHelpId function to assign a context identifier to a menu. You can retrieve the context identifier for a window or menu by using the GetWindowContextHelpId_win32_GetWindowContextHelpId or GetMenuContextHelpId function.
+$#A Keyword Searches
You can enable the user to find and view topics by assigning keywords to topics in the help file. A keyword is simply a string that is associated with one or more topics. Windows Help collects all keywords in a help file, places them in a table, and displays them in the Index list of the Help Topics dialog box. When the user selects a keyword, Windows Help displays the associated help topic or displays a list of topics from which the user can choose if there is more than one topic associated with the keyword.
In an application, you can use the HELP_KEY, HELP_PARTIALKEY, or HELP_MULTIKEY command with WinHelp_win32_WinHelp to search for and display help topics based on whole or partial keywords. You specify the command, the keyword string, the help file, and the handle of the owner window. In all cases, if a single match is found, WinHelp displays the corresponding topic. If more than one match is found, the function displays the Topics Found dialog, letting the user choose which topic to view. If no match is found, WinHelp either displays the Index list (for HELP_KEY and HELP_PARTIALKEY) or displays an error message (for HELP_MULTIKEY).
You can search for multiple keywords in a single call to WinHelp by separating each keyword from the preceeding one with a semicolon (;). (Searching for multiple keywords is not supported for help files created for version 3.x.) You can also search for a keywords across multiple help files if the help file that you specify has a contents (.CNT) file that contains :Index or :Link commands. With the HELP_KEY command, WinHelp searches for keywords in all files specified by these commands. With the HELP_MULTIKEY and HELP_PARTIALKEY commands, the function searches all files except those specified by :Link commands.
By default, Windows Help recognizes only the keyword table identified by the K footnote character in the help source file. You can direct Windows Help to create additional keyword tables by specifying a footnote character other than K with the keyword definitions in the Windows Help file. (The footnote character A, however, is reserved.) You must define any additional keyword tables by using MULTIKEY statements in the [OPTIONS] section of the project file when building the help file.
An application can use the HELP_SETINDEX command with the WinHelp_win32_WinHelp function to direct Windows Help to display a keyword table other than K in its Index list. To direct Windows Help to search for a keyword in an alternate keyword table, an application can use the HELP_MULTIKEY command. You specify the keyword and keyword table in a MULTIKEYHELP_win32_MULTIKEYHELP_str structure, which you pass to WinHelp.
When WinHelp displays a topic, it displays it in the window specified by the > footnote for the topic, or in the window specified by the :Base command in the contents file, or in the main window. If the main window is already open to a different help file when you call WinHelp, the function hides main window while searching. In this case, canceling both the Topics Found and Help Topics dialogs, close the main window.
+$#A Secondary Help Windows
The main window of the Windows Help application is called the primary window. Windows Help can also display help topics in a secondary window. Unlike the main help window, a secondary window does not contain a menu bar. You can include a navigation bar in a secondary window, and you can add buttons to the bar. You can also choose to have Windows Help automatically adjust the height of secondary window to accommodate the topic.
You must define secondary windows in the [WINDOWS] section of your help project file, providing the name and, optionally, initial size and position of each window. You can direct the Windows Help application to display a topic in a secondary window by appending an angle bracket (>) and the name you have defined for the secondary window to the name of the help file and then passing the resulting string to the WinHelp_win32_WinHelp function.
An application can change the size and position of a primary or secondary window by specifying the address of a HELPWININFO_win32_HELPWININFO_str structure and the HELP_SETWINPOS command in a call to WinHelp. HELPWININFO specifies the name of the window and its new size and position. The primary help window is the "main" window.
+$#A Training Card Help
Using training card help, an application can display a sequence of instructions to guide the user through the steps of a task. A training card typically consists of text that explains a particular step and authorable buttons associated with TCard macros that allow the user to tell the application what to do next. Training cards may only be displayed in secondary windows and must not contain hot links to other topics in the help file.
An application initiates the training card instance of Windows Help by calling the WinHelp_win32_WinHelp function and specifying the HELP_TCARD command in combination with another command such as HELP_CONTEXT. Subsequently, when the user clicks an authorable button in the training card, clicks a hot spot assigned to the TCard macro, or closes the training card, Windows Help notifies the application by sending it a WM_TCARD message. The wParam parameter identifies the button or user action, and the lParam parameter contains additional data, the meaning of which depends on the value of wParam.
+$#A Canceling Help
Windows Help requires an application to explicitly cancel Help so that it can free any resources it used to keep track of the application and its help files. The application can do this at any time by calling the WinHelp_win32_WinHelp function and specifying the HELP_QUIT command. Note that this is not true for the pop-up instance of Windows Help. An application should not try to close the pop-up instance.
If an application has made any calls to WinHelp, it must cancel Help before it closes its main window (for example, in response to the WM_DESTROY message in the main window procedure). An application needs to call WinHelp only once to cancel Help, no matter how many help files it has opened. Windows Help remains running until all applications or dynamic-link libraries (DLLs) that have called WinHelp have canceled Help.
To close the training card instance of Windows Help, you must specify both the HELP_TCARD and HELP_QUIT commands when calling the WinHelp function. An application does not need to cancel the training card instance of Windows Help if the user cancels it first. Windows Help notifies an application when the user cancels the training card instance by sending the WM_TCARD_win32_WM_TCARD message with the wParam parameter set to IDCLOSE.
+$#A Using Help
This section shows how to provide context-sensitive help for a dialog box and how to set the appearance of a secondary help window.
· Providing Help in a dialog box_win32_Providing_Help_in_a_Dialog_Box
· Setting the appearance of a secondary help window_win32_Setting_the_Appearance_of_a_Secondary_Help_Window

+$#A Providing Help in a Dialog Box
To provide context-sensitive help in a dialog box, you must create an array consisting of pairs of doubleword values. The first value in each pair is the identifier of a control in the dialog box, and the second is the context identifier of the help topic for the control. The array should contain one pair of identifiers for each control in the dialog box.
The dialog box procedure must process the WM_HELP_win32_WM_HELP and WM_CONTEXTMENU_win32_WM_CONTEXTMENU messages. The dialog box procedure receives WM_HELP when the user presses the F1 key and WM_CONTEXTMENU when the user clicks the right mouse button.
The lParam parameter of WM_HELP is a pointer to a HELPINFO_win32_HELPINFO_str structure. The hItemHandle member of this structure identifies the control for which the user has requested help. You must pass the handle to the WinHelp_win32_WinHelp function along with the HELP_WM_HELP command, the name of your help file, and a pointer to the array of identifiers. The WinHelp function searches the array for the control identifier of the specified control and then retrieves the corresponding help context identifier. Next, the function passes the help context identifier to Windows Help, which finds the corresponding topic and displays it in a pop-up window. If the control has an identifier of - 1, the system searches for the next control that is a tab stop and uses its identifier to find the help context identifier. For this reason, it is important that you place static text before controls in a resource file.
Processing WM_CONTEXTMENU is similar to processing WM_HELP when calling the WinHelp function, with these two exceptions:
· You pass the wParam parameter from WM_CONTEXTMENU, which is the handle of the control that sent the message.
· You specify the HELP_CONTEXTMENU command instead of HELP_WM_HELP.

The HELP_CONTEXTMENU command causes Windows Help to display a menu before it displays the help topic. The menu is system defined. It allows the user to display help for the control or to display the Help Topics dialog box.
The following example shows how to implement context-sensitive help in a dialog box.
LRESULT CALLBACK EditDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{

// Create an array of control identifiers and context identifiers.

static DWORD aIds[] = {
ID_SAVE, IDH_SAVE,
ID_DELETE, IDH_DELETE,
ID_COPY, IDH_COPY,
ID_PASTE, IDH_PASTE,
0,0
};

switch (uMsg) {
case WM_HELP:
WinHelp(((LPHELPINFO) lParam)->hItemHandle, "helpfile.hlp",
HELP_WM_HELP, (DWORD) (LPSTR) aIds);
break;

case WM_CONTEXTMENU:
WinHelp((HWND) wParam, "helpfile.hlp", HELP_CONTEXTMENU,
(DWORD) (LPVOID) aIds);
break;
.
. // process other messages here
.
}

return FALSE;

}

+$#A Setting the Appearance of a Secondary Help Window
An application can set the size, position, and show state of a secondary help window by passing the HELP_SETWINPOS command and the address of a HELPWININFO_win32_HELPWININFO_str structure to the WinHelp_win32_WinHelp function. The members of HELPWININFO specify the name of the window to change and the window's new size, position, and show state.
The following example sets the appearance of a secondary window named "wnd_menu". The name must be defined in the [WINDOWS] section of the help project file.

BOOL DoWindowSize(VOID)
{
HANDLE hhwi;
LPHELPWININFO lphwi;
WORD wSize;
char *szWndName = "wnd_menu";

wSize = sizeof(HELPWININFO) + lstrlen(szWndName);
hhwi = GlobalAlloc(GHND, wSize);
lphwi = (LPHELPWININFO) GlobalLock(hhwi);

lphwi->wStructSize = wSize;
lphwi->x = 256; // horizontal position
lphwi->y = 256; // vertical position
lphwi->dx = 767; // width
lphwi->dy = 512; // height
lphwi->wMax = SW_SHOW; // show the window
lstrcpy(lphwi->rgchMember, szWndName); // secondary window

WinHelp(hwnd, "myhelp.hlp", HELP_SETWINPOS, (DWORD) lphwi);
GlobalUnlock(hhwi);
GlobalFree(hhwi);

return;
}

+$#A Help Reference
The following sections list the messages and functions associated with Help controls.
+$#A Help Functions
The following functions are used with online Help.
GetMenuContextHelpId_win32_GetMenuContextHelpIdGetWindowContextHelpId_win32_GetWindowContextHelpIdSetMenuContextHelpId_win32_SetMenuContextHelpIdSetWindowContextHelpId_win32_SetWindowContextHelpIdWinHelp_win32_WinHelp
+$#A Help Messages
The following messages are used with online Help.
WM_HELP_win32_WM_HELPWM_TCARD_win32_WM_TCARD

+$#AK Image Color Matching
Image color matching provides accurate and consistent reproductions of color images on all output devices. This overview describes image color matching and explains how to use the related functions. For general information about colors and how to specify colors when drawing, see Colors_win32_Colors.
+$#A About Image Color Matching
Despite differences in imaging technologies and color capabilities between devices, image color matching ensures that a color image will look the same or as close as possible to the same when output to screen, paper, film, and other media. Applications typically use image color matching to let users create color images on the screen that can then be reproduced exactly on a printer or on other screens.
+$#A Logical Colors, Gamuts, and Gamut Matching
By default, Windows disables image color matching to ensure that existing applications continue to draw color images as originally intended. You can enable image color matching for a device context by using the SetICMMode_win32_SetICMMode function. You must enable image color matching for each device context you intend to use for output.
Once you enable image color matching, you can create a color space and select it into the device contexts of the devices you wish to use. The color space defines the set of logical colors that you may use to draw. For each device, the system or the device driver transforms the logical colors to device (or physical) colors in a way that ensures consistency across devices.
To use a color from the color space, you specify a logical color value. The format of the value depends on the color space type. In some color spaces, this is similar to RGB (red, green, blue) color value described in previous Windows documentation, but the red, green, and blue components of logical color values are defined in terms of independent colormetric measure rather than arbitrary color units imposed by a device driver. In other cases, you may use color values that actually contain CMYK (cyan, magenta, yellow, black) values.
Not all devices support all colors. You can determine which colors are supported by a given device by checking its gamut, the set of colors it can produce. Different devices have different gamuts. For example, the screen can display cyans that no printer can produce; likewise, there are some reds on a page that never appear on a display.
An important element of image color matching is gamut matching, the process of finding color matches in the gamuts of different devices. This can range from the simple (such as truncating to the surface of the printer gamut), to the sophisticated (such as moving all colors a bit to maintain contrasts). Matching is not simply the process of ensuring that each pixel is faithfully reproduced with a given color. Instead, image color matching works within the capabilities of the given output device to produce an image in which the colors have the same relation to one another as they do when produced on other devices.
You can determine whether given colors are within the gamut of a device by using the CheckColorsInGamut_win32_CheckColorsInGamut function. This is useful for determining whether the screen has a larger gamut than the current printer. In such cases, you can notify the user of which screen colors won't print. You can preview the colors of a print job on the screen by using the ColorMatchToTarget_win32_ColorMatchToTarget function. The function first uses the current color space and gamut-matching method of a given device context, such as for a printer, to convert the colors in an image. It then converts the converted image to display on the preview device.
Image color matching applies to any drawing operation in which colors are given as color values. It affects colors that you specify directly, such as when setting the color of individual pixels using the SetPixel_win32_SetPixel function, or indirectly, when you set the colors in pens, brushes, fonts and other graphics objects. Image color matching does not apply to device-dependent bitmaps, such as those created by the CreateBitmap_win32_CreateBitmap and CreateCompatibleBitmap_win32_CreateCompatibleBitmap functions.
+$#A Color Spaces
Image color matching uses color spaces to define the set of colors available for a device. To each device context, the system assigns a default color space whose attributes, such as color space type, gamut-matching method and color endpoints, fully define how the system transforms color values for the device. You can create your own color spaces and replace the default color space for a device context by using the CreateColorSpace_win32_CreateColorSpace and SetColorSpace_win32_SetColorSpace functions. These functions let you explicitly set the format of the color values you use in your application and how the device interprets those values and generates colors.
To create a color space, you set the members of a LOGCOLORSPACE_win32_LOGCOLORSPACE_str structure and pass the structure to the CreateColorSpace function. You can either set the members to the values you want or specify the filename of a color profile that contains the characterization of the device. For more information, see Color Profiles_win32_Color_Profiles.
Every color space must have a color space type and a gamut matching method. Gamut matching specifies whether colors in a given image are transformed into an exact match or are modified to make best use of the gamut of the given device, and is described in Gamut Matching Method_win32_Gamut_Matching_Method.
The color space types can be device RGB_win32_RGB (red, green, blue), device CMYK_win32_CMYK (cyan, magenta, yellow, black), or calibrated RGB. Device RGB, the default color space for device contexts, consists of the arbitrary set of colors for the specified output device. Logical color values in this space are given as three 8-bit color values (in a 32-bit value) and are sent to the device driver without transformation. Device CMYK is similar to device RGB except that logical color values are given as four 8-bit values. You use this color space for devices, typically printers, that use CMYK color values. For example, you might use it to carry out color separation and pass color values directly to a CMYK printer.
Calibrated RGB consists of colors in the set defined by three color endpoints. The color endpoints are given as three 32-bit values and are interpreted as defined by the 1931 CIE (Commission Internationale de L'Eclairage) XYZ standard.
After you have created a color space, you can select it into a device context by using the SetColorSpace_win32_SetColorSpace function. You can delete a color space if you no longer need it by using the DeleteColorSpace_win32_DeleteColorSpace function.
You can retrieve the color space for a specified device context by using the GetColorSpace_win32_GetColorSpace function. This is useful if you want to get the logical definition or color profile file associated with the color space. You can use the GetLogColorSpace_win32_GetLogColorSpace function to copy this information to a LOGCOLORSPACE_win32_LOGCOLORSPACE_str structure.
+$#AK Gamut Matching Method
The gamut matching method determines how colors on a given device are "matched" with colors on another device. The type of matching needed depends on the image. There are some images for which users rarely want image color matching. For example, users like saturated, undithered colors in bar charts and highlights used in presentations ¾ the device colors are far superior to dithered patterns of primary colors.
Gamut-matching methods include the following:
· Business graphics
Preserves saturation (prevents dithering) if the requested color is not available. Useful for business charts and many computer-generated presentations.
· Colormetric
Produces exact colormetric matches. Dithers primary colors if necessary. Colormetric matching is important when named colors are wanted.
· Photographic
Produces the best perceptual match between devices. Takes advantage of the entire gamut of the device to maximize the number of distinct colors presented. This is best for photographic images.

You set the gamut-matching method for a device context by setting the lcsIntent member in the LOGCOLORSPACE_win32_LOGCOLORSPACE_str structure when creating the color space. The matching choices for the display and printer are global settings; they apply to all applications at all times.
In some cases, a user may want to use different matching methods in the same document. For example, the user may want to use the colormetric method to draw the company logo but the business graphics method for bar charts. You can mix gamut-matching methods on the same page of output by creating appropriate color spaces and using SetColorSpace_win32_SetColorSpace to switch between the spaces as needed.
+$#A Color Profiles
A color profile is a file that contains information that defines the color capabilities of a device. A color profile for a display monitor, for example, defines the chromaticities of the phosphors for that monitor; one for a printer defines the chromaticities of the inks on a given type of paper. The system uses this information when mapping logical colors to the physical colors available on the device.
Color profiles exist for display monitors, printers, and scanners. By default, the system uses the color profile that best matches a given device, based on information in the profile and provided by the device driver. If a color profile is not present for a given device, the system uses a default profile for that device.
In some cases, it may be important to let the user choose the color profile to use for a given device. This can occur when the device configuration has changed in a way that the system cannot detect. You can enumerate all color profiles for a device by using the EnumICMProfiles_win32_EnumICMProfiles function. This identifies each color profile by passing its filename to an EnumICMProfilesProc_win32_EnumICMProfilesProc callback function that you supply. You can set the color profile for the device by using the SetICMProfile_win32_SetICMProfile function. You can determine the current color profile for the device by using the GetICMProfile_win32_GetICMProfile function.
A color profile is available only if it is listed in the registry. You can install or remove color profiles in the registry by using the UpdateICMRegKey_win32_UpdateICMRegKey function. You can also use this function to query for printer profiles that match specified device settings, or to associate a color profile with a given color-matching DLL and output device.
+$#AK Device-Independent Bitmaps and Enhanced Metafiles
To provide for image color matching, color characterization is included in device-independent bitmaps (DIBs) and enhanced metafiles. In DIBs, the color characterization is placed in the DIB header. In enhanced metafiles, the characterization is in metafile records. Color characterization is not available for compatible bitmaps and standard (16-bit) metafiles.
You set the color characterization for a DIB by setting the appropriate members in the BITMAPV4HEADER_win32_BITMAPV4HEADER_str structure for the DIB. This structure is identical to the BITMAPINFOHEADER_win32_BITMAPINFOHEADER_str structure except that it includes members that define the color space type, color endpoints, and other related values. Some operating systems support the device CMYK color space type in DIBs but only for 32-bit DIBs. In such cases, the output device must also accept this format. If a DIB does not contain color characterization information, the system uses a default characterization.
You set color characterization for an enhanced metafile by using the CreateColorSpace_win32_CreateColorSpace and SetColorSpace_win32_SetColorSpace functions when recording the metafile. The system records information about the color spaces and carries out color matching with the intended output device when the metafile is played. The system also records the SetICMMode_win32_SetICMMode and DeleteColorSpace_win32_DeleteColorSpace functions, but no other image color matching functions can be used with enhanced metafiles. If you specify a color profile in the LOGCOLORSPACE_win32_LOGCOLORSPACE_str structure when recording an enhanced metafile, the system records the filename of the color profile but not the content of the profile. This is similar to the way fonts are handled in enhanced metafiles.
+$#A Gamma Correction
Some display adapters support downloadable gamma correction tables. These tables let you control the colors generated by the digital-to-analog converter (DAC) by mapping specific RGB values to DAC values. You can set the gamma ramp for any 24-bit display adapters that provide hardware gamma correction by using the SetDeviceGammaRamp_win32_SetDeviceGammaRamp function. The hardware gamma ramp is a global property that affects all running applications and is not changed on a per-device context basis. You can retrieve the current gamma ramp by using the GetDeviceGammaRamp_win32_GetDeviceGammaRamp function.
+$#A Image Color Matching Reference
This section provides a listing of the image color matching functions and structures.
+$#A Image Color Matching Functions
This following are the image color matching functions.
CheckColorsInGamut_win32_CheckColorsInGamutColorMatchToTarget_win32_ColorMatchToTargetCreateColorSpace_win32_CreateColorSpaceDeleteColorSpace_win32_DeleteColorSpaceEnumICMProfiles_win32_EnumICMProfilesEnumICMProfilesProc_win32_EnumICMProfilesProcGetColorSpace_win32_GetColorSpaceGetDeviceGammaRamp_win32_GetDeviceGammaRampGetICMProfile_win32_GetICMProfileGetLogColorSpace_win32_GetLogColorSpaceSetColorSpace_win32_SetColorSpaceSetDeviceGammaRamp_win32_SetDeviceGammaRampSetICMMode_win32_SetICMModeSetICMProfile_win32_SetICMProfileUpdateICMRegKey_win32_UpdateICMRegKey
+$#A Image Color Matching Macros
This following are the image color matching macros.
CMYK_win32_CMYKGetCValue_win32_GetCValueGetKValue_win32_GetKValueGetMValue_win32_GetMValueGetYValue_win32_GetYValue
+$#A Image Color Matching Structures
The following structures are used with the image color matching functions.
BITMAPV4HEADER_win32_BITMAPV4HEADER_strCIEXYZ_win32_CIEXYZ_strCIEXYZTRIPLE_win32_CIEXYZTRIPLE_strLOGCOLORSPACE_win32_LOGCOLORSPACE_str

+$#AK Interprocess Communications
The Microsoft® Win32® application programming interface (API) provides a rich set of mechanisms for facilitating communications and data sharing between applications. Collectively, the activities enabled by these mechanisms are called interprocess communications (IPC). In addition to facilitating the division of labor among several specialized processes, some forms of IPC can distribute the computational load among cooperating computers on a network.
+$#A About Interprocess Communications
As computer users become more sophisticated, they demand more power from the applications they use. To meet this demand, developers add more features to their applications, and the applications become larger. These large applications can eventually become unmanageable, both from a development standpoint and from a user-interface point of view. Therefore, developers now tend to produce highly focused applications that do a good job on a limited number of features and then to enable those applications to communicate and share data with other specialized applications. No longer can any one application meet all user expectations; the age of cooperating and communicating applications has arrived.
Typically, cooperating and communicating applications can be categorized as clients or servers. A client is an application or a process that requests a service from some other process. A server is an application or a process that responds to a client request. Many applications act as both a client and a server, depending on the situation. For example, a word processing application might act as a client in requesting a summary table of manufacturing costs from a spreadsheet application acting as a server. The spreadsheet application, in turn, might act as a client in requesting the latest inventory levels from an automated inventory control application.
+$#A Development Considerations
If a developer decides that an application would benefit from IPC, the developer must consider some of the following questions before deciding which of the available IPC methods to use.
· Should the application be able to communicate with other applications running on other computers on a network, or is it sufficient for the application to communicate only with applications on the local computer? In other words, does the application need to be networkable? Some IPC methods work either on the local computer or over a network; others work only on the local computer.
· Should the application be able to communicate with applications running on other computers that may be running under different operating systems (that is, MS-DOS®, Microsoft® Windows® Version 3.x, UNIX®)? In other words, must the application be interoperable?
· Should the user of the application have to choose the other application(s) with which the application communicates, or can the application implicitly find its cooperating partners?
· Should the application communicate with many different applications in a general way, such as allowing cut and paste operations with any other application, or should its communications requirements be limited to a restricted set of interactions with specific other applications? Applications that communicate in a general way are called loosely coupled; applications that have a more strictly defined interaction are called tightly coupled.
· Is performance a critical aspect of the application? All IPC mechanisms include some amount of communications overhead.
· Should the application be a Windows-based application, or will character-mode functionalities be sufficient? Some IPC mechanisms discussed in this topic do not work in character-mode-only applications. The clipboard, dynamic data exchange (DDE), and object linking and embedding (OLE) all require that the application have at least one window.

The answers to these questions determine whether an application can benefit by usi n g o n e o r m o r e o f t h e IPC mechanisms available in the Win32 API. This topic discusses the strengths and weaknesses of each of the Win32 IPC mechanisms.
+$#A File Mapping and IPC
File mapping enables a process to treat the content of a file as if it were a block of memory in the process's address space. Therefore, instead of using file input and output (I/O) operations, the process can use simple pointer operations to examine and modify the contents of the file.
The Win32 API enables two or more processes to access the same file-mapping object. Each process receives a pointer to memory in its own address space. With this pointer, the process can read or modify the contents of the file.
There are three ways an application can share a file-mapping object created in one process with another process:
· Inheritance. The first process creates the file-mapping object and then allows the handle of the object to be inherited by a child process.
· Named file mapping. The first process creates the file-mapping object with a well-known name (which can be different from the filename). The second process opens the file-mapping object by specifying the well-known name. Alternatively, the first process can create a file-mapping object with a unique name and communicate that name to the second process through some other IPC mechanism (named pipe, mailslot, and so on).
· Handle duplication. The first process creates the file-mapping object and then passes the handle of the object to the second process. The second process then duplicates the handle to gain access to the shared memory. The original process can communicate the file-mapping handle to the second process through one of the other IPC mechanisms described in this topic (named pipe, mailslot, and so on). For more information about duplicating handles, see Synchronization_win32_Synchronization.
When two or more processes have read-write access to a shared memory block, they must use some sort of synchronization object, such as a semaphore, to prevent data corruption in a multitasking environment.
File mapping is quite efficient and also provides operating-system-supported security attributes that can help prevent unauthorized data corruption. File mapping can be used only between processes on a local computer; it cannot be used over a network. An application can, however, create a file-mapping object to a file on a remotely mounted volume. For example, if a remote server is mounted as the F drive, an application can create a file-mapping object to a file on that volume. However, a process running on a remote server cannot share a file-mapping object with a process running on a local computer.

Key Point File mapping is an efficient way for two or more processes on the same computer to share data, but the developer must provide synchronization between the processes. For more information, see File Mapping_win32_File_Mapping, and Synchronization_win32_Synchronization.

+$#A Shared Memory and IPC
The Win32 API uses a special case of file mapping to provide shared memory access between processes. If you specify the system swapping file when creating a file-mapping object, the file-mapping object is treated as a shared memory block. Other processes can access the same block of memory by opening the same file-mapping object, as described in File Mapping_win32_File_Mapping.
Because shared memory is implemented with file mapping, it supports security access attributes and can operate only between processes running on the same computer.

Key Point Shared memory in the Win32 API is implemented by using file mapping. All characteristics of file mapping apply to shared memory. For more information, see File Mapping_win32_File_Mapping.

+$#A Anonymous Pipes and IPC
An anonymous (or unnamed) pipe enables related processes to transfer information back and forth as if they were reading from and writing to a file. Typically, anonymous pipes are used for redirecting the standard input and output (I/O) of a child process so that it can exchange data with its parent process.
To use an anonymous pipe, the parent process typically creates the pipe and then allows its read and write handles to be inherited by a child process. The parent process writes data to the pipe; the child process can read the data from the other end of the pipe. Likewise, the child process can write data to the pipe and the parent process can read the data from its end of the pipe. A parent process can also create two or more child processes that inherit the read and write handles to an anonymous pipe. Those child processes can use that pipe to communicate between each other directly, without going through the parent process.
Anonymous pipes cannot be used over a network, nor can they be used between unrelated processes. For information about a pipe mechanism that can be used with unrelated processes and over a network, see Named Pipes_win32_Named_Pipes.

Key Point Anonymous pipes provide an efficient way to redirect standard I/O to child processes on the same computer. For more information, see Pipes_win32_Pipes.

+$#A Named Pipes and IPC
Like anonymous pipes, named pipes are used to transfer data back and forth between processes. Unlike anonymous pipes, however, named pipes can operate between unrelated processes and across a network between computers. Typically, a server process creates a named pipe with a well-known name. Client processes that can get the name of the pipe can open the other end of the pipe, subject to access restrictions specified by the pipe's creator. After they are connected, the server and client can exchange data by performing read and write operations on the pipe. Alternatively, the pipe creator can create a pipe and let a child process inherit the handle to the pipe, or it can create a pipe with a unique name and communicate that name to the client through some other IPC mechanism (such as a mailslot maintained by the client).

Key Point Named pipes provide a relatively simple programming interface that makes transferring data across a network no more difficult than transferring data between two processes on the same computer. For more information, see Pipes_win32_Pipes.

+$#A IPC Mailslots
Mailslots provide a one-way interprocess communications capability. Any process can create a mailslot and become a mailslot server. Other processes, called mailslot clients, can gain access to the mailslot by its name and send messages to the mailslot server process. A process can be both a mailslot server and a mailslot client, so two-way IPC is possible with multiple mailslots.
Incoming messages are always appended to the mailslot. The mailslot saves the messages until the creating process has a chance to read them.
Mailslots are similar to named pipes, but with a somewhat simplified programming interface and the added ability to broadcast messages to all computers in a specified network domain. A mailslot client can send a message to a mailslot on its local computer, to a mailslot on another computer, or to all mailslots with the same name on all computers in a specified network domain. Messages broadcast to a domain can be no longer than 400 bytes; messages sent to a single mailslot are limited only by the maximum message size specified by the creator of the mailslot (which can be unlimited).

Key Point Mailslots offer an easy way for applications to send and receive short messages. They also provide the ability to broadcast messages across all computers in a network domain. For more information, see Mailslots_win32_Mailslots.

+$#A Using the Clipboard for IPC
The clipboard provides a mechanism for the well-established cut-copy-paste model for simple data sharing between Win32-based applications. It enables an application to read or write data in many different standard and application-defined formats. It also works between applications on the same computer or on different computers on a network.
The clipboard acts as a central depository for data sharing among applications. When a user performs a cut or copy operation in an application, the application puts the selected data on the clipboard in one or more formats. Any other application can then retrieve the data from the clipboard, choosing from the available formats. The clipboard is a very loosely coupled exchange medium, wherein the two applications need only agree on the data format.

Key Point All Win32-based applications should support the clipboard for those data formats that they understand. For example, a text editor or word processor should at least be able to produce and accept clipboard data in pure text format. For more information, see Clipboard_win32_Clipboard.

+$#A Dynamic Data Exchange and IPC
Dynamic data exchange (DDE) is a protocol for interprocess communications that enables applications to exchange data in a variety of formats. Applications can use DDE for one-time data exchanges or for ongoing exchanges in which the applications update one another as new data becomes available.
The data formats used by DDE are the same as those used for the Windows clipboard IPC mechanism. DDE can be thought of as an extension of the clipboard mechanism. The clipboard is almost always used for a one-time response to a user command, such as choosing the Paste command from a menu. DDE is also usually initiated by a user command, but it often continues to function without further user interaction.
Three types of data exchange are possible with DDE:
· Cold link. The exchange is a one-time data transfer, like the clipboard.
· Warm link. A server notifies the client when data changes, and the client must then request new data.
· Hot link. A server sends data updates to the client when data changes.
DDE exchanges can occur between applications running on the same computer or on different computers on a network.

Key Point Most major Windows-based applications support DDE. Like the clipboard, DDE support enables an application to exchange data in a variety of standard formats with other Windows-based applications that support DDE. A developer can also define custom DDE data formats for special-purpose IPC between applications with more tightly coupled communications requirements. For more information, see Dynamic Data Exchange_win32_Dynamic_Data_Exchange and Dynamic Data Exchange Management Library_win32_Dynamic_Data_Exchange_Management_Library.

+$#A Object Linking and Embedding
Object linking and embedding (OLE) applications manage compound documents ¾ that is, documents made up of data from a variety of different applications. OLE provides services that make it easy for applications to call on other applications for data editing. For example, an OLE-aware word processor could embed a graph from a spreadsheet. The user could start the spreadsheet automatically from within the word processor by choosing the embedded chart for editing. The OLE libraries would take care of starting the spreadsheet and presenting the graph for editing. When the user quit the spreadsheet, the graph would be updated in the original word processor document. Contrast this with a DDE link involving a spreadsheet graph in a word processor document. With DDE, the user would have to explicitly start the spreadsheet and open the graph document in order to make changes. With OLE, the spreadsheet appears to be an extension of the word processor.
Like an application using DDE, an OLE-aware application can communicate with a wide variety of other Windows-based applications. Because the OLE protocol includes all necessary context information, the application will be able to hold OLE conversations with all other OLE-aware applications ¾ even those that have yet to be written.

Key Point OLE supports compound documents and enables an application to include embedded or linked data that, when chosen, automatically starts another application for data editing. This enables the application to be extended by any other OLE-aware application.

+$#A Dynamic-Link Libraries and IPC
It is possible to build a Win32-based dynamic-link library (DLL) so that its global data is shared with all processes that call the DLL. Therefore, cooperating processes can call the DLL to examine and modify global data owned by the DLL. For example, process A calls a DLL function with data that the DLL stores in its global data space. Process B calls another DLL function that retrieves that data. Because of the multitasking nature of the Win32 API, the DLL would have to use a semaphore or another synchronization object to control access to the shared memory.
Although shared global data can be used in a DLL, Win32 file mapping is recommended for shared memory. File mapping is more efficient and provides the additional benefit of access protection. (For example, a client can be limited to read-only access to a file-mapping object.)

Key Point Although a developer can use shared global data segments in a DLL to allow two or more applications to share data, it is preferable to use Win32 file-mapping functions to create shared memory. For more information, see Dynamic-Link Libraries_win32_Dynamic_Link_Libraries.

+$#A Remote Procedure Call
The Win32 API provides remote procedure calls (RPC) to enable applications to call functions remotely. With RPC, communication with other processes becomes as easy as calling a function. RPC operates between processes on a single computer or on different computers on a network. One way to think of RPC is as a DLL that works on a network.
The RPC provided by the Win32 API is compliant with the Open Software Foundation (OSF) Distributed Computing Environment (DCE). This means that RPC applications written by using the Win32 API are able to communicate with other RPC applications running with other operating systems that support DCE. RPC automatically supports data conversion to account for different hardware architectures and for byte-ordering between dissimilar environments.
The Win32 software development kit includes RPC libraries that support MS-DOS-hosted RPC clients. With this facility, a Windows NT server, for example, can provide service to many MS-DOS clients through RPC.
RPC clients and servers are tightly coupled but still maintain high performance. Windows makes extensive use of RPC to facilitate a client-server relationship between different parts of the operating system.

Key Point RPC provides IPC with a function interface, with support for automatic data conversion and for communications with other operating systems. Using RPC, a developer can create high-performance, tightly coupled distributed applications. For more information, see the Microsoft RPC Components13FVJZP@rpc.hlp.

+$#A The Netbios Function and IPC
The Win32 API provides the Netbios_win32_Netbios function to process low-level network control functions. This capability is provided primarily for applications written using the IBM NetBIOS system that must be ported to Windows. It is highly recommended that a developer writing a new application use the other IPC mechanisms described in this topic rather than the low-level Netbios function. The IPC mechanisms provided in the Win32 API encapsulate and hide an enormous amount of raw NetBIOS functionality that the developer would otherwise have to implement.
Consider, for example, the following list of NetBIOS actions that would be necessary to emulate a Win32-based mailslot:
On the server end
· Add the name to the local table.
· Listen for the session connection.
· Read the data upon connection.
· Append the data to the message queue.
· Close the session.
· Listen for another session connection.
· Allow the queued messages to be retrieved.

On the client side
· Open the session.
· Write the data.
· Close the session.

Key Point The Netbios_win32_Netbios function is provided only for porting an existing application written using the IBM NetBIOS system or for those applications that need specialized access to low-level network functionality. For most applications, it is better to use the higher-level IPC mechanisms available in the Win32 API. For more information, see Networks_win32_Networks.

+$#A Summary
It is likely that an application will support IPC by using several of the mechanisms described in this topic. For example, all Win32-based applications should provide at least minimal support for the clipboard. In addition, DDE and OLE may offer the application an opportunity to communicate in a loosely coupled way with a wide variety of applications that support these protocols. The great strength of these loosely coupled mechanisms is that a developer can enable an application to share data with other applications without knowing anything about the applications themselves. By supporting the protocols for the clipboard, DDE, and OLE, the developer is assured of a growing number of applications with which the application can share data. As new applications are written that support these protocols, the developer's application will be ready to communicate with them.
As an application becomes more sophisticated, a developer may find it advantageous to break up the application into tightly coupled cooperating processes that use shared memory, pipes, or RPC to communicate. These tightly coupled mechanisms can provide high-performance extensions to the application. But when adding these more specialized IPC methods, the developer should not abandon the more loosely coupled IPC methods that allow the application to share data in a general way with most other Win32-based applications.

+$#AK Large Integer Operations
This overview describes Microsoft Win32 support for operations on large (64-bit) integers.
+$#A About Large Integer Operations
Applications can multiply signed or unsigned 32-bit integers, generating 64-bit results, by using the Int32x32To64_win32_Int32x32To64 and UInt32x32To64_win32_UInt32x32To64 functions. Applications can shift bits in 64-bit values to the left or right by using the Int64ShllMod32_win32_Int64ShllMod32, Int64ShraMod32_win32_Int64ShraMod32, and Int64ShrlMod32_win32_Int64ShrlMod32 functions. These functions provide logical and arithmetic shifting.
Applications can also multiply and divide 32-bit values in a single operation by using the MulDiv_win32_MulDiv function. Although the result of the operation is a 32-bit value, the function stores the intermediate result as a 64-bit value, so that information is not lost when large 32-bit values are multiplied and divided.
+$#A Large Integer Operations Functions
Following are the functions used with large integer operations.
Int32x32To64_win32_Int32x32To64Int64ShllMod32_win32_Int64ShllMod32Int64ShraMod32_win32_Int64ShraMod32Int64ShrlMod32_win32_Int64ShrlMod32MulDiv_win32_MulDivUInt32x32To64_win32_UInt32x32To64

+$#AK Mailslots
A mailslot is a mechanism for one-way interprocess communications (IPC). An application written for Microsoft® Windows® can store messages in a mailslot. The owner of the mailslot can retrieve messages that are stored there. These messages are typically sent over a network to either a specified computer or to all computers in a specified domain. A domain is a group of workstations and servers that share a group name.
+$#A About Mailslots
A mailslot is a pseudofile; it resides in memory, and standard Windows file functions write to it. Unlike disk files, however, mailslots are temporary. When every handle of a mailslot is closed, the mailslot and all the data it contains are deleted. The data in a mailslot message can be in any form.
A mailslot server is a process that creates and owns a mailslot. When the server creates a mailslot, it receives a mailslot handle. This handle must be used when a process reads messages from the mailslot. Only the process that creates a mailslot (or has obtained the handle by some other mechanism, such as inheritance) can read from the mailslot. A mailslot exists until all server handles to it have been closed or all server processes have exited. All mailslots are local to the process that creates them; a process cannot create a remote mailslot.
A mailslot client is a process that writes a message to a mailslot. Any process that has the name of a mailslot can put a message there. New messages follow preexisting messages in the mailslot.
Mailslots can broadcast messages within a domain. If several processes in a domain each create a mailslot using the same name, every message that is addressed to that mailslot and sent to the domain is received by the participating processes. Because one process can control both a server mailslot handle and the client handle retrieved when the mailslot is opened for a write operation, applications can easily implement a simple message-passing facility within a domain.
Some developers choose to use named pipes instead of mailslots for interprocess communications. Named pipes are a simple way for two processes to exchange messages. Mailslots, on the other hand, are a simple way for a process to broadcast messages to many other processes. For more information about named pipes, see Pipes_win32_Pipes.
Another issue in the choice between mailslots and named pipes is that mailslots use datagrams, and named pipes do not. A datagram is a small packet of information that the network sends along the wire. Like a radio or television broadcast, a datagram offers no confirmation of receipt; there is no way to guarantee that a datagram has been received. Just as mountains, large buildings, or interfering signals might cause a radio or television signal to get lost, there are things the sender has no knowledge of that can prevent a datagram from reaching a particular destination. Named pipes are more like simple one-to-one telephone calls: you can talk only to one party, but you know that the conversation is occurring. So, if an application requires guaranteed reception, it should use a named pipe; but named pipes cannot broadcast. Mailslots do not guarantee reception, but they can broadcast.
+$#A Mailslot Names
When a process creates a mailslot, the mailslot name must have the following form.
\\.\mailslot\[path]name
A mailslot name requires the following elements: two backslashes to begin the name, a period, a backslash following the period, the word mailslot, and a trailing backslash. Case, however, is not important. A mailslot name can be preceded by a path consisting of the names of one or more pseudodirectories, separated by backslashes. For example, if a user expects messages on the subject of taxes from Bob, Pete, and Sue, the user's mailslot application might allow the user to create individual mailslots for each sender.
\\.\mailslot\taxes\bobs_comments
\\.\mailslot\taxes\petes_comments
\\.\mailslot\taxes\sues_comments

To put a message into a mailslot, a process opens a mailslot whose name is in one of several forms. To write to a mailslot on the local computer, a process can use a mailslot name using the same form required for creating a mailslot. This situation, however, is relatively uncommon. More frequently, a process uses the following form to write to a mailslot on a specific remote computer.
\\computername\mailslot\[path]name
The following form allows a process to put a message into every mailslot in the specified domain that has the given name.
\\domainname\mailslot\[path]name
To put a message into every mailslot with a given name in the system's primary domain, a process uses the following form.
\\*\mailslot\[path]name
+$#A Mailslots and Microsoft LAN Manager 2.x
Windows uses mailslots similar to those developed for Microsoft LAN Manager version 2.0. An application that uses LAN Manager 2.x mailslots can interact easily with mailslots created by Windows. There are significant differences, however, between Windows-based mailslots and LAN Manager mailslots:
· Windows-based mailslots all have the same class ¾ there are no "first class" and "second class" mailslots.
· All Windows-based mailslot messages have equal priority and are always appended to the mailslot buffer. When a Windows-based mailslot message is written to a mailslot on a computer that requires message priority, the system sets the priority to the lowest value (0).
· Write operations to Windows-based mailslots on local computers return an error value if an error occurs. Write operations to remote Windows-based mailslots do not return an error value when they fail. (Because there is no built-in limit to the size of the mailslot buffer failure is unlikely.)
· Write operations to Windows-based mailslots fail immediately if the message cannot be appended to the mailslot buffer (if the buffer is too small, for example). Immediate failure makes write time-outs irrelevant; contrary to LAN Manager mailslots, there are no write time-outs for Windows-based mailslots.
· A process that creates a Windows-based mailslot can specify that the mailslot handle be inherited by its child processes. (Mailslot handles in LAN Manager cannot be inherited.)

A process writes messages to a Windows-based mailslot by using standard file input and output functions. The following table shows LAN Manager mailslot functions and their corresponding Windows functions.
LAN Manager functions Windows functions
DosDeleteMailslot CloseHandle_win32_CloseHandle
DosMailslotInfo GetMailslotInfo_win32_GetMailslotInfo
DosMakeMailslot CreateMailslot_win32_CreateMailslot
DosReadMailslot ReadFile_win32_ReadFile
DosWriteMailslot WriteFile_win32_WriteFile

+$#A Server and Client Functions
Clients and servers when working with mailslots should use only the functions discussed in this section. Other Windows functions should not be used, even if they accept file handles or filenames as arguments.
+$#A Mailslot Server Functions
Mailslot servers have exclusive use of three functions, as shown in the following table.
Function Description
CreateMailslot_win32_CreateMailslot Creates a mailslot and returns a mailslot handle.
GetMailslotInfo_win32_GetMailslotInfo Retrieves the maximum message size, the mailslot size, the size of the next message in the mailslot, the number of messages in the mailslot, and the amount of time a read operation can wait for a message.
SetMailslotInfo_win32_SetMailslotInfo Changes the read time-out for a mailslot.

The following functions are also used by mailslot servers.
Function Description
DuplicateHandle_win32_DuplicateHandle Duplicates the mailslot handle.
ReadFile_win32_ReadFile Retrieves messages from a mailslot.
GetFileTime_win32_GetFileTime Retrieves the date and time a mailslot was created.
SetFileTime_win32_SetFileTime Sets the date and time a mailslot was created.

+$#A Client Functions
A client process uses the following functions when interacting with a mailslot.
Function Description
CloseHandle_win32_CloseHandle Closes a mailslot handle for a client process.
CreateFile_win32_CreateFile Creates a mailslot handle for a client process.
DuplicateHandle_win32_DuplicateHandle Duplicates a mailslot handle.
WriteFile_win32_WriteFile Writes data to a mailslot.

+$#A Using Mailslots
· Creating a mailslot_win32_Creating_a_Mailslot
· Writing to a mailslot_win32_Writing_to_a_Mailslot
· Reading from a mailslot_win32_Reading_from_a_Mailslot

+$#A Creating a Mailslot
Mailslots are supported by three specialized functions: CreateMailslot_win32_CreateMailslot, GetMailslotInfo_win32_GetMailslotInfo, and SetMailslotInfo_win32_SetMailslotInfo. These functions are used by the mailslot server.
The following code sample uses the CreateMailslot function to retrieve the handle of a mailslot named sam ple_mailslot.
BOOL FAR PASCAL Makeslot(HWND hwnd, HDC hdc)
{
LPSTR lpszSlotName = "\\\\.\\mailslot\\sample_mailslot";

/* The mailslot handle "h Slot1" is declared globally. */

hSlot1 = CreateMailslot(lpszSlotName,
0, /* no maximum message size */
MAILSLOT_WAIT_FOREVER, /* no time-out for read operations */
(LPSECURITY_ATTRIBUTES) NULL); /* no security attributes */

if (hSlot1 == INVALID_HANDLE_VALUE) {
ErrorHandler(hwnd, "CreateMailslot"); /* local error handler */
return FALSE;
}

TextOut(hdc, 10, 10, "CreateMailslot successful.", 26);
return TRUE;
}

To create a mailslot that can be inherited by child processes, an application should change the SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure passed as the last parameter of CreateMailslot_win32_CreateMailslot. To do this, the application sets the bInheritHandle member of this structure to TRUE (the default setting is FALSE).
+$#A Writing to a Mailslot
Writing to a mailslot is similar to writing to a standard disk file. The following code uses the CreateFile_win32_CreateFile, WriteFile_win32_WriteFile, and CloseHandle_win32_CloseHandle functions to put a short message in a mailslot. The message is broadcast to every computer in the primary domain of the system.
LPSTR lpszMessage = "Message for sample_mailslot in primary domain.";
BOOL fResult;
HANDLE hFile;
DWORD cbWritten;

hFile = CreateFile("\\\\*\\mailslot\\sample_mailslot",
GENERIC_WRITE,
FILE_SHARE_READ, /* required to write to a mailslot */
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler(hwnd, "Primary domain"); /* local error handler */
return FALSE;
}

fResult = WriteFile(hFile,
lpszMessage,
(DWORD) lstrlen(lpszMessage) + 1, /* include terminat. null char. */
&cbWritten,
(LPOVERLAPPED) NULL);

if (!fResult) {
ErrorHandler(hwnd, "WriteFile");
return FALSE;
}

TextOut(hdc, 10, 10, "WriteFile successful.", 21);

fResult = CloseHandle(hFile);

if (!fResult) {
ErrorHandler(hwnd, "CloseHandle");
return FALSE;
}

TextOut(hdc, 10, 30, "CloseHandle successful.", 23);
return TRUE;

Messages broadcast to a domain must be no longer than 400 bytes. Messages that are not broadcast should be smaller than 64 kilobytes. There is no limit to the number of messages that can be sent to a mailslot.
+$#A Reading from a Mailslot
The process that creates a mailslot can read messages from it by using the mailslot handle in a call to the ReadFile_win32_ReadFile function. The code in the following example calls the GetMailslotInfo_win32_GetMailslotInfo function to determine whether there are messages in the mailslot. If messages are waiting, each is displayed in a message box along with the number of messages remaining to be read.
BOOL FAR PASCAL Readslot(HWND hwnd, HDC hdc)
{
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPSTR lpszBuffer;
CHAR achID[80];
DWORD cAllMessages;

cbMessage = cMessage = cbRead = 0;

/* Mailslot handle "hSlot1" is declared globally. */

fResult = GetMailslotInfo(hSlot1, /* mailslot handle */
(LPDWORD) NULL, /* no maximum message size */
&cbMessage, /* size of next message */
&cMessage, /* number of messages */
(LPDWORD) NULL); /* no read time-out */

if (!fResult) {
ErrorHandler(hwnd, "GetMailslotInfo");
return FALSE;
}

if (cbMessage == MAILSLOT_NO_MESSAGE) {
TextOut(hdc, 10, 10, "No waiting messages.", 20);
return TRUE;
}

cAllMessages = cMessage;

while (cMessage != 0) { /* retrieves each message */

/* Create a message-number string. */

wsprintf((LPSTR) achID,
"\nMessage #%d of %d\n", cAllMessages - cMessage + 1,
cAllMessages);

/* Allocate memory for the message. */

lpszBuffer = (LPSTR) GlobalAlloc(GPTR,
lstrlen((LPSTR) achID) + cbMessage);

lpszBuffer[0] = '\0';

fResult = ReadFile(hSlot1,
lpszBuffer,
cbMessage,
&cbRead,
(LPOVERLAPPED) NULL);

if (!fResult) {
ErrorHandler(hwnd, "ReadFile");
GlobalFree((HGLOBAL) lpszBuffer);
return FALSE;
}

/* Concatenate the message and the message-number string. */

lstrcat(lpszBuffer, (LPSTR) achID);

/* Display the message. */

MessageBox(hwnd,
lpszBuffer,
"Contents of Mailslot",
MB_OK);

GlobalFree((HGLOBAL) lpszBuffer);

fResult = GetMailslotInfo(hSlot1, /* mailslot handle */
(LPDWORD) NULL, /* no maximum message size */
&cbMessage, /* size of next message */
&cMessage, /* number of messages */
(LPDWORD) NULL); /* no read time-out */

if (!fResult) {
ErrorHandler(hwnd, "GetMailslotInfo");
return FALSE;
}
}
return TRUE;
}

A mailslot exists until the CloseHandle_win32_CloseHandle function is called for all open server handles or until all server processes that own a mailslot handle exit. In both cases, any unread messages are deleted from the mailslot, all client handles to the mailslot are closed, and the mailslot itself is deleted from memory.
+$#A Mailslot Functions
The following functions can be used with mailslots.
CreateMailslot_win32_CreateMailslotGetMailslotInfo_win32_GetMailslotInfoSetMailslotInfo_win32_SetMailslotInfo

+$#AK Memory Management
This overview describes Win32 memory management. It also explains how to allocate and use memory.
+$#A About Memory Management
In the Microsoft® Win32® application programming interface (API), each process has its own 32-bit virtual address space that enables addressing up to 4 gigabytes (GB) of memory. The 2 GB in low memory (0x00 to 0x7FFFFFFF) are available to the user, and the 2 GB in high memory (0x80000000 to 0xFFFFFFFF) are reserved for the kernel. The virtual addresses used by a process do not represent the actual physical location of an object in memory. Instead, for each process the kernel maintains a page map, an internal data structure used to translate virtual addresses into corresponding physical addresses.
+$#A Virtual Address Space and Physical Storage
The virtual address space of each process is much larger than the total physical memory, random-access memory (RAM), available to all processes. To increase the size of physical storage, the kernel uses the disk for additional storage. The total amount of storage available to all executing processes is the sum of the physical memory, RAM, and the free space on disk available to the paging file, a disk file used to increase the amount of physical storage. Physical storage and the virtual (logical) address space of each process are organized into pages, units of memory, whose size depends on the host computer. For example, on x86 computers the host page size is 4 kilobytes (K).
To maximize its flexibility in managing memory, the kernel can move pages of physical memory to and from a paging file on disk. When a page is moved in physical memory, the kernel updates the page maps of the affected processes. When the kernel needs space in physical memory, it moves the least recently used pages of physical memory to the paging file. Manipulation of physical memory by the kernel is completely transparent to applications, which operate only in their virtual address spaces.
The pages of a process's virtual address space can be in one of the following states.
State Description
Free A free page is not currently accessible, but it is available to be committed or reserved.
Reserved A reserved page is a block of the process's virtual address space that has been set aside for future use. The process cannot access a reserved page, and there is no physical storage associated with it. A reserved page reserves a range of virtual addresses that cannot be used subsequently by other allocation operations (that is, by functions such as malloc, LocalAlloc_win32_LocalAlloc, and so on). A process can use the VirtualAlloc_win32_VirtualAlloc function to reserve pages of its address space and later to commit the reserved pages. It can use the VirtualFree_win32_VirtualFree function to release them.
Committed A committed page is one for which physical storage (in memory or on disk) has been allocated. It can be protected to allow either no access or read-only access, or it can have read and write access. A process can use the VirtualAlloc function to allocate committed pages. The GlobalAlloc_win32_GlobalAlloc and LocalAlloc functions allocate committed pages with read-write access. A committed page allocated by VirtualAlloc can be decommitted by the VirtualFree function, which releases the page's storage and changes the state of the page to reserved.

+$#A Global and Local Functions
A process can use the GlobalAlloc_win32_GlobalAlloc and LocalAlloc_win32_LocalAlloc functions to allocate memory. In the linear 32-bit environment of the Win32 API, the local heap and the global heap are not distinguished. As a result, there is no difference between the memory objects allocated by these functions.
Memory objects allocated by GlobalAlloc and LocalAlloc are in private, committed pages with read-write access. Private memory cannot be accessed by other processes. Memory allocated by using GlobalAlloc with the GMEM_DDESHARE flag is not actually shared globally as it is in Windows version 3. x. However, this flag is available for compatibility purposes and can be used by some applications to enhance the performance of dynamic data exchange (DDE) operations. Applications requiring shared memory for other purposes must use file-mapping objects. Multiple processes can map a view of the same file-mapping object to provide named shared memory. For more information about file mapping, see File Mapping_win32_File_Mapping and Shared Memory_win32_Shared_Memory.
By using GlobalAlloc and LocalAlloc, you can allocate a block of memory of any size that can be represented by 32 bits. You are limited only by the available physical memory, including storage in the paging file on disk. These functions, along with the other global and local functions that manipulate global and local memory objects, are included in the Win32 API for compatibility with 16-bit versions of Windows. However, the change from a 16-bit segmented memory model to a 32-bit virtual memory model has made some of the functions and their options unnecessary or meaningless. For example, there are no longer near and far pointers, because both local and global allocations return 32-bit virtual addresses.
Both GlobalAlloc and LocalAlloc can allocate fixed or movable memory objects. Movable objects can also be marked as discardable. In earlier versions of Windows, movable memory objects were important for memory management. They enabled the system to compact the heap when necessary to make space available for other memory allocations. By using virtual memory, the system is able to manage memory by moving pages of physical memory without affecting the virtual addresses of the processes using the pages. When the system moves a page of physical memory, it simply maps the process's virtual page to the new location of the physical page. Movable memory is still useful for allocating discardable memory. When the system needs additional physical storage, it can use a "least recently used" algorithm to free discardable memory that is not locked. Discardable memory should be used for data that is needed infrequently and can be regenerated easily.
When allocating a fixed memory object, GlobalAlloc_win32_GlobalAlloc and LocalAlloc_win32_LocalAlloc return a 32-bit pointer that the calling process can immediately use to access the memory. For movable memory, the return value is a handle. To get a pointer to a movable memory object, the calling process uses the GlobalLock_win32_GlobalLock and LocalLock_win32_LocalLock functions. These functions lock the memory so that it cannot be moved or discarded. The internal data structures for each memory object include a lock count that is initially zero. For movable memory objects, GlobalLock and LocalLock increment the count by one, and the GlobalUnlock_win32_GlobalUnlock and LocalUnlock_win32_LocalUnlock functions decrement the count by one. Locked memory is not moved or discarded, unless the memory object is reallocated by using the GlobalReAlloc_win32_GlobalReAlloc or LocalReAlloc_win32_LocalReAlloc function. The memory block of a locked memory object remains locked in memory until its lock count is decreased to zero, at which time it can be moved or discarded.
The actual size of the memory allocated by GlobalAlloc or LocalAlloc can be larger than the requested size. To determine the actual number of bytes allocated, use the GlobalSize_win32_GlobalSize or LocalSize_win32_LocalSize function. If the amount allocated is greater than the amount requested, the process can use the entire amount.
The GlobalReAlloc and LocalReAlloc functions change the size, in bytes, or the attributes of a memory object allocated by GlobalAlloc and LocalAlloc. The size can increase or decrease.
The GlobalFree_win32_GlobalFree and LocalFree_win32_LocalFree functions release memory allocated by GlobalAlloc, LocalAlloc, GlobalReAlloc, or LocalReAlloc.
Other global and local functions include the GlobalDiscard_win32_GlobalDiscard, LocalDiscard_win32_LocalDiscard, GlobalFlags_win32_GlobalFlags, LocalFlags_win32_LocalFlags, GlobalHandle_win32_GlobalHandle, and LocalHandle_win32_LocalHandle functions. To discard a specified discardable memory object without invalidating the handle, use GlobalDiscard or LocalDiscard. The handle can be used later by GlobalReAlloc_win32_GlobalReAlloc or LocalReAlloc_win32_LocalReAlloc to allocate a new block of memory associated with the same handle. To return information about a specified memory object, use GlobalFlags or LocalFlags. The information includes the object's lock count and indicates whether the object is discardable or has already been discarded. To return the handle of the memory object associated with a specified pointer, use GlobalHandle or LocalHandle.
+$#A Standard C Library Functions
Win32 processes can safely use the standard C library functions (malloc, free, and so on) to manipulate memory. When used with previous versions of Windows, these functions had potential problems that no longer apply to applications using the Win32 API. For example, malloc allocates a fixed pointer that does not take advantage of movable memory. Memory management is no longer a problem because the system is free to manage memory by moving pages of physical memory without affecting the virtual addresses. Similarly, the distinction between near and far pointers is no longer relevant. So, unless you want to allocate discardable memory, it is now reasonable to use the standard C library functions for memory management.
+$#A Virtual Memory Functions
The Win32 API provides a set of virtual memory functions that enable a process to manipulate or determine the status of pages in its virtual address space. Many applications are able to satisfy their memory needs by using the standard allocation functions (GlobalAlloc_win32_GlobalAlloc, LocalAlloc_win32_LocalAlloc, malloc, and so on). However, virtual memory functions provide some capabilities not available to the standard allocation functions. They can perform the following operations:
· Reserve a range of a process's virtual address space. Reserving address space does not allocate any physical storage, but it prevents other allocation operations from using the specified range. It does not affect the virtual address spaces of other processes. Reserving pages prevents needless consumption of physical storage, while enabling a process to reserve a range of its address space into which a dynamic data structure can grow. The process can allocate physical storage for this space, as needed.
· Commit a range of reserved pages in a process's virtual address space so that physical storage (either in RAM or on disk) is accessible only to the allocating process.
· Specify read-write, read-only, or no access for a range of committed pages. This differs from the standard allocation functions that always allocate pages with read-write access.
· Free a range of reserved pages, making the range of virtual addresses available for subsequent allocation operations by the calling process.
· Decommit a range of committed pages, releasing their physical storage and making it available for subsequent allocation by any process.
· Lock one or more pages of committed memory into physical memory (RAM) so that the system cannot swap the pages out to the paging file.
· Obtain information about a range of pages in the virtual address space of the calling process or a specified process.
· Change the access protection for a specified range of committed pages in the virtual address space of the calling process or a specified process.

The virtual memory functions manipulate pages of memory. The functions use the size of a page on the current computer to round off specified sizes and addresses.
To determine the size of a page on the current computer, use the GetSystemInfo_win32_GetSystemInfo function.
The VirtualAlloc_win32_VirtualAlloc function performs one of the following operations:
· Reserves one or more free pages.
· Commits one or more reserved pages.
· Reserves and commits one or more free pages.

You can specify the starting address of the pages to be reserved or committed, or you can allow the system to determine the address. The function rounds the specified address to the appropriate page boundary. Reserved pages are not accessible, but committed pages can be allocated with the PAGE_READWRITE, PAGE_READONLY, or PAGE_NOACCESS flag. When pages are committed, storage is allocated in the paging file, but each page is initialized and loaded into physical memory only at the first attempt to read from or write to that page. You can use normal pointer references to access memory committed by the VirtualAlloc_win32_VirtualAlloc function.
The VirtualFree_win32_VirtualFree function performs one of the following operations:
· Decommits one or more committed pages, changing the state of the pages to reserved. Decommitting pages releases the physical storage associated with the pages, making it available to be allocated by any process. Any block of committed pages can be decommitted.
· Releases a block of one or more reserved pages, changing the state of the pages to free. Releasing a block of pages makes the range of reserved addresses available to be allocated by the process. Reserved pages can be released only by freeing the entire block that was initially reserved by VirtualAlloc.
· Decommits and releases a block of one or more committed pages simultaneously, changing the state of the pages to free. The specified block must include the entire block initially reserved by VirtualAlloc_win32_VirtualAlloc, and all of the pages must be currently committed.

The VirtualLock_win32_VirtualLock function enables a process to lock one or more pages of committed memory into physical memory (RAM), preventing the system from swapping the pages out to the paging file. It can be used to ensure that critical data is accessible without disk access. Locking pages into memory is dangerous because it restricts the system's ability to manage memory. Excessive use of VirtualLock can degrade system performance by causing executable code to be swapped out to the paging file. The VirtualUnlock_win32_VirtualUnlock function unlocks memory locked by VirtualLock.
The VirtualQuery_win32_VirtualQuery and VirtualQueryEx_win32_VirtualQueryEx functions return information about a region of consecutive pages beginning at a specified address in the address space of a process. VirtualQuery returns information about memory in the calling process. VirtualQueryEx returns information about memory in a specified process and is used to support debuggers that need information about a process being debugged. The region of pages is bounded by the specified address rounded down to the nearest page boundary. It extends through all subsequent pages with the following attributes in common:
· The state of all pages is the same: either committed, reserved, or free.
· If the initial page is not free, all pages in the region are part of the same initial allocation of pages that were reserved by a call to VirtualAlloc_win32_VirtualAlloc.
· The access protection of all pages is the same (that is, the PAGE_READONLY, PAGE_READWRITE, or PAGE_NOACCESS flag).

The VirtualProtect_win32_VirtualProtect function enables a process to modify the access protection of any committed page in the address space of a process. For example, a process can allocate read-write pages to store sensitive data, and then it can change the access to read only or no access to protect against accidental overwriting. VirtualProtect is typically used with pages allocated by VirtualAlloc, but it also works with pages committed by any of the other allocation functions. However, VirtualProtect changes the protection of entire pages, and pointers returned by the other functions are not necessarily aligned on page boundaries. The VirtualProtectEx_win32_VirtualProtectEx function is similar to VirtualProtect, except it changes the protection of memory in a specified process. Changing the protection is useful to debuggers in accessing the memory of a process being debugged.
+$#A Heap Functions
The heap functions enable a process to create a private heap, a block of one or more pages in the address space of the calling process. The process can then use a separate set of functions to manage the memory in that heap. There is no difference between memory allocated from a private heap and that allocated by using the standard allocation functions (GlobalAlloc_win32_GlobalAlloc, LocalAlloc_win32_LocalAlloc, malloc, and so on).
The HeapCreate_win32_HeapCreate function creates a private heap object from which the calling process can allocate memory blocks by using the HeapAlloc_win32_HeapAlloc function. HeapCreate specifies both an initial size and a maximum size for the heap. The initial size determines the number of committed, read-write pages initially allocated for the heap. The maximum size determines the total number of reserved pages. These pages create a contiguous block in the virtual address space of a process into which the heap can grow. Additional pages are automatically committed from this reserved space if requests by HeapAlloc exceed the current size of committed pages, assuming that the physical storage for it is available. Once the pages are committed, they are not decommitted until the process is terminated or until the heap is destroyed by calling the HeapDestroy_win32_HeapDestroy function.
The memory of a private heap object is accessible only to the process that created it. If a dynamic-link library (DLL) creates a private heap, it does so in the address space of the process that called the DLL. It is accessible only to that process.
The HeapAlloc function allocates a specified number of bytes from a private heap and returns a pointer to the allocated block. The pointer identifies the block for the HeapFree_win32_HeapFree function to release or for the HeapSize_win32_HeapSize function to determine the size.
Memory allocated by HeapAlloc is not movable. Because the system cannot compact a private heap, the heap can become fragmented.
A possible use for the heap functions is to create a private heap when a process starts up, specifying an initial size sufficient to satisfy the memory requirements of the process. If the call to the HeapCreate_win32_HeapCreate function fails, the process can terminate or notify the user of the memory shortage; if it succeeds, however, the process is assured of having the memory it needs.
+$#A Shared Memory
In the Win32 API, shared memory is implemented by file mapping. All memory allocated by the other allocation methods (the GlobalAlloc_win32_GlobalAlloc, LocalAlloc_win32_LocalAlloc, HeapAlloc_win32_HeapAlloc, or VirtualAlloc_win32_VirtualAlloc function) is accessible only to the calling process. Memory allocated by a DLL, however, is in the address space of the process that called the DLL and is not accessible to other processes using the same DLL.
Named file mapping provides an easy way to create a block of shared memory. A process can specify a name when it uses the CreateFileMapping_win32_CreateFileMapping function to create a file-mapping object. Other processes can specify the same name to either the CreateFileMapping or OpenFileMapping_win32_OpenFileMapping function to obtain a handle of the mapping object. The names of event objects, semaphore objects, mutex objects, and file-mapping objects share the same name space. If a specified name matches the name of an existing object of a different type, an error occurs. When creating named objects, try to use unique names and check the function return values for duplicate name errors.
Each process specifies its handle of the file-mapping object in the MapViewOfFile_win32_MapViewOfFile function to map a view of the file into its own address space. The views of all processes for a single file-mapping object are mapped into the same sharable pages of physical storage. However, the virtual addresses of the mapped views can vary from one process to another, unless the MapViewOfFileEx_win32_MapViewOfFileEx function is used to map the view at a specified address. Although sharable, the pages of physical storage used for a mapped file view are not global; they are not accessible to processes that have not mapped a view of the file.
A file-mapping object is associated with a disk file that the system uses when the mapped view is swapped out of physical memory and onto the disk. This disk swap file can be the system's paging file, or it can be some other file that was specified when the file-mapping object was created. In that case, the memory is initialized along with the contents of the file. Mapping a specified file in the file system is useful for processes that need to share the data in an existing file or that want to use the file to save data generated by the sharing processes. If you map a specified file, you should open it for exclusive access and keep the handle open until you are finished with the shared memory. Keeping it open will prevent other processes from opening another handle on the file to use ReadFile_win32_ReadFile or WriteFile_win32_WriteFile or from creating additional mapping objects for the same file, any of which actions can lead to unpredictable results.
Any pages committed by mapping a view of a file are released when the last process with a view of the mapping object either terminates or unmaps its view by calling the UnmapViewOfFile_win32_UnmapViewOfFile function. At this time, the specified file (if any) associated with the mapping object is updated. A specified file can also be forced to update by calling the FlushViewOfFile_win32_FlushViewOfFile function.
For more information about file mapping, see File Mapping_win32_File_Mapping. For an example of shared memory in a DLL, see Dynamic-Link Libraries_win32_Dynamic_Link_Libraries.
If multiple processes have write access to shared memory, access to the memory should be synchronized. For more information about interprocess synchronization, see Synchronization_win32_Synchronization.
+$#A Access Validation
The Win32 API provides a set of functions that a process can use to verify whether it has a specified type of access to a given memory address or range of addresses. The following access validation functions are available.
Function Description
IsBadCodePtr_win32_IsBadCodePtr Determines whether the calling process has read access to the memory at the specified address.
IsBadReadPtr_win32_IsBadReadPtr Determines whether the calling process has read access to the memory at a specified range of addresses.
IsBadStringPtr_win32_IsBadStringPtr Determines whether the calling process has read access to the memory pointed to by a null-terminated string pointer. The function validates access for a specified number of characters or until it encounters the string's terminating null character.
IsBadWritePtr_win32_IsBadWritePtr Determines whether the calling process has write access to the memory at a specified range of addresses.

The IsBadHugeReadPtr_win32_IsBadHugeReadPtr and IsBadHugeWritePtr_win32_IsBadHugeWritePtr functions are also available for compatibility with earlier versions of Windows that distinguished between normal memory allocations and huge allocations occupying multiple segments. In the Win32 API, these functions are equivalent to IsBadReadPtr_win32_IsBadReadPtr and IsBadWritePtr_win32_IsBadWritePtr.
In a preemptive multitasking environment, it is possible for some other thread to change the process's access to the memory being tested. Even when an access validation function indicates that the process has the desired access to the specified memory, you should use structured exception handling when attempting to access the memory. Use of structured exception handling enables the system to notify the process if an access violation exception occurs, giving the process an opportunity to handle the exception. For more information about structured exception handling, see Structured Exception Handling_win32_Structured_Exception_Handling.
+$#A Guard Pages
An application establishes a guard page by setting a memory page's PAGE_GUARD page protection modifier flag. This flag can be specified, along with other page protection flags, in the functions VirtualAlloc_win32_VirtualAlloc, VirtualProtect_win32_VirtualProtect, and VirtualProtectEx_win32_VirtualProtectEx. The PAGE_GUARD flag can be used with any other page protection flag, except for the NO_ACCESS flag.
If a program attempts to access an address within a guard page, the operating system raises a STATUS_GUARD_PAGE (0x80000001) exception. The operating system also clears the PAGE_GUARD flag, removing the memory page's guard page status. The system will not stop the next attempt to access the memory page with a STATUS_GUARD_PAGE exception.
If a guard page exception occurs during a system service, the service fails and typically returns some failure status indicator. Since the system also removes the relevant memory page's guard page status, the next invocation of the same system service won't fail due to a STATUS_GUARD_PAGE exception (unless, of course, someone reestablishes the guard page).
A guard page thus provides a one-shot alarm for memory page access. This can be useful for an application that needs to monitor the growth of large dynamic data structures. For example, there are operating systems that use guard pages to implement automatic stack checking.
The following short program illustrates the one-shot behavior of guard page protection, and how it can cause a system service to fail:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{

// local variables
LPVOID lpvAddr;
DWORD cbSize;
BOOL vLock;
LPVOID commit;

// amount of memory we'll allocate
cbSize = 512;

// try to allocate some memory
lpvAddr = VirtualAlloc(NULL,cbSize,MEM_RESERVE,PAGE_NOACCESS);

// if we failed ...
if(lpvAddr == NULL)
fprintf(stdout,"VirtualAlloc failed on RESERVE with %ld\n",
GetLastError());

// try to commit the allocated memory
commit = VirtualAlloc(NULL,cbSize,MEM_COMMIT,PAGE_READONLY|PAGE_GUARD);

// if we failed ...
if(commit == NULL)
fprintf(stderr,"VirtualAlloc failed on COMMIT with %ld\n",
GetLastError());

else // we succeeded
fprintf(stderr,"Committed %lu bytes at address %lp\n",
cbSize,commit);

// try to lock the committed memory
vLock = VirtualLock(commit,cbSize);

// if we failed ...
if(!vLock)
fprintf(stderr,"Cannot lock at %lp, error = %lu\n",
commit,GetLastError());
else // we succeeded
fprintf(stderr,"Lock Achieved at %lp\n",commit);

// try to lock the committed memory again
vLock = VirtualLock(commit,cbSize);

// if we failed ...
if(!vLock)
fprintf(stderr,"Cannot get 2nd lock at %lp, error = %lu\n",
commit,GetLastError());
else // we succeeded
fprintf(stderr,"2nd Lock Achieved at %lp\n",commit);

} // endof function

The output of this program looks like this:
Committed 512 bytes at address 003F0000
Cannot lock at 003F0000, error = 0x80000001
2nd Lock Achieved at 003F0000

Note that the first attempt to lock the memory block fails, raising a STATUS_GUARD_PAGE exception. The second attempt succeeds, because the memory block's guard page protection has been toggled off by the first attempt.
+$#A Using the Virtual Memory Functions
This section explains how to use the virtual memory functions for dynamic allocation.
The following example illustrates the use of the VirtualAlloc_win32_VirtualAlloc and VirtualFree_win32_VirtualFree functions in reserving and committing memory as needed for a dynamic array. First, VirtualAlloc is called to reserve a block of pages with NULL specified as the base address parameter, forcing the kernel to determine the location of the block. Later, VirtualAlloc is called whenever it is necessary to commit a page from this reserved region, and the base address of the next page to be committed is specified.
The example uses try-except structured exception-handling syntax to commit pages from the reserved region. Whenever a page fault exception occurs during the execution of the try block, the filter function in the expression preceding the except block is executed. If the filter function can allocate another page, execution continues in the try block at the point where the exception occurred. Otherwise, the exception handler in the except block is executed. For more information about structured exception handling, see Structured Exception Handling_win32_Structured_Exception_Handling.
As an alternative to dynamic allocation, the process can simply commit the entire region instead of only reserving it. However, committing the region consumes physical storage that might not be needed, making it unavailable for use by other processes.
The example uses VirtualFree to free the reserved and committed pages when it is finished with them. The function is called twice: first to decommit the committed pages and again to release the entire region of reserved pages.
#define PAGELIMIT 80
#define PAGESIZE 0x1000

INT PageFaultExceptionFilter(DWORD);
VOID MyErrorExit(LPTSTR);

LPTSTR lpNxtPage;
DWORD dwPages = 0;

VOID UseDynamicVirtualAlloc(VOID) {
LPVOID lpvBase;
LPTSTR lpPtr;
BOOL bSuccess;
DWORD i;

/* Reserve pages in the process's virtual address space. */

lpvBase = VirtualAlloc(
NULL, /* system selects address */
PAGELIMIT*PAGESIZE, /* size of allocation */
MEM_RESERVE, /* allocates reserved pages */
PAGE_NOACCESS); /* protection = no access */
if (lpvBase == NULL )
MyErrorExit("VirtualAlloc reserve");

lpPtr = lpNxtPage = (LPTSTR) lpvBase;

/*
* Use try-except structured exception handling when
* accessing the pages. If a page fault occurs, the
* exception filter is executed to commit another page
* from the reserved block of pages.
*/

for (i=0; i < PAGELIMIT*PAGESIZE; i++) {

try {

/* Write to memory. */

lpPtr[i] = 'a';
}

/*
* If there is a page fault, commit another page
* and try again.
*/

except ( PageFaultExceptionFilter(
GetExceptionCode() ) ) {

/*
* This is executed only if the filter function is
* unsuccessful in committing the next page.
*/

ExitProcess( GetLastError() );

}

}

/* Release the block of pages when you are finished using them. */

/* First, decommit the committed pages. */

bSuccess = VirtualFree(
lpvBase, /* base address of block */
dwPages*PAGESIZE, /* bytes of committed pages */
MEM_DECOMMIT); /* decommit the pages */

/* Release the entire block. */

if (bSuccess)
bSuccess = VirtualFree(
lpvBase, /* base address of block */
0, /* releases the entire block */
MEM_RELEASE); /* releases the pages */

}

INT PageFaultExceptionFilter(DWORD dwCode) {
LPVOID lpvResult;

/* If the exception is not a page fault, exit. */

if (dwCode != EXCEPTION_ACCESS_VIOLATION) {
printf("exception code = %d\n", dwCode);
return EXCEPTION_EXECUTE_HANDLER;
}

printf("page fault\n");

/* If the reserved pages are used up, exit. */

if (dwPages >= PAGELIMIT) {
printf("out of pages\n");
return EXCEPTION_EXECUTE_HANDLER;
}

/* Otherwise, commit another page. */

lpvResult = VirtualAlloc(
(LPVOID) lpNxtPage, /* next page to commit */
PAGESIZE, /* page size, in bytes */
MEM_COMMIT, /* alloc committed page */
PAGE_READWRITE); /* read-write access */
if (lpvResult == NULL ) {
printf("VirtualAlloc failed\n");
return EXCEPTION_EXECUTE_HANDLER;
}

/*
* Increment the page count, and advance lpNxtPage
* to the next page.
*/

dwPages++;
lpNxtPage += PAGESIZE;

/* Continue execution where the page fault occurred. */

return EXCEPTION_CONTINUE_EXECUTION;

}

+$#A Memory Management Functions
Following are the Win32 functions used in memory management.
CopyMemory_win32_CopyMemoryFillMemory_win32_FillMemoryGetProcessHeap_win32_GetProcessHeapGetProcessHeaps_win32_GetProcessHeapsGlobalAlloc_win32_GlobalAllocGlobalDiscard_win32_GlobalDiscardGlobalFlags_win32_GlobalFlagsGlobalFree_win32_GlobalFreeGlobalHandle_win32_GlobalHandleGlobalLock_win32_GlobalLockGlobalMemoryStatus_win32_GlobalMemoryStatusGlobalReAlloc_win32_GlobalReAllocGlobalSize_win32_GlobalSizeGlobalUnlock_win32_GlobalUnlockHeapAlloc_win32_HeapAllocHeapCompact_win32_HeapCompactHeapCreate_win32_HeapCreateHeapDestroy_win32_HeapDestroyHeapFree_win32_HeapFreeHeapLock_win32_HeapLockHeapReAlloc_win32_HeapReAllocHeapSize_win32_HeapSizeHeapUnlock_win32_HeapUnlockHeapValidate_win32_HeapValidateIsBadCodePtr_win32_IsBadCodePtrIsBadHugeReadPtr_win32_IsBadHugeReadPtrIsBadHugeWritePtr_win32_IsBadHugeWritePtrIsBadReadPtr_win32_IsBadReadPtrIsBadStringPtr_win32_IsBadStringPtrIsBadWritePtr_win32_IsBadWritePtrLocalAlloc_win32_LocalAllocLocalDiscard_win32_LocalDiscardLocalFlags_win32_LocalFlagsLocalFree_win32_LocalFreeLocalHandle_win32_LocalHandleLocalLock_win32_LocalLockLocalReAlloc_win32_LocalReAllocLocalSize_win32_LocalSizeLocalUnlock_win32_LocalUnlockMoveMemory_win32_MoveMemoryVirtualAlloc_win32_VirtualAllocVirtualFree_win32_VirtualFreeVirtualLock_win32_VirtualLockVirtualProtect_win32_VirtualProtectVirtualProtectEx_win32_VirtualProtectExVirtualQuery_win32_VirtualQueryVirtualQueryEx_win32_VirtualQueryExVirtualUnlock_win32_VirtualUnlockZeroMemory_win32_ZeroMemory
Obsolete or Deleted Functions
ChangeSelector_win32_ChangeSelectorDefineHandleTable_win32_DefineHandleTableFreeSelector_win32_FreeSelectorGetCodeInfo_win32_GetCodeInfoGetFreeSpace_win32_GetFreeSpaceGlobalCompact_win32_GlobalCompactGlobalDosAlloc_win32_GlobalDosAllocGlobalDosFree_win32_GlobalDosFreeGlobalFix_win32_GlobalFixGlobalLRUNewest_win32_GlobalLRUNewestGlobalLRUOldest_win32_GlobalLRUOldestGlobalNotify_win32_GlobalNotifyGlobalPageLock_win32_GlobalPageLockGlobalPageUnlock_win32_GlobalPageUnlockGlobalUnfix_win32_GlobalUnfixGlobalUnWire_win32_GlobalUnWireGlobalWire_win32_GlobalWireLimitEmsPages_win32_LimitEmsPagesLocalCompact_win32_LocalCompactLocalInit_win32_LocalInitLocalShrink_win32_LocalShrinkLockSegment_win32_LockSegmentSetSwapAreaSize_win32_SetSwapAreaSizeSwitchStackBack_win32_SwitchStackBackSwitchStackTo_win32_SwitchStackToUnlockSegment_win32_UnlockSegment

+$#AK Performance Monitoring
Performance monitoring refers to the procedure for determining the system resources that an application consumes, such as processor time and memory.
+$#A About Performance Monitoring
An application developer can use the Microsoft® Win32® performance monitoring functions to determine whether an application uses system resources efficiently and to fine-tune its performance. These functions are especially helpful in evaluating the effects of changes as developers migrate Microsoft® Windows® version 3.x applications to Win32.
+$#A Counters and Elapsed Times
A counter is a general term used in programming to refer to an incrementing variable. Some systems include a high-resolution performance counter that provides high-resolution elapsed times. The elapsed time can be used to fine-tune sections of source code. Win32 provides two functions that enable application developers to obtain the value of a counter.
If a high-resolution performance counter exists on the system, the QueryPerformanceFrequency_win32_QueryPerformanceFrequency function can be used to express the frequency, in counts per second. The value of the count is processor dependent. On some processors, for example, the count might be the cycle rate of the processor clock.
The QueryPerformanceCounter_win32_QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter (if one exists on the system). By calling this function at the beginning and end of a section of code, an application essentially uses the counter as a stopwatch.
For example, QueryPerformanceFrequency might indicate that the frequency of the high-resolution performance counter on the system is 50,000 counts per second. If the application calls QueryPerformanceCounter immediately before and immediately after the section of code to be timed, the counter values might be 1500 counts and 3500 counts, respectively. These values would indicate that .04 seconds (2000 counts) elapsed while the code executed.
+$#A Registry Database
Windows includes several tools that monitor the system. To present data in a different format, to use data inside another application, or to create a performance monitor, an application must retrieve information through the registry. For more information about the registry, see Registry_win32_Registry.
+$#A Getting Performance Data
An application can obtain system-performance information by using the RegQueryValueEx_win32_RegQueryValueEx registry function with the HKEY_PERFORMANCE_DATA key. The application can use the RegOpenKey_win32_RegOpenKey function to open the HKEY_PERFORMANCE_DATA handle, but it should use RegCloseKey_win32_RegCloseKey to close the handle when it has finished running. This allows monitored software to be to be removed or installed, which cannot happen while they are being monitored. Network transports and drivers are examples of monitored software you may wish to remove.
Performance data is not stored in the registry. Using the RegQueryValueEx function with the HKEY_PERFORMANCE_DATA key causes the system to collect the data from the appropriate system object managers.
If an application is accessing a remote system, it must use the RegConnectRegistry_win32_RegConnectRegistry function with the name of the remote system and the HKEY_PERFORMANCE_DATA key. RegConnectRegistry retrieves a key that the application can use with RegQueryValueEx to retrieve performance data from the remote system.
The basic layout of the data retrieved by RegQueryValueEx is a PERF_DATA_BLOCK_win32_PERF_DATA_BLOCK_str structure followed by one PERF_OBJECT_TYPE_win32_PERF_OBJECT_TYPE_str structure and accompanying data for each type of object being monitored. The system being observed defines objects that can be monitored. Typical object types include processors, disks, and memory.
The PERF_DATA_BLOCK structure describes the system and the performance data. This structure contains the size of the data, the number of objects being monitored, the frequency and value of the high-resolution performance counter, an offset to the system name, and so on.
The PERF_OBJECT_TYPE structure describes the performance data for one type of object. This structure contains the size of the structure and the object-specific data that follows the structure, the number of counters defined for the object type, the number of instances of this object type, the level of informational details provided, the code page, indices to the object name and Explain title in the title database, and so on.
Following each PERF_OBJECT_TYPE structure, there is a list of PERF_COUNTER_DEFINITION_win32_PERF_COUNTER_DEFINITION_str structures, one for each counter defined for the object. The PERF_COUNTER_DEFINITION structure specifies the type and size of the counter, the offset to the counter, the detail level, the counter name, and Explain title in the registry, and so on.
An instance is a unique copy of a particular object type. The number of instances of a particular object type determines what follows the list of counter definitions. Not all object types have instances. A memory object, for example, has no instances because the system has only one memory. If the object type does not have instances, the counter definitions are followed by a single PERF_COUNTER_BLOCK_win32_PERF_COUNTER_BLOCK_str structure and data for each counter. PERF_COUNTER_BLOCK contains the total length of the structure, in bytes, and the counter data that follows it.
If the object type has one or more instances, the list of counter definitions is followed by a PERF_INSTANCE_DEFINITION_win32_PERF_INSTANCE_DEFINITION_str structure and a PERF_COUNTER_BLOCK structure for each instance. The PERF_INSTANCE_DEFINITION structure includes the name, identifier, and name of the parent of the instance. For example, the thread object type has instances; each thread in the system is an instance.
The number of object types, instances, and counters may vary from one call to the RegQueryValueEx_win32_RegQueryValueEx function to another. Therefore, the number and size of structures included in the performance data as well as the size of the data that follows each structure may also vary. To ensure that it is receiving the appropriate information, an application must use the offsets included in the performance structures to navigate through the performance data. Every offset is relative to the beginning of the structure containing it.
+$#A Getting Descriptive Text
The names of the objects and counters, as well as the text that explains their meaning, can be obtained by calling the RegQueryValueEx_win32_RegQueryValueEx function with hkey set to HKEY_PERFORMANCE_DATA and lpszValueName set to "Counter <langid>" or "Explain <langid>", where <langid> is a language identifier string. A language identifier string is the ASCII representation of a 3-digit hexadecimal language identifier. For example, the code for English is "009"; calling RegQueryValueEx with "Counter 009" will obtain the object names in English.
The 'Counter' and 'Explain' data are stored as MULTI_SZ strings. This type of data consists of a list of Unicode strings, each terminated with UNICODE_NULL. The last string is followed by an additional UNICODE_NULL.
The strings are listed in pairs. The first item is the Unicode string of the index contained in PERF_OBJECT_TYPE_win32_PERF_OBJECT_TYPE_str.ObjectNameTitleIndex or PERF_COUNTER_DEFINITION_win32_PERF_COUNTER_DEFINITION_str.CounterNameTitleIndex. The second item is the actual name of the index. Following is a fragment of the paired data:
2 System
4 Memory
6 % Processor Time
10 Read Operations/sec
12 Write Operations/sec

To retrieve a name or the Explain text for an item, an application should perform the following steps:
1. If the system is remote, call the RegConnectRegistry_win32_RegConnectRegistry function to establish a connection to the remote machine's registry.
2. Call the RegQueryValueEx_win32_RegQueryValueEx function, specifying either 'Counter <langid>' or 'Explain <langid>' as the name of the value to query. This provides MULTI_SZ data containing either the names of all the objects and counters, or Help information for all the objects and counters. Note that these query strings are case-sensitive.
If the call to RegQueryValueEx is successful, the return value is ERROR_SUCCESS, and the names are returned in the buffer pointed to by the function's lpbData parameter.
If the input buffer size is 0, the RegQueryValueEx return value is the buffer size needed to store the names.
If the input buffer size is too small, the RegQueryValueEx return value is ERROR_MORE_DATA.
If the specified language code is not installed in the system, the RegQueryValueEx_win32_RegQueryValueEx return value is ERROR_FILE_NOT_FOUND.
Refer to RegQueryValueEx for further details on its operation.
3. Convert the appropriate index (PERF_OBJECT_TYPE_win32_PERF_OBJECT_TYPE_str.ObjectNameTitleIndex, PERF_OBJECT_TYPE.ObjectHelpTitleIndex, PERF_COUNTER_DEFINITION_win32_PERF_COUNTER_DEFINITION_str.CounterNameTitleIndex, or PERF_COUNTER_DEFINITION.CounterHelpTitleIndex) into Unicode or ASCII, depending on whether the application is Unicode or ASCII.
4. Search the MULTI_SZ data for the appropriate index.
5. Retrieve the string following the matching index as the object's name or descriptive information.

See Displaying Object, Counter, and Instance Names_win32_Displaying_Object_Counter_and_Instance_Names and Displaying Counter Title Indices_win32_Displaying_Counter_Title_Indices for sample code illustrating these tasks.
+$#A Getting Selected Data
When using the RegQueryValueEx_win32_RegQueryValueEx function, an application must use the lpcbData parameter to specify a byte count of the amount of data to retrieve. Estimating this amount is difficult because the number of object types and their instances is determined by the system configuration and system activity. The amount may vary from one request to another. If the function does not pass enough space, the return value will be ERROR_MORE_DATA. Therefore, an application should include a retry loop in which it passes increasing amounts of buffer space until it no longer gets this error. The successful buffer size should be the starting point for subsequent calls to RegQueryValueEx.
In some cases, the application may not need to retrieve all the performance data provided by the system. Retrieving all of that data may be costly in terms of processor or memory requirements. The lpszValueName parameter of RegQueryValueEx indicates the amount of information to retrieve. This parameter can be in one of the following formats. The strings are case-sensitive. If a string includes more than one word, the words must be separated by a space.
String Description
Global The function returns all data for counters on the local computer except those included in the Costly category, described later in this table.
nnn xx yyy Each of these strings is a Unicode string representing the decimal value for PERF_OBJECT_TYPE_win32_PERF_OBJECT_TYPE_str.ObjectNameTitleIndex. The function returns data only about the specified object types on the local computer.
Note that more object types may be returned than are requested. This situation occurs if other object types are needed to support the requested data; for example, processes are needed to identify threads, so processes will also be returned even if only threads are requested.
Foreign ssss The string ssss is the name of a foreign computer ¾ that is, one that does not support the registry calls for returning data remotely. The function returns all data for counters on the foreign computer if your system is capable of collecting data from a foreign computer. This approach can be tried if RegConnectRegistry_win32_RegConnectRegistry fails to connect to a foreign computer.
Foreign ssss nnn xx yyy This format combines Foreign ssss and nnn xx yyy, discussed previously. The function returns all data for the specified counters on the foreign computer if the system is capable of collecting data from a foreign computer. An application can try this approach if RegConnectRegistry fails to connect to a foreign computer, but all of the available data is not wanted.
Costly The function returns data for object types whose data is expensive to collect in terms of processor time or memory usage. A secondary thread may be started if an application needs to respond to the user during data collection.

+$#A Using Performance Monitoring
This section includes examples of code that display objects, counters, and Explain text for a system.
· Displaying object, counter, and instance names_win32_Displaying_Object_Counter_and_Instance_Names
· Displaying counter title indices_win32_Displaying_Counter_Title_Indices

+$#A Displaying Object, Counter, and Instance Names
The example in this section shows how an application can display object, counter, and instance names.
#define UNICODE 1
#define _UNICODE 1
//
// "C" Include files
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
//
// Windows Include files
//
#include <windows.h>
#include <winperf.h>
#include <tchar.h>
//
// Local constants
//
#define RESERVED 0L // reserved argument in fn. calls
#define INITIAL_SIZE 40960L // initial perf data buff size
#define EXTEND_SIZE 4096L // extend size of perf data buff
#define LINE_LENGTH 80 // display line length

#define WRAP_POINT LINE_LENGTH-12 // wrap point for explain text
#define OBJECT_TEXT_INDENT 4 // indent of object explain text
//
// Command line option values
//
#define COMMAND_ERROR 0
#define COMMAND_HELP 1
#define COMMAND_COUNTER 2
#define COMMAND_NOT_FOUND 3
//
//
// Text string Constant definitions
//
const LPTSTR NamesKey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
const LPTSTR DefaultLangId = TEXT("009");
const LPTSTR Counters = TEXT("Counter ");
const LPTSTR Help = TEXT("Explain ");
const LPTSTR LastHelp = TEXT("Last Help");
const LPTSTR LastCounter = TEXT("Last Counter");
const LPTSTR Slash = TEXT("\\");
const LPTSTR Global = TEXT("Global");
//
// Global Buffers
//
TCHAR cValueName[LINE_LENGTH]; // used to store counter value string
CHAR cExeName[LINE_LENGTH*2]; // used in processing command line

CHAR cCtrName[LINE_LENGTH]; // used in processing command line

BOOL
StringToInt (
IN LPTSTR InStr,
OUT PDWORD pdwOutInt
)
/*++

StringToInt

Returns the decimal interpretation of the string in the InStr
argument. Works for either Unicode or ANSI strings.

Arguments:

InStr
string to translate

pdwOutInt
base 10 version of the number in the InStr argument

Return Value:

TRUE if conversion was successful
FALSE if not

--*/
{

// check for NULL pointers passed to function

if ((!InStr) || (!pdwOutInt)) {
SetLastError (ERROR_BAD_ARGUMENTS);
return FALSE;
}

if ( (_stscanf (InStr, TEXT(" %d"), pdwOutInt)) == 1) {
return TRUE;
} else {
return FALSE;
}
} // StringToInt

VOID
DisplayCommandHelp (
)
/*++

DisplayCommandHelp

displays usage of command line arguments

Arguments

NONE

Return Value

NONE

--*/
{
printf ("\n\nPERFEX:");

printf ("\n Sample program to demonstrate Win32 Performance API");
printf ("\n\nUsage:");
printf ("\n\n perfex [-? | counter]");
printf ("\n\n -? Displays a list of all counter objects in the system");
printf ("\n counter is the name of a counter object");
printf ("\n and displays the performance counters for that object");
printf ("\n");
} // DisplayCommandHelp

VOID
DisplayNotFound (
)
/*++

DisplayNotFound

displays not found & usage of command line arguments

Arguments

NONE

Return Value

NONE

--*/
{
printf ("\n*** Counter object not found in the system***\n");
DisplayCommandHelp(); // display command line usage
} // DisplayNotFound

LPTSTR
*BuildNameTable(
HKEY hKeyRegistry, // handle to registry db with counter names
LPTSTR lpszLangId, // unicode value of Language subkey

PDWORD pdwLastItem // size of array in elements
)
/*++

BuildNameTable

Caches the counter names and explain text to accelerate name lookups
for display.

Arguments:

hKeyRegistry
Handle to an open registry (this can be local or remote.) and
is the value returned by RegConnectRegistry or a default key.

lpszLangId
The unicode id of the language to look up. (default is 009)

pdwLastItem
The last array element

Return Value:

pointer to an allocated table.
(the caller must free it when finished!)

the table is an array of pointers to zero terminated TEXT strings.

A NULL pointer is returned if an error occured. (error value is
available using the GetLastError function).

The structure of the buffer returned is:

Array of pointers to zero terminated strings consisting of
pdwLastItem elements

MULTI_SZ string containing counter id's and names returned from
registry for the specified language

MULTI_SZ string containing explain text id's
and explain text strings
as returned by the registry for the specified language

The structures listed above are contiguous so that they may be freed
by a single "free" call when finished with them, however only the
array elements are intended to be used.

--*/
{

LPTSTR *lpReturnValue; // returned pointer to buffer

LPTSTR *lpCounterId; //
LPTSTR lpCounterNames; // pointer to Names buffer returned by reg.
LPTSTR lpHelpText ; // pointet to exlpain buffer returned by reg.

LPTSTR lpThisName; // working pointer

BOOL bStatus; // return status from TRUE/FALSE fn. calls
LONG lWin32Status; // return status from fn. calls

DWORD dwHelpItems; // number of explain text items
DWORD dwCounterItems; // number of counter text items
DWORD dwValueType; // value type of buffer returned by reg.
DWORD dwArraySize; // size of pointer array in bytes
DWORD dwBufferSize; // size of total buffer in bytes
DWORD dwCounterSize; // size of counter text buffer in bytes
DWORD dwHelpSize; // size of help text buffer in bytes
DWORD dwThisCounter; // working counter

DWORD dwLastId; // largest ID value used by explain/counter text

HKEY hKeyValue; // handle to Perflib entry in registry

TCHAR CounterNameBuffer[LINE_LENGTH]; // buffer conatining Counter query
TCHAR HelpNameBuffer[LINE_LENGTH]; // buffer conatining Help query
BOOL bClosePerformanceKey; // flag for closing HKEY_PERFORMANCE_DATA key

//initialize data

lpReturnValue = NULL;
hKeyValue = NULL;
bClosePerformanceKey = FALSE;

// check for null arguments and insert defaults if necessary

if (!lpszLangId) {
lpszLangId = DefaultLangId;
}

// open handle to Perflib key in registry to get number of items
// used in order to compute array and buffer sizes

lWin32Status = RegOpenKeyEx (
hKeyRegistry,
NamesKey,
RESERVED,
KEY_READ,
&hKeyValue);

// on error, free buffers, close keys and return NULL pointer

if (lWin32Status != ERROR_SUCCESS) {
goto BNT_BAILOUT;
}

// query registry to get number of Explain text items

dwBufferSize = sizeof (dwHelpItems);
lWin32Status = RegQueryValueEx (
hKeyValue,
LastHelp,
RESERVED,
&dwValueType,
(LPBYTE)&dwHelpItems,
&dwBufferSize);

if ((lWin32Status != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) {
goto BNT_BAILOUT;
}

// query registry to get number of counter and object name items

dwBufferSize = sizeof (dwCounterItems);
lWin32Status = RegQueryValueEx (
hKeyValue,
LastCounter,
RESERVED,
&dwValueType,
(LPBYTE)&dwCounterItems,
&dwBufferSize);

if ((lWin32Status != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) {
goto BNT_BAILOUT;
}

// use the greater of Help items or Counter Items to size array

if (dwHelpItems >= dwCounterItems) {
dwLastId = dwHelpItems;
} else {
dwLastId = dwCounterItems;
}

// array size is # of elements (+ 1, since names are "1" based)
// times the size of a pointer

dwArraySize = (dwLastId + 1) * sizeof(LPTSTR);

// build the Query strings for the specified language ID

lstrcpy (CounterNameBuffer, Counters);
lstrcat (CounterNameBuffer, lpszLangId);

lstrcpy (HelpNameBuffer, Help);
lstrcat (HelpNameBuffer, lpszLangId);

// get size of counter names

dwBufferSize = 0;
lWin32Status = RegQueryValueEx (
HKEY_PERFORMANCE_DATA,
CounterNameBuffer,
RESERVED,
&dwValueType,
NULL,
&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

bClosePerformanceKey = TRUE;
dwCounterSize = dwBufferSize;

// get size of help text

dwBufferSize = 0;
lWin32Status = RegQueryValueEx (
HKEY_PERFORMANCE_DATA,
HelpNameBuffer,
RESERVED,
&dwValueType,
NULL,
&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

dwHelpSize = dwBufferSize;

// allocate buffer with room for pointer array, counter name
// strings and help name strings

lpReturnValue = malloc (dwArraySize + dwCounterSize + dwHelpSize);

if (!lpReturnValue) {
lWin32Status = ERROR_OUTOFMEMORY;
goto BNT_BAILOUT;
}

// initialize pointers into buffer

lpCounterId = lpReturnValue;
lpCounterNames = (LPTSTR)((LPBYTE)lpCounterId + dwArraySize);

lpHelpText = (LPTSTR)((LPBYTE)lpCounterNames + dwCounterSize);

// initialize the pointers to NULL
for (dwBufferSize = 0; dwBufferSize <= dwLastId; dwBufferSize++) {
lpCounterId[dwBufferSize] = NULL;
}

// read counter names into buffer. Counter names will be stored as
// a MULTI_SZ string in the format of "###" "Name"

dwBufferSize = dwCounterSize;
lWin32Status = RegQueryValueEx (
HKEY_PERFORMANCE_DATA,
CounterNameBuffer,
RESERVED,
&dwValueType,
(LPVOID)lpCounterNames,
&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

// read explain text into buffer. Counter names will be stored as

// a MULTI_SZ string in the format of "###" "Text..."

dwBufferSize = dwHelpSize;
lWin32Status = RegQueryValueEx (
HKEY_PERFORMANCE_DATA,
HelpNameBuffer,
RESERVED,
&dwValueType,
(LPVOID)lpHelpText,
&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

// load counter array items, by locating each text string
// in the returned buffer and loading the
// address of it in the corresponding pointer array element.

for (lpThisName = lpCounterNames;

*lpThisName;
lpThisName += (lstrlen(lpThisName)+1) ) {

// first string should be an integer
// (in decimal digit characters)
// so translate to an integer for use in array
// element identification

bStatus = StringToInt (lpThisName, &dwThisCounter);

if (!bStatus) {
// error is in GetLastError
goto BNT_BAILOUT; // bad entry
}

// point to corresponding counter name which follows the id number
// string.

lpThisName += (lstrlen(lpThisName)+1);

// and load array element with pointer to string

lpCounterId[dwThisCounter] = lpThisName;

}

// repeat the above for the explain text strings

for (lpThisName = lpHelpText;
*lpThisName;
lpThisName += (lstrlen(lpThisName)+1) ) {

// first string should be an integer (in decimal unicode digits)

bStatus = StringToInt (lpThisName, &dwThisCounter);

if (!bStatus) {
// error is in GetLastError
goto BNT_BAILOUT; // bad entry

}

// point to corresponding counter name

lpThisName += (lstrlen(lpThisName)+1);

// and load array element;

lpCounterId[dwThisCounter] = lpThisName;

}

// if the last item arugment was used,
// then load the last ID value in it

if (pdwLastItem) *pdwLastItem = dwLastId;

// close the registry keys

RegCloseKey (hKeyValue);
RegCloseKey (HKEY_PERFORMANCE_DATA);

// exit returning the pointer to the buffer

return lpReturnValue;

BNT_BAILOUT:
if (lWin32Status != ERROR_SUCCESS) {
// if lWin32Status has error, then set last error value to it,
// otherwise assume that last error already has value in it
SetLastError (lWin32Status);
}

if (lpReturnValue) {
free ((LPVOID)lpReturnValue);
}

if (bClosePerformanceKey == TRUE) RegCloseKey (HKEY_PERFORMANCE_DATA);
if (hKeyValue) RegCloseKey (hKeyValue);

return NULL;
} // BuildNameTable

DWORD
GetSystemPerfData (
IN HKEY hKeySystem,
IN LPTSTR pValue,
IN PPERF_DATA_BLOCK *pPerfData
)

/*++
GetSystemPerfData

Allocates data buffer as required and queries performance data
specified in pValue from registry.

Arguments

hKeySystem
Handle to performance data in registry

pValue
Value string to return from registry

pPerfData
address of pointer to allocated perf data block that is
filled in by call to RegQueryValue.

Return Value

ERROR_SUCCESS if data returned
WIN 32 ERROR if error encountered
--*/
{ // GetSystemPerfData
LONG lError ; // Win 32 Error returned by fn. calls
DWORD Size; // size of data buffer passed to fn. call
DWORD Type; // type of data buffer returned by fn. call

// allocate initial buffer if one is not passed into this routine

if (*pPerfData == NULL) {
*pPerfData = malloc (INITIAL_SIZE);
if (*pPerfData == NULL) {
return ERROR_OUTOFMEMORY;
}
}

while (TRUE) {
Size = _msize (*pPerfData); // query the size of the data buffer

lError = RegQueryValueEx ( // get performance data from reg.
hKeySystem,
pValue,
RESERVED,
&Type,
(LPBYTE)*pPerfData,

&Size) ;

// check for success and valid perf data block signature

if ((!lError) &&
(Size > 0) &&
(*pPerfData)->Signature[0] == (WCHAR)'P' &&
(*pPerfData)->Signature[1] == (WCHAR)'E' &&
(*pPerfData)->Signature[2] == (WCHAR)'R' &&
(*pPerfData)->Signature[3] == (WCHAR)'F' ) {

return (ERROR_SUCCESS) ;
}

// if buffer is not big enough, reallocate and try again

if (lError == ERROR_MORE_DATA) {
*pPerfData = realloc (
*pPerfData,
_msize (*pPerfData) +
EXTEND_SIZE) ;

if (!*pPerfData) {
return (lError) ;
}
} else {
return (lError) ;
}
}
} // GetSystemPerfData

/*++

Pointer Functions

These functions are used to walk down the various structures
in the perf data block returned by the call to RegQueryValueEx

--*/
PPERF_OBJECT_TYPE
FirstObject (
PPERF_DATA_BLOCK pPerfData
)
{
return ((PPERF_OBJECT_TYPE) ((PBYTE) pPerfData + pPerfData->HeaderLength)) ;
}

PPERF_OBJECT_TYPE
NextObject (
PPERF_OBJECT_TYPE pObject
)
{ // NextObject
return ((PPERF_OBJECT_TYPE) ((PBYTE) pObject + pObject->TotalByteLength)) ;
} // NextObject

PERF_COUNTER_DEFINITION *
FirstCounter(
PERF_OBJECT_TYPE *pObjectDef
)
{
return (PERF_COUNTER_DEFINITION *)

((PCHAR) pObjectDef + pObjectDef->HeaderLength);
}

PERF_COUNTER_DEFINITION *
NextCounter(
PERF_COUNTER_DEFINITION *pCounterDef
)
{
return (PERF_COUNTER_DEFINITION *)
((PCHAR) pCounterDef + pCounterDef->ByteLength);
}

PERF_INSTANCE_DEFINITION *
FirstInstance (
PERF_OBJECT_TYPE *pObject
)
{
return (PERF_INSTANCE_DEFINITION *)
((PBYTE) pObject + pObject->DefinitionLength);

}

PERF_INSTANCE_DEFINITION *
NextInstance (
PERF_INSTANCE_DEFINITION *pInstance
)
{
// next instance is after
// this instance + this instances counter data
PERF_COUNTER_BLOCK *pCtrBlk;

pCtrBlk = (PERF_COUNTER_BLOCK *)
((PBYTE)pInstance + pInstance->ByteLength);

return (PERF_INSTANCE_DEFINITION *)
((PBYTE)pInstance + pInstance->ByteLength + pCtrBlk->ByteLength);

}

LONG
PrintExplainText(
DWORD Indent,
LPTSTR szTextString
)
/*++

PrintExplainText

Prints Long strings in a wrapped format for display on screen

Arguments

Indent
number of characters to indent text from left margin

szTextString
string to print on stdout

ReturnValue

number of lines of text printed

--*/
{
LPTSTR szThisChar;
BOOL bNewLine;

DWORD dwThisPos;
DWORD dwLinesUsed;

szThisChar = szTextString;
dwLinesUsed = 0;

// check arguments

if (!szTextString) {
return dwLinesUsed;
}

if (Indent > WRAP_POINT) {
return dwLinesUsed; // can't do this
}

bNewLine = TRUE;

// print text

while (*szThisChar) {

if (bNewLine){
for (dwThisPos = 0; dwThisPos < Indent; dwThisPos++) {
printf (" ");
}

bNewLine = FALSE;
}
if ((*szThisChar == ' ') && (dwThisPos >= WRAP_POINT)) {

printf ("\n");
bNewLine = TRUE;
// go to next printable character
while (*szThisChar <= ' ') {
szThisChar++;
}
dwLinesUsed++;
} else {
printf ("%c", *szThisChar);
szThisChar++;
}

dwThisPos++;
}

printf ("\n");
bNewLine = TRUE;
dwLinesUsed++;

return dwLinesUsed;
} // PrintExplainText

DWORD
ProcessCommandLine (

IN LPTSTR lpCmdLine,
IN LPTSTR *lppCounterNames,
IN DWORD dwLastCounter,
OUT LPTSTR *lpValueString
)
/*++

ProcesCommandLine

Reads arguments in command line and determines
what program should do.

Command line syntax is:

PERFEX [ ? | Object]

if "?" is in command line, then list of counter objects is displayed

if a text string (other than "?") then string is translated to a
counter object id and the counters for that object are displayed

Arguments

lpCmdLine

null terminated string that contains command line arguments

lppCounterNames

pointer to array of counter names (used for lookup)

dwLastCounter

number of elements in CounterNames array

lpValueString

value string to send to ReqQueryValue in order to return counter
data for specified counter

ReturnValue

TRUE if valid data is returned
FALSE if unable to process command line
(command syntax help should be displayed).

--*/
{
LPTSTR lpLocalString;
DWORD dwReturnValue;
DWORD dwElem;
TCHAR LocalBuffer[100];
INT BufferSize;
INT Index;

dwReturnValue = COMMAND_ERROR; // assume error until
// proven otherwise

lpLocalString = lpCmdLine;

// move pointer past exe name

while ((*lpLocalString > TEXT(' ')) && (*lpLocalString)) lpLocalString++;

// move pointer to next non-space char;

while ((*lpLocalString <= TEXT(' ')) && (*lpLocalString)) lpLocalString++;

if (*lpLocalString) {
// check for "HELP" command line argument

if (*lpLocalString == TEXT('-')) {
if (*++lpLocalString == TEXT('?')) {
*lpValueString = Global;
dwReturnValue = COMMAND_HELP;
}

} else { // first arg is not preceded by a '-'
// so compare to counters

// see if command line matches a counter name

// duplicate the LocalString
BufferSize = sizeof(LocalBuffer) / sizeof(TCHAR) - 1;
for (Index=0; Index < BufferSize; Index++) {
if (*lpLocalString == TEXT('\0') ||
*lpLocalString == TEXT(' ')){
LocalBuffer[Index] = TEXT('\0');
break;
} else {
LocalBuffer[Index] = *lpLocalString++;
}
}
LocalBuffer[BufferSize] = TEXT('\0');

dwReturnValue = COMMAND_NOT_FOUND;
for (dwElem = 0; dwElem <= dwLastCounter; dwElem++) {
// compare counter name to command line
if (lppCounterNames[dwElem]) {
if ((lstrcmpi(lppCounterNames[dwElem], LocalBuffer))
== 0) {
// match found
// make element Number a string and exit
_stprintf ((LPTSTR)&cValueName[0], TEXT("%d"), dwElem);

*lpValueString = (LPTSTR)&cValueName[0];
dwReturnValue = COMMAND_COUNTER;
break; // out of for loop
} // endif command line matches string
} // endif lppCounterNames[dwElem] not equal NULL
} // end for loop that searches table
} // if not "-?"
} else {
dwReturnValue = COMMAND_ERROR;
}

return dwReturnValue;
} // ProcessCommandLine

VOID
DisplayObjectNames (
PPERF_DATA_BLOCK pPerf,
LPTSTR *pNames

)
/*++

DisplayObjectNames

Scans the perf data and prints the name of each object found in the
list

Arguments

pPerf
pointer to the perf data block returned by the call to
RegQueryValueEx

pNames
pointer to the name table containing the array of counter names
and explain text pointers.

Return Value

NONE

--*/
{
DWORD dwThisObject; // count of objects
PPERF_OBJECT_TYPE pThisObject; // pointer to object structure

printf ("\nThe available performance counter objects are:");

// loop from first object in buffer returned and display the

// number of objects in the buffer

for (dwThisObject = 0, pThisObject = FirstObject (pPerf);
dwThisObject < pPerf->NumObjectTypes;
dwThisObject++, pThisObject = NextObject(pThisObject)) {

printf ("\n\"%ws\"\n",
pNames[pThisObject->ObjectNameTitleIndex]);

PrintExplainText (OBJECT_TEXT_INDENT,
pNames[pThisObject->ObjectHelpTitleIndex]);
}
} // DisplayObjectNames

VOID
DisplayCounterData (
PPERF_DATA_BLOCK pPerf,

LPTSTR *pNames
)
/*++

DisplayCounterData

Scans the perf data and prints the name of counter and
instance found in the list

Arguments

pPerf
pointer to the perf data block returned by the call to
RegQueryValueEx

pNames
pointer to the name table containing the array of counter names
and explain text pointers.

Return Value

NONE

--*/
{
DWORD dwThisObject; // count of objects
PPERF_OBJECT_TYPE pThisObject; // pointer to object structure

DWORD dwThisCounter; // count of counters

PPERF_COUNTER_DEFINITION pThisCounter;

LONG lThisInstance;
PPERF_INSTANCE_DEFINITION pThisInstance;

// for each object in buffer, display each counter and instance

for (dwThisObject = 0, pThisObject = FirstObject (pPerf);
dwThisObject < pPerf->NumObjectTypes;
dwThisObject++, pThisObject = NextObject(pThisObject)) {

printf ("\n\nThe object \"%ws\", ",
pNames[pThisObject->ObjectNameTitleIndex]);

// if there are instnances, then loop through all instances

// of this object

if (pThisObject->NumInstances != PERF_NO_INSTANCES) {
printf (" which has the following instances:");

for (lThisInstance = 0,
pThisInstance = FirstInstance(pThisObject);
lThisInstance < pThisObject->NumInstances;
lThisInstance++,
pThisInstance = NextInstance(pThisInstance)) {

printf ("\n\t\"%ws\"",
(LPWSTR)((PBYTE)pThisInstance
+ pThisInstance->NameOffset));

}
printf ("\n");
} else {
printf (" which has only one occurance,");
}

// now display all counters described in this object

printf ("\nand has the following counters:");
for (dwThisCounter = 0,
pThisCounter = FirstCounter (pThisObject);
dwThisCounter < pThisObject->NumCounters;
dwThisCounter++, pThisCounter = NextCounter(pThisCounter)) {

printf ("\n \"%ws\"",
pNames[pThisCounter->CounterNameTitleIndex]);

}
}
} // DisplayCounterData

int
_CRTAPI1 main(
int argc,
char *argv[]
)
/*++

main

entry point to perfex

This program illustrates how to:

get performance counter names and explain text from the registry
obtain counter object value strings for use
in querying perf. data
walk the performance data structures returned by the registry

This program does not show how to:
format or process the counter data returned
the counter data returned by the registry depends on the
counter type. This data must be processed and formatted by
the application program.

Arguments

argc
# of command line arguments present

argv
array of pointers to command line strings

(note that these are obtained from the GetCommandLine function in
order to work with both Unicode and ANSI strings.)

ReturnValue

0 (ERROR_SUCCESS) if command was processed
Non-Zero if command error was detected.

--*/
{

LPTSTR *lpCounterText; // array of counter name strings
LPTSTR lpCommandLine; // pointer to command line string
LPTSTR lpValueString; // value string to pass to registry for data

DWORD dwLastElement; // number of elements in lpCounterText

PPERF_DATA_BLOCK pDataBlock; // pointer to perfdata block

DWORD dwStatus; // return status of fn. calls
DWORD dwAction; // display mode indicated by command line

// query counter names from registry and load into table

lpCounterText = BuildNameTable (
HKEY_LOCAL_MACHINE,
DefaultLangId,
&dwLastElement);

if (!lpCounterText) {
printf ("ERROR: Unable to read counter names from registry.");

return (TRUE);
}

lpCommandLine = GetCommandLine(); // get command line

// read command line to determine what to do

dwAction = ProcessCommandLine (lpCommandLine,
lpCounterText,
dwLastElement,
&lpValueString);

if (dwAction == COMMAND_ERROR) { // if unable to read command line
DisplayCommandHelp(); // display command line usage
free (lpCounterText); // free buffer and return error
return (TRUE);
} else if (dwAction == COMMAND_NOT_FOUND) {
DisplayNotFound(); // display not found
// & command line usage
free (lpCounterText); // free buffer and return error
return (TRUE);
} else {
// of command requires processing, then get perf data from reg.

pDataBlock = NULL;
dwStatus = GetSystemPerfData (
HKEY_PERFORMANCE_DATA,
lpValueString,
&pDataBlock);

if (dwStatus != ERROR_SUCCESS) {
printf ("\nERROR: Unable to obtain data for that counter");
free (lpCounterText);
return (dwStatus);
} else {

// if data was returned, then display in format requested by
// command line

if (dwAction == COMMAND_HELP) {
// display object names returned from registry

DisplayObjectNames (pDataBlock, lpCounterText);
} else {
// display counter data for specified object
DisplayCounterData (pDataBlock, lpCounterText);
}
}

free (lpCounterText);
return (ERROR_SUCCESS); // success
}
}

+$#A Displaying Counter Title Indices
This section includes example code that displays counter title indices and their names.
#define UNICODE 1
#define _UNICODE 1

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>
#include <string.h>
#include <memory.h>
#include <process.h>
#include <tchar.h>

#define SZ_DUMMY 10

int GetRegDataBound(HKEY hkey)
{
TCHAR chDummy[SZ_DUMMY];
DWORD cSubKeys;
DWORD cbMaxSubkey;
DWORD cbMaxClass;
DWORD cValues;
DWORD cbMaxValueName;

DWORD cbMaxValueData;
DWORD cbSecurityDescriptor;
FILETIME ftLastWriteTime;
DWORD lErr;
DWORD cbClassSize = SZ_DUMMY;

lErr = RegQueryInfoKey(hkey, chDummy, &cbClassSize,
NULL, &cSubKeys, &cbMaxSubkey, &cbMaxClass,
&cValues, &cbMaxValueName, &cbMaxValueData,
&cbSecurityDescriptor, &ftLastWriteTime);

if (lErr == ERROR_SUCCESS)

return cbMaxValueData;
else
return -1;
}

int
_CRTAPI1 main(
)
{
HKEY hKeyData;
LONG lErr;
LPTSTR lptstr;
int iSize;
DWORD dwType;
DWORD dwSize;
int iIndex;

lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Perflib\\009"),
0, KEY_ALL_ACCESS, &hKeyData);

assert(lErr == ERROR_SUCCESS);

iSize = GetRegDataBound(hKeyData);

if (iSize == -1)
exit(-1);

iSize++;
lptstr = malloc(iSize * sizeof(TCHAR));

dwSize = iSize;

lErr = RegQueryValueEx(hKeyData, TEXT("Counters"), 0, &dwType,
(LPBYTE) lptstr, &dwSize);

assert(lErr == ERROR_SUCCESS);

while (*lptstr) {
_stscanf (lptstr, TEXT(" %d"), &iIndex);
// iIndex = atoi(lptstr);
lptstr += lstrlen(lptstr) + 1;
printf("#define CNTR_%.3d\t \"%ws\"\n", iIndex, lptstr);

lptstr += lstrlen(lptstr) + 1;
}

lErr = RegCloseKey(hKeyData);
assert(lErr == ERROR_SUCCESS);
return 0;
}

+$#A Performance Monitoring Functions
Following are the functions used for performance monitoring.
RegConnectRegistry_win32_RegConnectRegistryRegQueryValueEx_win32_RegQueryValueExQueryPerformanceCounter_win32_QueryPerformanceCounterQueryPerformanceFrequency_win32_QueryPerformanceFrequency
Obsolete or Deleted Functions
ProfClear_win32_ProfClearProfFinish_win32_ProfFinishProfFlush_win32_ProfFlushProfInsChk_win32_ProfInsChkProfSampRate_win32_ProfSampRateProfSetup_win32_ProfSetupProfStart_win32_ProfStartProfStop_win32_ProfStop

+$#AK Pipes
A pipe is a communication conduit with two ends: a process with a handle to one end can communicate with a process having a handle to the other end. This overview describes how to create, manage, and use pipes.
+$#A About Pipes
Pipes can be one-way ¾ where one end is read-only and the other end is write-only, or they can be two-way ¾ where both ends of the pipe can read or write. The Microsoft® Win32® application programming interface (API) provides both anonymous (unnamed) pipes and named pipes.
+$#A Anonymous Pipes
An anonymous pipe is an unnamed, one-way pipe that transfers data between a parent process and a child process or between two child processes of the same parent process. Although it is possible to use anonymous pipes for communication between unrelated processes, it is easier to use named pipes for that purpose (for more information, see Named Pipes_win32_Named_Pipes). Anonymous pipes are always local; they cannot be used for communication over a network.
The CreatePipe_win32_CreatePipe function creates an anonymous pipe and returns two handles, one to the read end and one to the write end of the pipe. The read handle has only read access to the pipe, and the write handle has only write access to the pipe. To communicate through the pipe, a handle to one of the ends must be passed to another process. Usually, this is done through inheritance; that is, a child process inherits a handle from its parent process. The inheritability of these handles is controlled by the creating process in the following ways:
· The CreatePipe function specifies a SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure. If the bInheritHandle member of this structure is TRUE, the handles can be inherited.
· The DuplicateHandle_win32_DuplicateHandle function can change the inheritability of a pipe handle. This function enables a process to create a noninheritable duplicate of an inheritable pipe handle or to create an inheritable duplicate of a noninheritable pipe handle.
· The CreateProcess_win32_CreateProcess function enables a parent process to specify whether the new child process will inherit all or none of the parent's inheritable handles.

When a child process inherits a handle, the system enables the process to access the pipe referred to by that handle. However, the parent process must communicate the handle value to the child process. Typically, this is done by using the SetStdHandle_win32_SetStdHandle and GetStdHandle_win32_GetStdHandle functions, which set and retrieve the standard handles (standard input, standard output, or standard error) of a process. The parent process can specify a pipe handle in a call to SetStdHandle before the child process is created. The current standard handles of the parent process are inherited by the child process. So, when the child process starts up, it can use the GetStdHandle function to retrieve the handle value. The parent process can change its own standard handles without affecting the standard handles of its existing child processes.
To read from the pipe, a process uses the read handle in a call to the ReadFile_win32_ReadFile function. Unless an error occurs, ReadFile does not return until the specified number of bytes has been read or until all handles to the write end of the pipe have been closed.
To write to the pipe, a process uses the write handle in a call to the WriteFile_win32_WriteFile function. WriteFile does not return until the specified number of bytes has been written or an error occurs. If the pipe's buffer is full and bytes remain to be written, WriteFile does not return until some other process or thread reads from the pipe, making more buffer space available. CreatePipe_win32_CreatePipe enables the creating process to specify a buffer size for the pipe, or it can use the default buffer size.
Asynchronous (overlapped) read and write operations are not supported for anonymous pipes. This means that the ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions cannot be used with anonymous pipes. The lpOverLapped parameter of the ReadFile and WriteFile functions is ignored when used with an anonymous pipe.
An anonymous pipe exists until all handles to both read and write ends of the pipe are closed by the CloseHandle_win32_CloseHandle function.
+$#A Named Pipes
A named pipe is a one-way or two-way pipe for communicating between a server process and one or more client processes. A server process specifies a name when it calls the CreateNamedPipe_win32_CreateNamedPipe function to create one or more instances of a named pipe. All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles and provides a separate conduit for client-server communication. When a client process specifies a pipe name in the CreateFile_win32_CreateFile or CallNamedPipe_win32_CallNamedPipe function, it connects to an instance of the pipe. The use of instances enables multiple client processes to use the same named pipe simultaneously.
Subject to security checks, any client process can access a named pipe, making it easy to communicate between related or unrelated processes. Named pipes can be used locally to communicate between processes on the same machine or across a network to connect processes on different machines.
In the Win32 API, the difference between client and server processes is greatly diminished, because any process can act as both a server and a client, making peer-to-peer communication possible. As used here, server refers to a process that uses CreateNamedPipe to create a named pipe, and client refers to a process that uses CreateFile or CallNamedPipe to connect to an instance of a named pipe.
+$#A Pipe Names
Each named pipe must have a unique name that distinguishes it from other named pipes. Use the following form when specifying the name of a pipe in the CreateNamedPipe_win32_CreateNamedPipe, CreateFile_win32_CreateFile, WaitNamedPipe_win32_WaitNamedPipe, or CallNamedPipe_win32_CallNamedPipe function.
\\servername\pipe\pipename
The server process cannot create a pipe on a computer, so CreateNamedPipe must use a period to indicate the local computer as shown in this example.
\\.\pipe\pipename
Client processes using CreateFile_win32_CreateFile, WaitNamedPipe_win32_WaitNamedPipe, or CallNamedPipe_win32_CallNamedPipe can use a period to specify a local pipe, but they must specify the name of a server to open a pipe on a remote computer.
The pipename part of the name can include any character, including numbers and special characters. The entire pipe name string can be up to 256 characters long. Pipe names are not case-sensitive.
+$#A Named Pipe Modes
A number of modes associated with a named pipe or with a handle to a named pipe affect the use and behavior of the pipe. Some of these modes can be set differently for each handle to a pipe, while others must be the same for all handles to a pipe.
The server process specifies the following modes when it calls the CreateNamedPipe_win32_CreateNamedPipe function. The type and access modes must be the same for all instances of a pipe.
Mode Meaning
Type PIPE_TYPE_BYTE or PIPE_TYPE_MESSAGE. The type mode determines whether data is written to a named pipe as a stream of messages or as a stream of bytes. If no type is specified in CreateNamedPipe_win32_CreateNamedPipe, the default is PIPE_TYPE_BYTE. A handle to a byte pipe must be in byte-read mode. A handle to a message pipe can be in either byte-read or message-read mode. For more information about pipe types, see Named Pipe Type and Read Mode_win32_Named_Pipe_Type_and_Read_Mode.
Access PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND, or PIPE_ACCESS_DUPLEX. This mode is equivalent to specifying the read or write access of the server's handles to the pipe, where inbound is equivalent to GENERIC_READ access, outbound is equivalent to GENERIC_WRITE access, and duplex is equivalent to GENERIC_READ | GENERIC_WRITE access. Client processes using the CreateFile_win32_CreateFile function to connect to a named pipe must specify an access compatible with the access specified by the server. For example, a client must specify GENERIC_READ access for an outbound pipe.

The remaining pipe modes can differ for each pipe handle. This means that the client and server ends of the same pipe instance can use different modes. CreateNamedPipe_win32_CreateNamedPipe sets all of these modes for the pipe handles of a server. CreateFile supports setting the overlapped and write-through modes for the pipe handles of a client. Both client and server processes use the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function to set the read and wait modes of a pipe handle.
Mode Meaning
Read PIPE_READMODE_BYTE or PIPE_READMODE_MESSAGE. In byte-read mode, data is read from a named pipe as a stream of bytes. In message-read mode, data is read from a named pipe as a stream of messages. Only byte-read mode is allowed for byte-type pipes. Either byte-read or message-read mode is allowed for message-type pipes. The default is byte-type mode, if no read mode is specified in CreateNamedPipe_win32_CreateNamedPipe. For more information about read modes, see Named Pipe Type and Read Mode_win32_Named_Pipe_Type_and_Read_Mode.
Wait PIPE_WAIT or PIPE_NOWAIT. In blocking-wait mode, the ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, and ConnectNamedPipe_win32_ConnectNamedPipe functions wait indefinitely in some situations for a process on the other end of the pipe to perform an action. In nonblocking-wait mode, the functions return if they cannot finish their operations immediately. For more information about wait modes, see Blocking and Nonblocking Pipe Operations_win32_Blocking_and_Nonblocking_Pipe_Operations.
Overlapped FILE_FLAG_OVERLAPPED. In overlapped mode, functions performing read, write, and connect operations that take a significant time to be completed can return immediately. This enables the thread to perform other operations while a time-consuming operation executes in the background. For more information about overlapped mode, see Synchronous and Overlapped Input and Output_win32_Synchronous_and_Overlapped_Input_and_Output.
Write-through FILE_FLAG_WRITE_THROUGH. In write-through mode, the functions that write to a named pipe do not return until the data written is transmitted across the network and into the pipe's buffer on the remote computer. This mode affects only write operations on byte-type pipes and, then, only when the client and server processes are on different computers. If write-through mode is not enabl ed, a writ e func tion returns successfully, even if the data is not transmitted. For more information about write-through modes, see Write-through to Remote Clients_win32_Write.2d.throu gh_to_Remote_Clients.

The initial read mode for a pipe handle is specified when a server process calls CreateNamedPipe_win32_CreateNamedPipe. If PIPE_READMODE_BYTE is specified or if no read mode is specified, the pipe handle is created in byte-read mode. If both PIPE_READMODE_MESSAGE and PIPE_TYPE_MESSAGE are specified, the handle is created in message-read mode. For a client process, a pipe handle returned by CreateFile_win32_CreateFile is always in byte-read mode initially. Both client and server processes can use the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function to change the read mode of a pipe handle. For a message-type pipe, the read mode can be different for server and client handles to the same pipe instance.
+$#A Named Pipe Operations
The first time the server calls CreateNamedPipe_win32_CreateNamedPipe, the function specifies the maximum number of instances of the pipe that can exist simultaneously. Limited by this maximum, the server can call CreateNamedPipe repeatedly to create additional instances of the pipe. If the function succeeds, each call returns a handle to the server end of a named pipe instance.
As soon as a pipe instance is created, a client process can connect to it by calling the CreateFile_win32_CreateFile or CallNamedPipe_win32_CallNamedPipe function. If a pipe instance is available, CreateFile returns a handle to the client end of the pipe instance. If no instances of the pipe are available, a client process can use the WaitNamedPipe_win32_WaitNamedPipe function to wait for one to become available.
A client process can also use CallNamedPipe to combine into a single operation the functions that connect to a pipe instance (waiting for one to be available, if necessary), write a message, read a message, and close its pipe handle. CallNamedPipe can be used only by a client process and only with a message-type pipe.
A server process specifies a handle to a pipe instance in a call to the ConnectNamedPipe_win32_ConnectNamedPipe function to determine when a client process is connected to it. If the pipe handle is in blocking mode, the ConnectNamedPipe operation does not return until a client is connected.
Client and server processes can call one of several functions ¾ in addition to CallNamedPipe ¾ to read from and write to a named pipe. The behavior of these functions depends on the type of pipe and the modes in effect for the specified pipe handle, as follows:
· The ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions can be used with either byte-type or message-type pipes.
· The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions can be used with either byte-type or message-type pipes if the pipe handle was opened for overlapped operations.
· The PeekNamedPipe_win32_PeekNamedPipe function can be used to read without removing the contents of either a byte-type pipe or a message-type pipe. PeekNamedPipe can also return additional information about the pipe instance.
· The TransactNamedPipe_win32_TransactNamedPipe function can be used with message-type pipes if the pipe handle of the calling process is set to message-read mode. The function writes a request message and reads a reply message in a single operation, enhancing network performance.

For a pipe handle in byte-read mode, a read operation is completed successfully when all available bytes in the pipe are read or when the specified number of bytes is read.
For a pipe handle in message-read mode, a ReadFile_win32_ReadFile or ReadFileEx_win32_ReadFileEx operation is completed successfully only when the entire message is read. If the specified number of bytes to read is less than the size of the next message, the function reads as much of the message as possible before returning FALSE (with the GetLastError_win32_GetLastError function returning ERROR_MORE_DATA). However, the remainder of the message can be read by making additional calls to ReadFile, ReadFileEx, or PeekNamedPipe_win32_PeekNamedPipe. Note that PeekNamedPipe returns TRUE after reading a complete or partial message, and it reports the number of bytes remaining in a partially read message.
For a message-type pipe with multiple unread messages, a read operation using a message-read pipe handle returns after reading one message. An operation using a byte-read handle does not distinguish between the messages ¾ it reads all available bytes up to the specified number.
When a client and server finish using a pipe instance, the server calls the DisconnectNamedPipe_win32_DisconnectNamedPipe function to close the connection to the client process. This function makes the client's handle invalid (if it has not already been closed). Any unread data in the pipe is discarded. To ensure that all bytes or messages written to the pipe are read by the client, the server first calls the FlushFileBuffers_win32_FlushFileBuffers function, which does not return until the client has read all data from the pipe. After the client is disconnected, the server calls the CloseHandle_win32_CloseHandle function to close its handle to the pipe instance. Alternatively, the server can use ConnectNamedPipe_win32_ConnectNamedPipe to enable a new client to connect to this instance of the pipe.
A process can retrieve information about a named pipe by calling the GetNamedPipeInfo_win32_GetNamedPipeInfo function, which returns the type of the pipe, the size of the input and output buffers, and the maximum number of pipe instances that can be created. The GetNamedPipeHandleState_win32_GetNamedPipeHandleState function reports on the read and wait modes of a pipe handle, the current number of pipe instances, and additional relevant information for pipes that communicate over a network. The SetNamedPipeHandleState_win32_SetNamedPipeHandleState function sets the read mode and wait modes of a pipe handle. For client processes communicating with a remote server, the function also controls the maximum number of bytes to collect or the maximum time to wait before transmitting a message (assuming the client's handle was not opened with write-through mode enabled).
+$#A Named Pipe Server Process
The simplest server process can use the CreateNamedPipe_win32_CreateNamedPipe function to create a single instance of a pipe, connect to a single client, communicate with the client, disconnect the pipe, close the pipe handle, and terminate. Typically, however, a server process must communicate with multiple client processes. A server process can use a single pipe instance by connecting to and disconnecting from each client in sequence, but performance would be poor. To handle multiple clients simultaneously, the server process must create multiple pipe instances.
The Win32 API supports three basic strategies for servicing multiple pipe instances. The server process can perform the following tasks:
· Create multiple threads (and/or processes) with a separate thread for each instance of the pipe. For an example of a multithreaded server process, see Multithreaded Server_win32_Multithreaded_Server.
· Overlap operations by specifying an OVERLAPPED_win32_OVERLAPPED_str structure in the ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, and ConnectNamedPipe_win32_ConnectNamedPipe functions. For an example of a server process that uses overlapped operations, see Server Using Overlapped Input and Output_win32_Server_Using_Overlapped_Input_and_Output.
· Overlap operations by using the ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions, which specify a completion routine to be executed when the operation is complete. For an example of a server process that uses completion routines, see Server Using Completion Routines_win32_Server_Using_Completion_Routines.
The multithreaded server strategy is easy to write, because the thread for each instance handles communications for only a single client. The system allocates processor time to each thread as needed. But each thread uses system resources, which is a potential disadvantage for a server that handles a large number of clients. Other complications occur if the actions of one client necessitate communications with other clients (as for a network game program, where a move by one player must be communicated to the other players).
With a single-threaded server, it is easier to coordinate operations that affect multiple clients, and it is easier to protect shared resources (for example, a database file) from simultaneous access by multiple clients. The challenge of a single-threaded server is that it requires coordination of overlapped operations in order to allocate processor time for handling the simultaneous needs of the clients.
+$#A Named Pipe Type and Read Mode
The type of a pipe determines how data is written to a named pipe, and the read mode determines how data is read from a named pipe. Data can be transmitted through a named pipe as either a stream of bytes or as a stream of messages. When using the CreateNamedPipe_win32_CreateNamedPipe function to create an instance of a named pipe, the server process specifies the pipe's type, the type it must specify for all instances.
A byte-type pipe is created if PIPE_TYPE_BYTE is specified or, by default, if no type is specified. This means that data is written to the pipe as a stream of bytes, and the system does not differentiate between the bytes written in different write operations. A handle to a byte-type pipe can be in byte-read mode only.
A message-type pipe is created if PIPE_TYPE_MESSAGE is specified. This means that the system treats each write operation to the pipe as a message unit. A handle to a message-type pipe can be in either byte-read or message-read mode.
+$#A Blocking and Nonblocking Pipe Operations
The wait mode (blocking or nonblocking) of a pipe handle determines whether the ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, and ConnectNamedPipe_win32_ConnectNamedPipe functions return immediately in situations that would otherwise require an indefinite wait. By default, all named pipe handles returned by the CreateNamedPipe_win32_CreateNamedPipe or CreateFile_win32_CreateFile function are created with blocking-wait mode enabled. Nonblocking-wait mode can be enabled for a server's pipe handle by specifying PIPE_NOWAIT in CreateNamedPipe. Both server and client processes can change a pipe handle's wait mode by specifying either PIPE_WAIT or PIPE_NOWAIT in a call to the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function.
Note that nonblocking-wait mode is supported for compatibility with Microsoft® LAN Manager version 2.0, and it should not be used to achieve overlapped input and output (I/O) with named pipes. Overlapped I/O should be used instead because it enables time-consuming ReadFile, WriteFile, and ConnectNamedPipe operations to execute in the background after the function returns. For more information about overlapped I/O, see Synchronous and Overlapped Input and Output_win32_Synchronous_and_Overlapped_Input_and_Output.
A ReadFile operation is affected by the wait mode of a pipe handle when the pipe is empty. With a blocking-wait handle, the operation is not completed successfully until data is available from a thread writing to the other end of the pipe. Using a nonblocking-wait handle, the function returns FALSE immediately, and the GetLastError_win32_GetLastError function returns ERROR_NO_DATA.
A WriteFile operation is affected by a pipe handle's wait mode when there is insufficient space in the pipe's buffer to hold all the data. With a blocking-wait handle, the write operation cannot succeed until sufficient space is created in the buffer by a thread reading from the other end of the pipe. With a nonblocking-wait handle, the write operation returns TRUE immediately, without writing any bytes (for a message-type pipe) or after writing as many bytes as the buffer holds (for a byte-type pipe).
A ConnectNamedPipe_win32_ConnectNamedPipe operation is affected by the wait mode of a pipe handle when there is no client connected or waiting to connect to the pipe instance. With a blocking-wait handle, the connect operation does not succeed until a client process connects to the pipe instance by calling either the CreateFile_win32_CreateFile or CallNamedPipe_win32_CallNamedPipe function. With a nonblocking-wait handle, the connect operation returns FALSE immediately, and the GetLastError function returns ERROR_PIPE_LISTENING.
The wait mode of a pipe handle does not affect read or write operations when using the TransactNamedPipe_win32_TransactNamedPipe or CallNamedPipe function.
+$#A Synchronous and Overlapped Input and Output
The Win32 API supports both synchronous and asynchronous (overlapped) operations on named pipes. The ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, TransactNamedPipe_win32_TransactNamedPipe, and ConnectNamedPipe_win32_ConnectNamedPipe functions can run either synchronously or asynchronously. The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions can run only asynchronously. When functions run synchronously, they do not return until the operation is finished. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. When functions run asynchronously, they return immediately even before their operations are finished. This enables a time-consuming operation to be executed in the background while the calling thread is free to perform other tasks. In the Win32 API, asynchronous execution is known as overlapped I/O.
Overlapped operations make it possible for one pipe to read and write data simultaneously and for a single thread to perform simultaneous I/O operations on multiple pipe handles. This enables a single-threaded server process to handle communications with multiple client processes efficiently. For examples that illustrate overlapped operations by a server process, see Server Using Overlapped Input and Output_win32_Server_Using_Overlapped_Input_and_Output and Server Using Completion Routines_win32_Server_Using_Completion_Routines.
For a server process to use synchronous operations to communicate with more than one client, it must create a separate thread for each client process so that one or more threads can run while other threads are waiting. For an example of a multithreaded server process that uses synchronous operations, see Multithreaded Server_win32_Multithreaded_Server.
Overlapped operations on a named pipe are enabled by specifying the FILE_FLAG_OVERLAPPED flag when the handle is created (by the CreateNamedPipe_win32_CreateNamedPipe function for a server process or the CreateFile_win32_CreateFile function for a client process). If this flag is not specified, overlapped mode is disabled. The overlapped mode of a pipe handle cannot be changed after the pipe handle has been created. The overlapped mode can be different for server and client handles to the same pipe instance.
The ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, TransactNamedPipe_win32_TransactNamedPipe, and ConnectNamedPipe_win32_ConnectNamedPipe functions can be performed asynchronously only if overlapped mode is enabled for the specified pipe handle and a valid pointer to an OVERLAPPED_win32_OVERLAPPED_str structure is specified. If the OVERLAPPED pointer is NULL, the system attempts to make the operation look synchronous. Depending on the number of pending operations, this attempt may or may not be successful. Therefore, it is strongly recommended that if you create a handle with FILE_FLAG_OVERLAPPED and want asynchronous behavior, you should always specify a valid OVERLAPPED structure or use the functions intended for asynchronous operations such as ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx.
When ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe are performed asynchronously, one of the following outcomes occurs:
· If the operation is complete when the function returns, the return value indicates the success or failure of the operation. If an error occurs, the return value is FALSE and the GetLastError_win32_GetLastError function returns something other than ERROR_IO_PENDING.
· If the operation has not finished when the function returns, the return value is FALSE and GetLastError returns ERROR_IO_PENDING. In this case, the calling thread must wait until the operation has finished. The calling thread must then call the GetOverlappedResult_win32_GetOverlappedResult function to determine the results.

The hEvent member of the specified OVERLAPPED_win32_OVERLAPPED_str structure must contain a handle to a manual-reset ¾ not an auto-reset ¾ event object. This is a synchronization object created by the CreateEvent_win32_CreateEvent function. The thread that initiates the overlapped operation uses the event object to determine when the operation is finished. For more information about event objects, see Synchronization_win32_Synchronization.
An event object is either in a signaled or nonsignaled state. If an overlapped function returns before its operation has finished, the system sets the state of the event object to nonsignaled. When the operation is finished, the system sets the state to signaled. The thread can detect the object's state by specifying a handle to the event object in a call to one of the wait functions: WaitForSingleObject_win32_WaitForSingleObject, WaitForMultipleObjects_win32_WaitForMultipleObjects, WaitForSingleObjectEx_win32_WaitForSingleObjectEx, or WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx. These functions do not return until the state of a specified object is signaled or until a time-out interval has elapsed. For the wait functions that wait for multiple objects, the function's return value indicates which of the objects is signaled. Doing this enables a server process to use a loop that performs the following steps:
1. Specifies multiple event objects in a call to WaitForMultipleObjects or WaitForMultipleObjectsEx, and then waits for one of the objects to be in a signaled state.
2. Uses the wait function's return value to determine which overlapped operation is finished.
3. Performs the tasks necessary to clean up the completed operation and initiate the next operation for that pipe handle. This may involve starting another overlapped operation for the same pipe handle.

If the hEvent member of the OVERLAPPED_win32_OVERLAPPED_str structure is a NULL pointer instead of a handle to an event object, the calling thread can use the pipe handle to determine when an overlapped operation has finished. In this case, the system sets the state of the pipe handle to signaled or nonsignaled, as described previously for the state of an event object. It is strongly recommended that you always use an event object when performing overlapped operations rather than relying on the pipe handle. In particular, you should not use the pipe handle for this purpose when performing simultaneous operations on the same handle because there is no way of knowing which operation's completion caused the pipe handle to be signaled. The only safe technique for performing simultaneous operations on the same pipe handle is to use a separate OVERLAPPED structure with its own event object for each operation.
The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions, which use an event object to signal completion, the extended functions specify a completion routine. A completion routine is a function that is queued for execution when the read or write operation is finished. The completion routine is not executed until the thread that called ReadFileEx and WriteFileEx enters an alertable wait by calling one of the extended wait functions: WaitForSingleObjectEx_win32_WaitForSingleObjectEx, WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx, or SleepEx_win32_SleepEx. These functions are like the normal wait functions in that they return when a specified object is in the signaled state or when a time-out interval has elapsed. However, these functions can also perform an alertable wait that occurs when their fAlertable parameter is set to TRUE. In an alertable wait, the functions also return when a ReadFileEx or WriteFileEx completion routine is queued for execution. A server process can use the extended functions to perform a sequence of read and write operations for each client that connects to it. Each read or write operation in the sequence specifies a completion routine, and each completion routine initiates the next step in the sequence.
+$#A Write-through to Remote Clients
The write-through mode of a named pipe handle affects the behavior of byte-type pipes when data is transmitted across a network. To enable write-through mode for a named pipe handle, a process specifies the FILE_FLAG_WRITE_THROUGH flag in the CreateNamedPipe_win32_CreateNamedPipe function for a server process or in the CreateFile_win32_CreateFile function for a client process. If this flag is not specified, write-through mode is disabled. The write-through mode of a pipe handle cannot be changed after the pipe handle has been created. The write-through mode can be different for server and client handles to the same pipe instance.
If write-through mode is not enabled, the system enhances the efficiency of network operations by buffering data until a minimum number of bytes have accumulated or until a maximum time period has elapsed. Buffering enables multiple write operations to be combined into a single network transmission. This means that a write operation can be successfully completed after the data is in the outbound buffer but before it is transmitted across the network.
A client process can use the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function to control the number of bytes and the time-out period before transmission for a pipe on which write-through mode is disabled.
If write-through mode is enabled, the transmission across the network is not delayed and the write operation is not completed until the data is in the pipe buffer on the remote machine. Write-through mode is useful for applications that require synchronization for every write operation.
The system always performs write operations on message-type pipes as if write-through mode were enabled.
+$#A Pipe Security
A server process uses the ImpersonateNamedPipeClient_win32_ImpersonateNamedPipeClient function to assume the security token of the client process connected to the specified pipe instance. This function can be useful in determining whether to grant the request of a client process. For example, a named pipe server can provide access to a database or file system to which the server process has privileged access. When a client process makes a request from the server, the client typically has some lesser level of security access. By assuming the security token of the client, the server can attempt to access the protected database. The system then grants or denies the server's access, based on the security level of the client. When the server is finished, it uses the RevertToSelf_win32_RevertToSelf function to restore its original security token.
+$#A Using Pipes
Pipes are useful in applications that must pass a continuous stream of data between processes.
· Anonymous pipes overview_win32_Anonymous_Pipes_Overview
· Multithreaded server_win32_Multithreaded_Server
· Server using overlapped input and output_win32_Server_Using_Overlapped_Input_and_Output
· Server using completion routines_win32_Server_Using_Completion_Routines
· Named pipe client process_win32_Named_Pipe_Client_Process
· Transactions on named pipes_win32_Transactions_on_Named_Pipes

+$#A Anonymous Pipes Overview
A typical use of an anonymous pipe is to create a channel for communication between a parent process and its child process by redirecting the standard input or standard output handles of the child process. To redirect the standard output handle of a child process, the parent process performs the following steps:
1. Calls the GetStdHandle_win32_GetStdHandle function to get the current standard output handle, which it saves for later use.
2. Calls the CreatePipe_win32_CreatePipe function to create an anonymous pipe. This function returns handles to the read and write ends of the pipe.
3. Calls the SetStdHandle_win32_SetStdHandle function to set its standard output to be the write handle of the pipe.
4. Calls the CreateProcess_win32_CreateProcess function to create the child process. The child process inherits the inheritable handles of the parent process. It also inherits the values of the standard handles of its parent process, which it can retrieve using the GetStdHandle function.
5. Calls the CloseHandle_win32_CloseHandle function to close the parent's handle to the write end of the pipe. After the child process inherits this handle, the parent process no longer needs its copy of the handle.
6. Calls the ReadFile_win32_ReadFile function to read from the pipe. This operation enables the parent process to read the data written to standard output by the child process.

The child process uses the GetStdHandle_win32_GetStdHandle function to get its standard output handle, which is actually a handle to the write end of the pipe. The child process then uses the WriteFile_win32_WriteFile function to write its output to the pipe.
Data is written to an anonymous pipe as a stream of bytes. This means that a process reading from a pipe cannot distinguish between the bytes written in separate write operations, unless both reading and writing processes use some protocol that lets the reading process know how many bytes to read. Typically, there is no protocol, so the reading process reads from the pipe until all write handles to the pipe are closed, which causes the ReadFile function to return FALSE. When a child process's standard output is redirected, the child process calls CloseHandle_win32_CloseHandle or terminates (which automatically closes the handle). Note that it is important for the parent process to close its handle to the write end of the pipe before trying to read from the pipe. Otherwise, its ReadFile operation cannot return FALSE because there is still an open handle to the write end of the pipe.
The procedure for redirecting standard input is similar to that for redirecting standard output, except that the pipe's read handle is used for the child's standard input. In this case, the parent process must ensure that the child process does not inherit the pipe's write handle. Otherwise, the ReadFile_win32_ReadFile operation of the child process cannot return FALSE because the child process has an open handle to the write end of the pipe.
The parent process typically creates the read and write handles to the pipe so that they can be inherited by a child process. It does this by using CreatePipe_win32_CreatePipe, specifying a SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure with the bInheritHandle member set to TRUE. When a child's standard input is redirected, the child process should not inherit the pipe's write handle. The parent process prevents inheritance by using the DuplicateHandle_win32_DuplicateHandle function to create a noninheritable duplicate of the handle and then using CloseHandle to close the inheritable handle.
For an example program that uses anonymous pipes to redirect the standard handles of a child process, see Processes and Threads_win32_Processes_and_Threads.
+$#A Multithreaded Server
This example of a multithreaded server process has a main thread that loops continuously, creating a pipe instance and waiting for a client process to connect. When a client process connects, the server process creates a thread to service that client and the loop starts over. It is possible for a client process to connect successfully to the pipe instance in the interval between calls to the CreateNamedPipe_win32_CreateNamedPipe and ConnectNamedPipe_win32_ConnectNamedPipe functions. If this happens, ConnectNamedPipe returns FALSE, and GetLastError_win32_GetLastError returns ERROR_PIPE_CONNECTED.
The thread created to service each pipe instance reads requests from the pipe and writes replies to the pipe until the client process closes its handle. When this happens, the thread flushes the pipe, disconnects, closes its pipe handle, and terminates.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

VOID InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int xx = 0;

DWORD main(VOID) {
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*
* The main loop creates an instance of the named pipe and
* then waits for a client to connect to it. When the client
* connects, a thread is created to handle communications
* with that client, and the loop is repeated.
*/

for (;;) {
hPipe = CreateNamedPipe(
lpszPipename, /* pipe name */
PIPE_ACCESS_DUPLEX, /* read/write access */
PIPE_TYPE_MESSAGE | /* message type pipe */
PIPE_READMODE_MESSAGE | /* message-read mode */
PIPE_WAIT, /* blocking mode */
PIPE_UNLIMITED_INSTANCES, /* max. instances */
BUFSIZE, /* output buffer size */
BUFSIZE, /* input buffer size */
PIPE_TIMEOUT, /* client time-out */
NULL); /* no security attr. */
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/*
* Wait for the client to connect; if it succeeds,
* the function returns TRUE. If the function returns FALSE,
* GetLastError returns ERROR_PIPE_CONNECTED.
*/

fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE :
(GetLastError() == ERROR_PIPE_CONNECTED);

if (fConnected) {

/* Create a thread for this client. */

hThread = CreateThread(
NULL, /* no security attr. */
0, /* default stack size */
(LPTHREAD_START_ROUTINE) InstanceThread,
(LPVOID) hPipe, /* thread parameter */
0, /* not suspended */
&dwThreadId); /* returns thread ID */
if (hThread == INVALID_HANDLE_VALUE)
MyErrExit("CreateThread");

} else

/* The client could not connect, so close the pipe. */

CloseHandle(hPipe);

}

return 1;
}

VOID InstanceThread(LPVOID lpvParam) {
CHAR chRequest[BUFSIZE];
CHAR chReply[BUFSIZE];
DWORD cbBytesRead, cbReplyBytes, cbWritten;
BOOL fSuccess;
HANDLE hPipe;

/* The thread's parameter is a handle to a pipe instance. */

hPipe = (HANDLE) lpvParam;

while (1) {

/* Read client requests from the pipe. */

fSuccess = ReadFile(
hPipe, /* handle to pipe */
chRequest, /* buffer to receive data */
BUFSIZE, /* size of buffer */
&cbBytesRead, /* number of bytes read */
NULL); /* not overlapped I/O */
if (! fSuccess || cbBytesRead == 0)
break;
GetAnswerToRequest(chRequest, chReply, &cbReplyBytes);

/* Write the reply to the pipe. */

fSuccess = WriteFile(
hPipe, /* handle to pipe */
chReply, /* buffer to write from */
cbReplyBytes, /* number of bytes to write */
&cbWritten, /* number of bytes written */
NULL); /* not overlapped I/O */
if (! fSuccess || cbReplyBytes != cbWritten) break;
}

/*
* Flush the pipe to allow the client to read the pipe's contents
* before disconnecting. Then disconnect the pipe, and close the
* handle to this pipe instance.
*/

FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}

+$#A Server Using Overlapped Input and Output
This example is a single-threaded server process that uses overlapped operations to service simultaneous connections to multiple client processes. The server process creates a fixed number of pipe instances, each of which can be connected to a separate client process. When a client process has finished using its pipe instance, the server disconnects from the client and reuses the pipe instance to connect to a new client.
Associated with each pipe instance is an OVERLAPPED_win32_OVERLAPPED_str structure containing an event object. This structure is specified as a parameter in each ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, and ConnectNamedPipe_win32_ConnectNamedPipe operation on the pipe instance. Although the example shows simultaneous operations on different pipe instances, it avoids simultaneous operations on a single pipe instance. Because the same event object is used for read, write, and connect operations for each instance, there is no way to know which operation's completion caused the event to be set to the signaled state for simultaneous operations using the same pipe instance.
The event handles for each pipe instance are also stored in an array used by the WaitForMultipleObjects_win32_WaitForMultipleObjects function. This function waits for one of the events to be signaled, and its return value is the array index of the event that satisfied the wait. The example uses this index to retrieve a structure containing information for the pipe instance. The server uses the fPendingIO member of the structure to keep track of whether the most recent I/O operation on the instance was pending, necessitating a call to the GetOverlappedResult_win32_GetOverlappedResult function. It uses the dwState member of the structure to determine the next operation that must be performed for the instance.
Overlapped ReadFile, WriteFile, and ConnectNamedPipe operations may have finished when the function returns, or they may still be pending when the function returns. If the operation is pending, the event object in the specified OVERLAPPED structure is set to the nonsignaled state before the function returns. When the pending operation has finished, the system sets the state of the event object to signaled. The state of the event object is not changed if the operation finishes before the function returns.
Because the example uses manual reset event objects, the state of the event objects is not changed to nonsignaled by the WaitForMultipleObjects function. This is important, because the example relies on the event objects remaining in the signaled state except when there is a pending operation.
If the operation is already finished when ReadFile_win32_ReadFile, WriteFile_win32_WriteFile, or ConnectNamedPipe_win32_ConnectNamedPipe returns, the function's return value indicates the result. For read and write operations, the number of bytes transferred is also returned. If the operation is still pending, the ReadFile, WriteFile, or ConnectNamedPipe function returns FALSE and the GetLastError_win32_GetLastError function returns ERROR_IO_PENDING. In this case, the results are retrieved using the GetOverlappedResult function after the operation has finished. GetOverlappedResult returns only the results of operations that were pending, and does not report the results of operations that were completed before the overlapped ReadFile, WriteFile, or ConnectNamedPipe function returned.
Before disconnecting from a client, the multithreaded server example in the previous section used FlushFileBuffers_win32_FlushFileBuffers to ensure that the client had read everything written to the pipe. This would defeat the purpose of overlapped I/O, because the flush operation would block the execution of the server thread while it waits for the client to empty the pipe. Consequently, it is necessary to wait for a signal from the client that it has finished before disconnecting. In this example, the signal is the error generated by trying to read from the pipe after the client process closes its handle.
#include <windows.h>

#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
#define INSTANCES 4

typedef struct {
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
DWORD dwState;
BOOL fPendingIO;
} PIPEINST, *LPPIPEINST;

VOID DisconnectAndReconnect(DWORD);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);

PIPEINST Pipe[INSTANCES];
HANDLE hEvents[INSTANCES];

DWORD main(VOID) {
DWORD i, dwWait, cbBytes, dwErr;
BOOL fSuccess;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*
* The initial loop creates several instances of a named pipe
* along with an event object for each instance. An
* overlapped ConnectNamedPipe operation is started for
* each instance.
*/

for (i = 0; i < INSTANCES; i++) {

/* Create an event object for this instance. */

hEvents[i] = CreateEvent(
NULL, /* no security attr. */
TRUE, /* manual-reset event */
TRUE, /* initial state = signaled */
NULL); /* unnamed event object */
if (hEvents[i] == NULL)
MyErrExit("CreateEvent");

Pipe[i].oOverlap.hEvent = hEvents[i];

Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename, /* pipe name */
PIPE_ACCESS_DUPLEX | /* read/write access */
FILE_FLAG_OVERLAPPED, /* overlapped mode */
PIPE_TYPE_MESSAGE | /* message-type pipe */
PIPE_READMODE_MESSAGE | /* message-read mode */
PIPE_WAIT, /* blocking mode */
INSTANCES, /* number of instances */
BUFSIZE, /* output buffer size */
BUFSIZE, /* input buffer size */
PIPE_TIMEOUT, /* client time-out */
NULL); /* no security attr. */
if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/* Call the subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : /* still connecting */
READING_STATE; /* ready to read */

}

while (1) {

/*
* Wait for the event object to be signaled, indicating
* completion of an overlapped read, write, or
* connect operation.
*/

dwWait = WaitForMultipleObjects(
INSTANCES, /* number of event objects */
hEvents, /* array of event objects */
FALSE, /* does not wait for all */
INFINITE); /* waits indefinitely */

/* dwWait shows which pipe completed the operation. */

i = dwWait - WAIT_OBJECT_0; /* determines which pipe */
if (i < 0 || i > (INSTANCES - 1))
MyErrExit("index out of range");

/* Get the result if the operation was pending. */

if (Pipe[i].fPendingIO) {
fSuccess = GetOverlappedResult(
Pipe[i].hPipeInst, /* handle to pipe */
&Pipe[i].oOverlap, /* OVERLAPPED structure */
&cbBytes, /* bytes transferred */
FALSE); /* do not wait */

switch (Pipe[i].dwState) {

/* Pending connect operation */

case CONNECTING_STATE:
if (! fSuccess)
MyErrExit("ConnectNamedPipe");
Pipe[i].dwState = READING_STATE;
break;

/* Pending read operation */

case READING_STATE:
if (! fSuccess || cbBytes == 0) {
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = WRITING_STATE;
break;

/* Pending write operation */

case WRITING_STATE:
if (! fSuccess ||
cbBytes != Pipe[i].cbToWrite) {
DisconnectAndReconnect(i);
continue;
}
Pipe[i].dwState = READING_STATE;
break;

default:
MyErrExit("invalid pipe state");

}

}

/* The pipe state determines which operation to do next. */

switch (Pipe[i].dwState) {

/*
* READING_STATE:
* The pipe instance is connected to the client
* and is ready to read a request from the client.
*/

case READING_STATE:
fSuccess = ReadFile(
Pipe[i].hPipeInst,
Pipe[i].chBuf,
BUFSIZE,
&cbBytes,
&Pipe[i].oOverlap);

/* The read operation completed successfully. */

if (fSuccess && cbBytes != 0) {
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = WRITING_STATE;
continue;
}

/* The read operation is still pending */

dwErr = GetLastError();
if (! fSuccess &&
(dwErr == ERROR_IO_PENDING)) {
Pipe[i].fPendingIO = TRUE;
continue;
}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);
break;

/*
* WRITING_STATE:
* The request was successfully read from the client.
* Get the reply data and write it to the client.
*/

case WRITING_STATE:

GetDataToWriteToClient(&Pipe[i]);

fSuccess = WriteFile(
Pipe[i].hPipeInst,
Pipe[i].chBuf,
Pipe[i].cbToWrite,
&cbBytes,
&Pipe[i].oOverlap);

/* The write operation completed successfully. */

if (fSuccess &&
cbBytes == Pipe[i].cbToWrite) {
Pipe[i].fPendingIO = FALSE;
Pipe[i].dwState = READING_STATE;
continue;
}

/* The write operation is still pending. */

dwErr = GetLastError();
if (! fSuccess &&
(dwErr == ERROR_IO_PENDING)) {
Pipe[i].fPendingIO = TRUE;
continue;
}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);
break;

default:
MyErrExit("invalid pipe state");
}
}

return 0;
}

/*
* DisconnectAndReconnect(DWORD)
* This function is called when an error occurs or when the client
* closes its handle to the pipe. Disconnect from this client, and then
* call ConnectNamedPipe to wait for another client to connect.
*/

VOID DisconnectAndReconnect(DWORD i) {

/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )
MyErrExit("DisconnectNamedPipe");

/* Call a subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(
Pipe[i].hPipeInst,
&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?
CONNECTING_STATE : /* still connecting */
READING_STATE; /* ready to read */

}

/*
* ConnectToNewClient(HANDLE, LPOVERLAPPED)
* This function is called to start an overlapped connect operation. It
* returns TRUE if an operation is pending or FALSE if the connection
* has been completed.
*/

BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) {
BOOL fConnected, fPendingIO = FALSE;

/* Start an overlapped connection for this pipe instance. */

fConnected = ConnectNamedPipe(hPipe, lpo);

/* Overlapped ConnectNamedPipe should return FALSE. */

if (fConnected)
MyErrExit("ConnectNamedPipe");

switch (GetLastError()) {

/* The overlapped connection in progress. */

case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;

/* Client is already connected, so signal an event. */

case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;

/* If an error occurs during the connect operation... */

default:
MyErrExit("ConnectNamedPipe");
}

return fPendingIO;
}

+$#A Server Using Completion Routines
The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions, which use an event object to signal completion, the extended functions specify a completion routine. A completion routine is a function that is queued for execution when the read or write operation is done. The completion routine is not executed until the thread that called ReadFileEx and WriteFileEx enters an alertable wait by calling one of the extended wait functions: WaitForSingleObjectEx_win32_WaitForSingleObjectEx, WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx, or SleepEx_win32_SleepEx. These functions are like the other wait functions in that they return when a specified object is in the signaled state or when a time-out interval has elapsed. However, these functions can also perform an alertable wait, which occurs when their fAlertable parameter is set to TRUE. In an alertable wait, the functions also return when a ReadFileEx or WriteFileEx completion routine is queued for execution. A server process can use the extended functions to perform a sequence of read and write operations for each client that connects to it. Each read or write operation in the sequence specifies a completion routine, and each completion routine initiates the next step in the sequence.
Like the previous example, this example is a single-threaded server process that creates a message-type pipe and uses overlapped operations. The server process differs in that it uses the extended functions ReadFileEx and WriteFileEx to perform overlapped I/O. Unlike the overlapped ReadFile and WriteFile functions, which signal an event object upon completion, the extended functions specify a completion routine, which is queued for execution when the operation is finished. The server process uses the WaitForSingleObjectEx function, which performs an alertable wait that returns when a completion routine is ready to execute. The wait function also returns when an event object is signaled, which in this example indicates that the overlapped ConnectNamedPipe_win32_ConnectNamedPipe operation has finished (a new client has connected).
Initially, the server process creates a single instance of the pipe and starts an overlapped ConnectNamedPipe operation. When a client connects, the server allocates a structure to provide storage for that pipe instance and then calls the ReadFileEx function to launch a sequence of I/O operations to handle communications with the client. Each operation specifies a completion routine that performs the next operation in the sequence. The sequence terminates when the client is disconnected and the pipe instance closed. After starting the sequence of operations for the new client, the server creates another pipe instance and waits for the next client to connect.
The parameters of the ReadFileEx and WriteFileEx functions specify a completion routine and a pointer to an OVERLAPPED_win32_OVERLAPPED_str structure. This pointer is later passed to the completion routine in its lpOverLap parameter. Because the OVERLAPPED structure points to the first member in the structure allocated for each pipe instance, the completion routine can use its lpOverLap parameter to access the structure for the pipe instance.
To avoid duplication, the listing for the ConnectToNewClient subroutine is not shown; it is identical to the one used by the overlapped server process in the previous section.
#include <windows.h>

typedef struct {
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;

BOOL CreateAndConnectInstance();
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);
VOID DisconnectAndClose(LPPIPEINST);
VOID CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);

HANDLE hPipe;

DWORD main(VOID) {
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbBytes;
BOOL fSuccess, fPendingIO;

/* Create one event object for the connect operation. */

hConnectEvent = CreateEvent(
NULL, /* no security attr. */
TRUE, /* manual reset event */
TRUE, /* initial state = signaled */
NULL); /* unnamed event object */
if (hConnectEvent == NULL)
MyErrExit("CreateEvent");

oConnect.hEvent = hConnectEvent;

/*
* Call a subroutine to create one instance, and wait for
* the client to connect.
*/

fPendingIO = CreateAndConnectInstance(&oConnect);

while (1) {

/*
* Wait for a client to connect, or for a read or write
* operation to be completed, which causes a completion
* routine to be queued for execution.
*/

dwWait = WaitForSingleObjectEx(
hConnectEvent, /* event object to wait for */
INFINITE, /* waits indefinitely */
TRUE); /* alertable wait enabled */

switch (dwWait) {

/* The wait is satisfied by a completed connect operation. */

case 0:

/* If an operation is pending, get the result of the
* connect operation.
*/

if (fPendingIO) {
fSuccess = GetOverlappedResult(
hPipe, /* pipe handle */
&oConnect, /* OVERLAPPED structure */
&cbBytes, /* bytes transferred */
FALSE); /* does not wait */
if (! fSuccess)
MyErrExit("ConnectNamedPipe");
}

/* Allocate storage for this instance. */

lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
MyErrExit("GlobalAlloc lpPipeInst");

lpPipeInst->hPipeInst = hPipe;

/*
* Start the read operation for this client.
* Note that this same routine is later used as a
* completion routine after a write operation.
*/

lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0,
(LPOVERLAPPED) lpPipeInst);

/* Create new pipe instance for the next client. */

fPendingIO = CreateAndConnectInstance(
&oConnect);
break;

/*
* The wait is satisfied by a completed read or write
* operation. This allows the system to execute the
* completion routine.
*/

case WAIT_IO_COMPLETION:

break;

/* An error occurred in the wait function. */

default:

MyErrExit("WaitForSingleObjectEx");

}

}

return 0;
}

/*
* CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)
* This routine is called as an I/O completion routine after writing to
* the pipe, or when a new client has connected to a pipe instance. It
* starts another read operation.
*/

VOID CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
LPOVERLAPPED lpOverLap) {
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;

/* lpOverlap points to storage for this instance. */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*
* The write operation has finished, so read the next request (if
* there is no error).
*/

if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
BUFSIZE,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);

/* Disconnect if an error occurred. */

if (! fRead)
DisconnectAndClose(lpPipeInst);

}

/*
* CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)
* This routine is called as an I/O completion routine after reading a
* request from the client. It gets data and writes it to the pipe.
*/

VOID CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
LPOVERLAPPED lpOverLap) {
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;

/* lpOverlap points to storage for this instance. */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*
* The read operation has finished, so write a response (if no
* error occurred).
*/

if ((dwErr == 0) && (cbBytesRead != 0)) {
GetDataToWriteToClient(lpPipeInst);

fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
lpPipeInst->cbToWrite,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}

/* Disconnect if an error occurred. */

if (! fWrite)
DisconnectAndClose(lpPipeInst);

}

/*
* DisconnectAndClose(LPPIPEINST)
* This routine is called when an error occurs or when the client closes
* its handle to the pipe.
*/

VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
MyErrExit("DisconnectNamedPipe");

/* Close the handle to the pipe instance. */

CloseHandle(lpPipeInst->hPipeInst);

/* Release the storage for the pipe instance. */

if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}

/*
* CreateAndConnectInstance(LPOVERLAPPED)
* This function creates a pipe instance and connects to the new client.
* It returns TRUE if the connect operation is pending and FALSE if
* the connection has been completed.
*/

BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) {

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

hPipe = CreateNamedPipe(
lpszPipename, /* pipe name */
PIPE_ACCESS_DUPLEX | /* read/write access */
FILE_FLAG_OVERLAPPED, /* overlapped mode */
PIPE_TYPE_MESSAGE | /* message-type pipe */
PIPE_READMODE_MESSAGE | /* message read mode */
PIPE_WAIT, /* blocking mode */
PIPE_UNLIMITED_INSTANCES, /* unlimited instances */
BUFSIZE, /* output buffer size */
BUFSIZE, /* input buffer size */
PIPE_TIMEOUT, /* client time-out */
NULL); /* no security attr. */
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");

/* Call a subroutine to connect to the new client. */

return ConnectToNewClient(hPipe, lpoOverlap);

}

+$#A Named Pipe Client Process
A client process uses the CreateFile_win32_CreateFile function to open a handle to a named pipe. If the pipe exists but all of its instances are busy, CreateFile returns FALSE and the GetLastError_win32_GetLastError function returns ERROR_PIPE_BUSY. When this happens, the client process uses the WaitNamedPipe_win32_WaitNamedPipe function to wait for an instance of the pipe to be available.
CreateFile fails if the access specified is incompatible with the access specified (duplex, outbound, or inbound) when the server created the pipe. For a duplex pipe (read/write), the client can specify read, write, or read/write access; for an outbound pipe (server write only), the client must specify read only; and for an inbound pipe (server read only), the client must specify write only.
The handle returned by CreateFile defaults to byte-read mode, blocking-wait mode, overlapped mode disabled, and write-through mode disabled. The client process can use CreateFile to enable overlapped mode by specifying FILE_FLAG_OVERLAPPED or to enable write-through mode by specifying FILE_FLAG_WRITE_THROUGH. The client can use the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function to enable nonblocking mode by specifying PIPE_WAIT or to enable message-read mode by specifying PIPE_READMODE_MESSAGE.
The following example shows a client process that opens a named pipe, sets the pipe handle to message-read mode, uses WriteFile_win32_WriteFile to send a request to the server, and uses ReadFile_win32_ReadFile to read the server's reply. This client process can be used with any of the message-type servers shown in the previous sections. With a byte-type server, however, this client process fails when it calls SetNamedPipeHandleState to change to message-read mode. Because the client is reading from the pipe in message-read mode, it is possible for the ReadFile operation to return FALSE after reading a partial message. This happens when the message is larger than the read buffer. In this situation, GetLastError returns ERROR_MORE_DATA, and the remainder of the message is read by additional calls to ReadFile.
#include <windows.h>

DWORD main(int argc, char *argv[]) {
HANDLE hPipe;
LPVOID lpvMessage;
CHAR chBuf[512];
BOOL fSuccess;
DWORD cbRead, cbWritten, dwMode;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/* Try to open a named pipe; wait for it, if necessary. */

while (1) {
hPipe = CreateFile(
lpszPipename, /* pipe name */
GENERIC_READ | /* read/write access */
GENERIC_WRITE,
0, /* no sharing */
NULL, /* no security attr. */
OPEN_EXISTING, /* opens existing pipe */
0, /* default attributes */
NULL); /* no template file */

/* Break if the pipe handle is valid. */

if (hPipe != INVALID_HANDLE_VALUE)
break;

/* Exit if an error other than ERROR_PIPE_BUSY occurs. */

if (GetLastError() != ERROR_PIPE_BUSY)
MyErrExit("Could not open pipe");

/* All pipe instances are busy, so wait for 20 seconds. */

if (! WaitNamedPipe(lpszPipename, 20000) )
MyErrExit("Could not open pipe");

}

/* The pipe connected; change to message-read mode. */

dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, /* pipe handle */
&dwMode, /* new pipe mode */
NULL, /* don't set max. bytes */
NULL); /* don't set max. time */
if (! fSuccess)
MyErrExit("SetNamedPipeHandleState");

/* Send a message to the pipe server. */

lpvMessage = (argc > 1) ? argv[1] : "default message";

fSuccess = WriteFile(
hPipe, /* pipe handle */
lpvMessage, /* message */
strlen(lpvMessage) + 1, /* message length */
&cbWritten, /* bytes written */
NULL); /* not overlapped */
if (! fSuccess)
MyErrExit("WriteFile");

do {

/* Read from the pipe. */

fSuccess = ReadFile(
hPipe, /* pipe handle */
chBuf, /* buffer to receive reply */
512, /* size of buffer */
&cbRead, /* number of bytes read */
NULL); /* not overlapped */

if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
break;

/* Reply from the pipe is written to STDOUT. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chBuf, cbRead, &cbWritten, NULL))
break;

} while (! fSuccess); /* repeat loop if ERROR_MORE_DATA */

CloseHandle(hPipe);

return 0;

}

+$#A Transactions on Named Pipes
A named pipe transaction is a client-server communication that combines a write operation and a read operation into a single network operation. A transaction can be used only on a duplex, message-type pipe. Transactions improve the performance of network communications between a client and a remote server. Processes can use the TransactNamedPipe_win32_TransactNamedPipe and CallNamedPipe_win32_CallNamedPipe functions to perform named pipe transactions.
TransactNamedPipe is typically used by a client process to write a request message to the named pipe server and read the server's response message. The client process must specify GENERIC_READ | GENERIC_WRITE access when it opens its pipe handle by calling the CreateFile_win32_CreateFile function. Then, the client process sets the pipe handle to message-read mode by calling the SetNamedPipeHandleState_win32_SetNamedPipeHandleState function. If the read buffer specified in the call to TransactNamedPipe is not large enough to hold the entire message written by the server, the function returns FALSE and GetLastError_win32_GetLastError returns ERROR_MORE_DATA. The client can read the remainder of the message by calling either the ReadFile_win32_ReadFile, ReadFileEx_win32_ReadFileEx, or PeekNamedPipe_win32_PeekNamedPipe function.
TransactNamedPipe is typically called by client processes, but can also be used by a server process.
The following example shows a client process using TransactNamedPipe. The example assumes that the client process has used CreateFile to connect to the pipe and SetNamedPipeHandleState to set the pipe handle's read mode, as shown in the client process example in the preceding section.
fSuccess = TransactNamedPipe(
hPipe, /* pipe handle */
lpszWrite, /* message to server */
strlen(lpszWrite)+1, /* message length */
chReadBuf, /* buffer to receive reply */
512, /* size of read buffer */
&cbRead, /* number of bytes read */
NULL); /* not overlapped */

/*
* Exit if an error occurs, unless the error indicates there is more
* data in the message.
*/

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))
MyErrExit("TransactNamedPipe");

while(1) {

/* Data from the pipe is written to STDOUT. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chReadBuf, cbRead, &cbWritten, NULL) )
break;

/* Break if TransactNamedPipe or ReadFile is successful. */

if (fSuccess)
break;

/* Read from the pipe if there is more data in the message.*/

fSuccess = ReadFile(
hPipe, /* pipe handle */
chReadBuf, /* buffer to receive reply */
512, /* size of buffer */
&cbRead, /* number of bytes read */
NULL); /* not overlapped */

/* Exit if an error other than ERROR_MORE_DATA occurs. */

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))
break;

}

A client process uses CallNamedPipe_win32_CallNamedPipe to combine the CreateFile_win32_CreateFile, WaitNamedPipe_win32_WaitNamedPipe (if necessary), TransactNamedPipe_win32_TransactNamedPipe, and CloseHandle_win32_CloseHandle operations into a single function. Because the pipe handle is closed before the function returns, any additional bytes in the message are lost if the message is larger than the speciffied size of the read buffer. The following example shows the use of CallNamedPipe.
/* Combines connect, wait, write, read, and close operations. */

fSuccess = CallNamedPipe(
lpszPipename, /* pipe name */
lpszWrite, /* message to server */
strlen(lpszWrite)+1, /* message length */
chReadBuf, /* buffer to receive reply */
512, /* size of read buffer */
&cbRead, /* number of bytes read */
20000); /* waits for 20 seconds */

if (fSuccess || GetLastError() == ERROR_MORE_DATA) {

/* Data from the pipe is written to STDOUT. */

WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
chReadBuf, cbRead, &cbWritten, NULL);

/*
* The pipe is closed, so no more bytes can be read from the
* message.
*/

if (! fSuccess)
printf("\n...extra data in message was lost\n");
}

+$#A Pipe Functions
Following are the functions used with pipes.
CallNamedPipe_win32_CallNamedPipeConnectNamedPipe_win32_ConnectNamedPipeCreateNamedPipe_win32_CreateNamedPipeCreatePipe_win32_CreatePipeDisconnectNamedPipe_win32_DisconnectNamedPipeGetNamedPipeHandleState_win32_GetNamedPipeHandleStateGetNamedPipeInfo_win32_GetNamedPipeInfoPeekNamedPipe_win32_PeekNamedPipeSetNamedPipeHandleState_win32_SetNamedPipeHandleStateTransactNamedPipe_win32_TransactNamedPipeWaitNamedPipe_win32_WaitNamedPipe

+$#AK Power Management
This overview describes the power management functions and messages, and explains how to use them in applications and installable drivers.
+$#A About Power Management
The Windows operating system uses power management to reduce the power consumption of a computer and the attached devices. Windows works in conjunction with the computer and its devices to monitor system operation and shut down devices or the computer itself when idle. To help applications and installable drivers take advantage of power management and to ensure that no data is lost when a device or the computer is shut down, Windows provides functions and messages that reveal the system power status and notify of power management events.
+$#A About System Power Status
System power status indicates the source of power for a computer, that is, whether it uses a system battery or AC power. For computers that use batteries, the status also indicates how much battery life remains and whether the battery is currently charging.
You can retrieve the system power status by using the GetSystemPowerStatus_win32_GetSystemPowerStatus function. This copies information about the power supply and battery status to a SYSTEM_POWER_STATUS_win32_SYSTEM_POWER_STATUS_str structure. This structure identifies whether the system has a battery, and if it does, whether the battery is being used and the percentage of charge remaining.
Applications and installable drivers typically use the system power status to make decisions about whether to continue operation. For example, an application that is beginning a lengthy operation may check the status to determine whether enough battery power exists to complete the operation.
If power is low, an application can request user intervention or request that the system to shut itself down. You can suspend the operation of the system by using the SetSystemPowerState_win32_SetSystemPowerState function. This generates a power management event that, if approved by all applications and drivers in the system, shuts the system down until the user restores power. Even if a computer uses AC power, an application that determines that the system is idle and can request that the system shut itself down to save power.
+$#A Power Management Events
A power management event is a change in either the system power status or the operational mode of a device or the computer. Because these events can affect the operation of applications and installable drivers and can lead to loss of data, Windows notifies all applications and installable drivers by broadcasting a WM_POWERBROADCAST_win32_WM_POWERBROADCAST message for each event.
A system power status event occurs when there is a change in the power supply or in the system battery status. For example, the system broadcasts a message whenever the user switches from battery to AC power or vice versa. The system also broadcasts a message when remaining battery power slips below 10%.
An operational mode event occurs when there is a change in power consumption, such as the system shutting itself down or the user turning the system back on. Some changes in operational mode can cause loss of data if applications and drivers do not prepare for them. Therefore, the system broadcasts messages about these changes before they actually occur. For example, if the system determines that it is idle, it broadcasts a message notifying applications and drivers that it is about to suspend operation and shut itself down to save power. The applications and drivers can prepare for this suspension by closing files and saving data that may otherwise be lost when power is shut off.
The system has three ways to shut itself down. It can suspend operation, place operation on standby, or carry out critical suspension. An application can also shut the system down by using the SetSystemPowerState_win32_SetSystemPowerState function to suspend operation.
When system operation is suspended, power to all devices is lost. The WM_POWERBROADCAST_win32_WM_POWERBROADCAST messages, sent immediately before suspension, allow device drivers to save the state of the device. For many devices (but not all), saving the state means the driver can restore the state when power is eventually restored, allowing the device to continue operation as if power was never lost.
When the system goes into standby, power to devices may be reduced but no state is lost and interrupts continue to be processed. For example, a hard disk drive on standby may stop spinning but state information is preserved and the drive responds to requests for access. Standby operation is transparent to applications because the corresponding WM_POWERBROADCAST messages are never sent to them. Instead only device drivers receive and process these messages.
When the system carries out a critical suspension, battery power is too low to let the computer continue to run. Unlike for suspended and standby operations, the system does not notify applications and drivers before carrying out a critical suspension. This means data may be lost.
When system operation is restored after having been suspended or placed on standby, the system notifies all applications and drivers. It also identifies how the system was previously shut down so that the application or driver can take appropriate steps to restore its data and continue its own operation.
+$#A Power Messages
The system sends power messages to all applications and installable drivers whenever a power management event occurs or whenever an application calls the SetSystemPowerState_win32_SetSystemPowerState function to suspend operation. The system sends these messages through the WM_POWERBROADCAST_win32_WM_POWERBROADCAST message, setting the wParam parameter to the message type. For example, the message type, PBT_APMPOWERSTATUSCHANGE_win32_PBT_APMPOWERSTATUSCHANGE, indicates a system power status change message.
The system sends a PBT_APMQUERYSUSPEND_win32_PBT_APMQUERYSUSPEND or PBT_APMQUERYSTANDBY_win32_PBT_APMQUERYSTANDBY message to request permission to suspend system operation or place it on stanmdby. The system expects each application and driver to determine whether the requested event should occur and to return TRUE or FALSE indicating this decision. Any application or driver can deny the request and prevent it from occurring.
The system sends a PBT_APMSUSPEND_win32_PBT_APMSUSPEND or PBT_APMSTANDBY_win32_PBT_APMSTANDBY message immediately before suspending operation or placing it on standby. This gives applications and drivers one last chance to prepare for the event before it occurs. In many cases, the system sends these messages without requesting permission to do so. This happens, for example, if an application forces suspension with the SetSystemPowerState function.
The standby messages (PBT_APMQUERYSTANDBY, PBT_APMQUERYSTANDBYFAILED, PBT_APMRESUMESTANDBY, and PBT_APMSTANDBY) are not set to applications. these are intended to be sent to low-level drivers and system components.
The system sends the PBT_APMQUERYSUSPENDFAILED_win32_PBT_APMQUERYSUSPENDFAILED or PBT_APMQUERYSTANDBYFAILED_win32_PBT_APMQUERYSTANDBYFAILED message whenever a requested event is denied. These messages are intended to notify applications and drivers to continue operation as usual.
The system sends the PBT_APMRESUMESUSPEND_win32_PBT_APMRESUMESUSPEND, PBT_APMRESUMESTANDBY_win32_PBT_APMRESUMESTANDBY, or PBT_APMRESUMECRITICAL_win32_PBT_APMRESUMECRITICAL message when system operation has been restored.

Note The WM_POWER_win32_WM_POWER message, previously available to applications and drivers for use with power management, is maintained for backward compatibility. All current applications and installable drivers should use and process the WM_POWERBROADCAST_win32_WM_POWERBROADCAST message instead.

+$#A Power Management Reference
This section describes the power management functions, messages, and structures.
+$#A Power Management Functions
This section describes the power management functions.
GetSystemPowerStatus_win32_GetSystemPowerStatusSetSystemPowerState_win32_SetSystemPowerState
+$#A Power Management Messages
This section descibes the power management messages.
PBT_APMPOWERSTATUSCHANGE_win32_PBT_APMPOWERSTATUSCHANGEPBT_APMQUERYSTANDBY_win32_PBT_APMQUERYSTANDBYPBT_APMQUERYSTANDBYFAILED_win32_PBT_APMQUERYSTANDBYFAILEDPBT_APMQUERYSUSPEND_win32_PBT_APMQUERYSUSPENDPBT_APMQUERYSUSPENDFAILED_win32_PBT_APMQUERYSUSPENDFAILEDPBT_APMRESUMECRITICAL_win32_PBT_APMRESUMECRITICALPBT_APMRESUMESTANDBY_win32_PBT_APMRESUMESTANDBYPBT_APMRESUMESUSPEND_win32_PBT_APMRESUMESUSPENDPBT_APMSTANDBY_win32_PBT_APMSTANDBYPBT_APMSUSPEND_win32_PBT_APMSUSPENDWM_POWERBROADCAST_win32_WM_POWERBROADCAST
+$#A Power Management Structure
This section describes the power management structure.
SYSTEM_POWER_STATUS_win32_SYSTEM_POWER_STATUS_str

+$#AK Processes and Threads
A process is an executing application that consists of a private virtual address space, code, data, and other operating system resources, such as files, pipes, and synchronization objects that are visible to the process. A process also contains one or more threads that run in the context of the process. A thread is the basic entity to which the operating system allocates CPU time. A thread can execute any part of the application's code, including a part currently being executed by another thread. All threads of a process share the virtual address space, global variables, and operating system resources of the process.
This overview describes processes and threads, and explains how to create and use them.
+$#A About Processes and Threads
An application written for Microsoft® Windows® can consist of more than one process, and a process can consist of more than one thread. The Microsoft® Win32® application program interface (API) supports multitasking, which creates the effect of simultaneous execution of multiple processes and threads.
+$#A Multitasking
A process is a program that is loaded into memory and prepared for execution. Each process has a private virtual address space. A process consists of the code, data, and other system resources ¾ such as files, pipes, and synchronization objects ¾ that are accessible to the threads of the process. Each process is started with a single thread, but additional independently executing threads can be created.
A thread can execute any part of the program's code, including a part executed by another thread. Threads are the basic entity to which the operating system allocates CPU time. Each thread maintains a set of structures for saving its context while waiting to be scheduled for processing time. The context includes the thread's set of machine registers, the kernel stack, a thread environment block, and a user stack in the address space of the thread's process. All threads of a process share the virtual address space and can access the global variables and system resources of the process.
A multitasking operating system divides the available CPU time among the threads that need it. In Windows, the Win32 API is designed for preemptive multitasking; this means that the system allocates small slices of CPU time among the competing threads. The currently executing thread is suspended when its time slice elapses, allowing another thread to run. When the system switches from one thread to another, it saves the context of the suspended thread and restores the saved context of the next thread in the queue.
Because each time slice is small (approximately 20 milliseconds), it appears that multiple threads are executing at the same time. This is actually the case on multiprocessor systems, where the executable threads are distributed among the available processors. On a single processor system, however, using multiple threads does not result in more instructions being executed. In fact, the system can slow down if it is forced to keep track of too many threads.
To the user, the advantage of multitasking is the ability to have several applications open and working at the same time. For example, a user can edit a file with one application while another application is printing or recalculating a spreadsheet.
To the application developer, the advantage of multitasking is the ability to create applications that use more than one process and to create processes that use more than one thread of execution. For example, you can have one thread that handles interactions with the user (keyboard and mouse input), while other threads with lower priority perform the other work of the process. The higher priority of the input thread makes the program responsive to the user, while the other threads ensure that the processor can be used efficiently during the slices of time between keystrokes.
+$#A When to Use Multitasking
Any thread of any process can use the CreateThread_win32_CreateThread and CreateProcess_win32_CreateProcess functions to create additional threads or processes. An application might use multiple processes for functions that require a private address space and private resources, to protect them from the activities of other threads. Functions that are to be distributed as separate executable modules but that can be integrated to form a single application, can benefit from the multiprocess approach.
It is useful for a process to have multiple threads in situations where the application has several tasks to run concurrently. For example, an application that opens more than one window can have a separate thread perform the work of the application in each window. Multiple threads can be a convenient way to structure a program that performs several similar or identical tasks. For example, a named pipe server can create a thread to handle communications with each client process that attaches to the pipe. Other examples of using multiple threads are processes that do the following:
· Handle input for multiple windows.
· Manage input from several communications devices.
· Need to distinguish among tasks of varying priority. For example, a high-priority thread handles time-critical tasks, and a low-priority thread performs other tasks.
It is typically more efficient for an application to implement multitasking by distributing tasks among the threads of a single process, rather than by creating multiple processes, for the following reasons:
· The system can create and execute threads more quickly than it can create processes, primarily because the code for threads has already been mapped into the address space of the process, while the code for a new process must be loaded.
· All threads of a process share the same address space and can access the process's global variables, which can simplify communications between threads.
· All threads of a process can use open handles to resources such as files and pipes.
For both multiple threads and multiple processes, there are costs and dangers to be considered. The system consumes memory for the structures required by both processes and threads. Keeping track of a large number of threads consumes CPU time. With multiple threads accessing the same resources, you must synchronize the threads to avoid conflicts. This is true for system resources (such as communications ports or disk drives), handles to resources shared by multiple processes (such as file or pipe handles), or the resources of a single process (such as global variables accessed by multiple threads). Failure to synchronize multiple threads properly (in the same or in different processes) can lead to program problems such as deadlock and race conditions. The Win32 API provides a set of synchronization objects and functions you can use to coordinate multiple threads. For more information about synchronization, see Synchronizing Execution of Multiple Threads_win32_Synchronizing_Execution_of_Multiple_Threads and Synchronization_win32_Synchronization.
The Win32 API also provides alternative ways to execute multiple tasks that, in some cases, make multithreaded processes unnecessary. The most significant of these are overlapped I/O and the ability to wait for multiple events.
Overlapped I/O enables a single thread to initiate multiple time consuming I/O requests that can run concurrently. This asynchronous I/O can be performed on files, pipes, serial communications devices, or tape devices. For more information about files, see Files_win32_Files. For more information about pipes, see Pipes_win32_Pipes. For more information about serial communication devices, see Communications_win32_Communications. For more information about tape devices, see Tape Backup_win32_Tape_Backup.
Waiting for multiple events enables a single thread to block its execution while waiting for any one of several events to occur. This is much more efficient than using multiple threads, each waiting for a single event, or using a single thread that consumes CPU time by continually checking for an event of interest. For more information about waiting for multiple events, see Synchronization_win32_Synchronization.
+$#A Scheduling
The system scheduler controls multitasking by determining which of the competing threads receives the next slice of the processor's time. Each time slice consists of a fixed number of clock ticks. The Windows scheduler is preemptive, gaining control whenever a clock interrupt occurs and decrementing the count of clock ticks remaining in the time slice of the currently executing thread. If the count goes to zero, the scheduler determines which thread runs next. The scheduler maintains a queue of executable threads at each priority level. When the highest priority is shared by more than one executable thread, the next time slice goes to the thread at the head of the priority queue. The thread that just finished executing is placed at the end of the queue of threads with its priority. Threads that are suspended or blocked (for example, waiting for a semaphore object or waiting for input) are not executable, so until they become unblocked, the scheduler does not allocate any processor time to them, regardless of their priority. When the scheduler awards the next time slice to a different thread, the context of the currently executing thread is saved, and the machine state is set according to the context of the next executable thread.
+$#A Scheduling Priorities
The scheduling priority of each thread is determined by the following criteria:
· The priority class of its process (high, normal, or idle)
· The priority level of the thread within the priority class of its process (lowest, below normal, normal, above normal, highest)
· The dynamic priority boost, if any, the system applies to the thread's base priority

By default, the priority class of a process is set to normal. The CreateProcess_win32_CreateProcess function allows a parent process to specify the priority class of its child processes. Use SetPriorityClass_win32_SetPriorityClass to change the priority class of a process and GetPriorityClass_win32_GetPriorityClass to determine the current priority class of a process.
There are five priority levels within each priority class. The levels range from lowest (-2) to highest (2). All threads are started with normal (0) priority. The SetThreadPriority_win32_SetThreadPriority function enables you to change the priority level of a thread to any of the five levels. Use the GetThreadPriority_win32_GetThreadPriority function to determine the current priority level of a thread.
The base priority of each thread is the thread's priority level within the priority class of its process. The scheduler uses this base priority to determine a thread's dynamic priority, which it uses to make scheduling decisions. A thread's dynamic priority is never less than its base priority. The scheduler raises and lowers the dynamic priority of a thread to enhance its responsiveness when significant things happen to the thread. For example, whenever a window receives input (such as timer messages, mouse move messages, keyboard input), the scheduler boosts the priorities of all threads within the process that owns the window. In some cases, the dynamic priority of a waiting thread is raised when a wait is satisfied. For example, a wait associated with disk or keyboard I/O results in a dynamic boost when the input occurs.
After raising a thread's dynamic priority, the scheduler reduces that priority by one level each time the thread completes a time slice, until the thread drops back to its base priority.
In addition to these dynamic priority boosts, the scheduler raises the priority class of the process associated with the foreground window, so it is greater than or equal to the priority class of any background processes. The process's priority class returns to its original setting when it is no longer in the foreground. A user can always make a given window lose the foreground priority boost by clicking on another window or by using the ALT+TAB or CTRL+ESC key combination.
The priority classes are intended to be used as follows.
Priority Meaning
HIGH_PRIORITY_CLASS Indicates a process that performs time-critical tasks that must be executed immediately for it to run correctly. The threads of a high-priority class process preempt the threads of normal or idle priority class processes. An example is Windows Task List, which must respond quickly when called by the user, regardless of the load on the operating system. Use extreme care when using the high-priority class, because a high-priority class CPU-bound application can use nearly all available cycles.
IDLE_PRIORITY_CLASS Indicates a process whose threads run only when the system is idle and are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle priority class is inherited by child processes.
NORMAL_PRIORITY_CLASS Indicates a normal process with no special scheduling needs.
REALTIME_PRIORITY_CLASS Indicates a process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.

Use the high-priority class with extreme care. If a thread runs at the highest priority level for extended periods, other threads in the system will be starved for CPU time. If several threads are set at high priority at the same time, the effect of the high priority is neutralized. The high-priority class should be reserved for threads that must respond to time critical events. If your application has a task that requires the high-priority class while most of its functions are normal priority, use SetPriorityClass_win32_SetPriorityClass to raise the priority class temporarily; then reduce it once the time-critical task has been completed. Another strategy is to create a high-priority process that is blocked most of the time, awakening only when the critical task is needed. The important point is that a thread should be high priority and executable only for a brief period, when it has time-critical work to perform.
Having selected a priority class for a multithreaded process, use SetThreadPriority_win32_SetThreadPriority to adjust the relative priority of its threads. A typical strategy is to use a higher priority level for the process's input thread, to ensure responsiveness to the user. Other threads, particularly those that are CPU intensive, can be set to a lower relative priority, to ensure that they can be preempted when necessary. However, if you have a higher priority thread waiting for a lower priority thread to complete some task, be sure to block the execution of the waiting thread (using a wait function, critical section, or the Sleep_win32_Sleep function) rather than having it iterate in a loop. Otherwise, the process will deadlock, because the lower priority thread will never get scheduled.
+$#A Creating Threads
The CreateThread_win32_CreateThread function creates a new thread for a process. The creating thread must specify the starting address of the code that the new thread is to execute. Typically, the starting address is the name of a function defined in the program's code. A process can have multiple threads simultaneously executing the same function. The function called by the thread takes a single 32-bit parameter and returns a doubleword result.
The following example illustrates the creation of a new thread that executes the locally defined ThreadFunc function.
DWORD ThreadFunc(LPDWORD lpdwParam) {
printf("ThreadFunc: thread parameter=%d\n", *lpdwParam);
return 0;
}

DWORD main(VOID) {
DWORD dwThreadId, dwThrdParam = 1;
HANDLE hThread;
hThread = CreateThread(
NULL, /* no security attributes */
0, /* use default stack size */
(LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
&dwThrdParam, /* argument to thread function */
0, /* use default creation flags */
&dwThreadId); /* returns the thread identifier */

/* Check the return value for success. */

if (hThread == NULL)
ErrorExit("CreateThread error\n");

For simplicity, this example passes a pointer to dwThrdParam as an argument to the thread function. This could be a pointer to any type of data or structure, or it could be omitted altogether by passing a NULL pointer and deleting the references to the parameter in ThreadFunc. It is risky to pass the address of the local variable if the creating thread exits before the new thread, because the pointer becomes invalid. Instead, either pass dynamically allocated parameters or make the creating thread wait for the new thread to terminate. Data can also be passed from the creating thread to the new thread using global variables. With global variables, it is usually necessary to synchronize access by multiple threads. For more information about synchronizing, see Synchronizing Execution of Multiple Threads_win32_Synchronizing_Execution_of_Multiple_Threads.
Using the thread function's argument to pass data to the new thread is particularly useful in processes where the main thread might create multiple threads to execute the same code. In this case, it is inconvenient to use global variables. For example, a process that enables the user to open several files at the same time can create a new thread for each file, with each of the threads executing the same thread function. The creating thread can pass as an argument the unique information (such as the filename) required by each instance of the thread function.
The creating thread can use the arguments to CreateThread_win32_CreateThread to specify the following:
· The security attributes for the handle of the new thread. These security attributes include an inheritance flag that determines whether the handle can be inherited by child processes. The security attributes also include a security descriptor, which the system uses to perform access checks on all subsequent uses of the thread's handle before access is granted. For more information about security attributes, see Security_win32_Security.
· The initial stack size of the new thread. The thread's stack is allocated automatically in the memory space of the process; it can grow as needed and it is freed when the thread terminates.
· A creation flag that enables you to create the thread in a suspended state. When suspended, the thread does not run until the ResumeThread_win32_ResumeThread function is called.

The handle returned by CreateThread_win32_CreateThread has full-access rights to the new thread. Subject to security checks, the handle can be used in any of the Win32 API functions that accepts a handle to a thread object. The handle can be duplicated by DuplicateHandle_win32_DuplicateHandle, which enables you to create a thread handle with a subset of the access rights.
A thread can also be created with the CreateRemoteThread_win32_CreateRemoteThread function. This is used by debugger processes to create a thread that runs in the address space of another process.
+$#A Suspending Thread Execution
A thread can suspend and resume the execution of another thread using the SuspendThread_win32_SuspendThread and ResumeThread_win32_ResumeThread functions. While a thread is suspended, it is not scheduled for time on the processor.
SuspendThread is not particularly useful for synchronization because it does not control the point in the code at which the thread's execution is suspended. However, you might want to suspend a thread in a situation where you are confirming a user's input that would terminate the work of the thread. If confirmed, the thread is terminated; otherwise, it resumes.
If a thread is created in a suspended state, it does not begin to run until ResumeThread is called with a handle to the suspended thread. This can be useful for initializing the thread's state before it begins to run. See Using a Multithreaded Multiple Document Interface Application_win32_Using_a_Multithreaded_Multiple_Document_Interface_Application for an example that uses this method to modify the thread's priority before it can run. Suspending a thread at creation can be useful for one-time synchronization, because ResumeThread ensures that the suspended thread will resume running at the starting point of its code.
A thread can temporarily suspend its own execution for a specified interval by calling the Sleep_win32_Sleep or SleepEx_win32_SleepEx functions. This is useful particularly in cases where the thread responds to user interaction, as it can delay execution long enough to enable the users to observe the results of their actions. During the sleep interval, the thread is not scheduled for time on the processor.
+$#A Synchronizing Execution of Multiple Threads
To avoid conflicts, it is necessary to synchronize access by multiple threads to shared resources. Synchronization is also necessary to ensure that interdependent code is executed in the proper sequence.
The Win32 API provides a number of objects whose handles can be used to synchronize multiple threads. These objects include:
· Synchronization objects: mutex, semaphore, and event handles
· File handles
· Named pipe handles
· Console input buffer handles
· Communication device handles
· Process handles
· Thread handles

The state of each of these objects is either signaled or not signaled. When a handle to any of these objects is specified by a call to one of the wait functions ¾ WaitForSingleObject_win32_WaitForSingleObject, WaitForMultipleObjects_win32_WaitForMultipleObjects, WaitForSingleObjectEx_win32_WaitForSingleObjectEx, or WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx ¾ the execution of the calling thread is blocked until the state of the specified object is signaled. As with a suspended thread, a waiting thread consumes very little processor time.
Some of these objects are useful in blocking a thread until some event occurs. For example, a console input buffer handle is signaled when there is unread input, such as a keystroke or mouse button press. Process and thread handles are signaled when the process or thread terminates. This allows a process, for example, to create a child process and then block its own execution until the new process has terminated.
Other objects are useful in protecting shared resources from simultaneous access. For example, multiple threads can each have a handle to a mutex object. Before accessing a shared resource, the threads must call one of the wait functions to wait for the state of the mutex to be signaled. When the mutex becomes signaled, only one waiting thread is released to access the resource. The state of the mutex is immediately reset to not signaled so any other waiting threads remain blocked. When the thread is finished with the resource, it must set the state of the mutex to signaled to free other threads to access the resource.
For the threads of a single process, critical-section objects provide another (and slightly more efficient) means of synchronization. A critical section is used like a mutex to enable only one thread at a time to use the protected resource. A thread uses the EnterCriticalSection_win32_EnterCriticalSection function to request ownership of a critical section. If it is already owned by another thread, the requesting thread is blocked. After it receives ownership, the thread is free to use the protected resource. The execution of the other threads of the process is not affected unless they attempt to enter the same critical section.
The WaitForInputIdle_win32_WaitForInputIdle function makes a thread wait until a specified process is initialized and waiting for user input with no input pending. This can be useful for synchronizing parent and child processes, because CreateProcess_win32_CreateProcess returns without waiting for the child process to complete its initialization.
For more about critical sections and synchronization, see Synchronization_win32_Synchronization.
+$#A Thread Local Storage
All threads of a process share the virtual address space and the global variables of that process. Sometimes it is desirable to have static storage that is local to each thread. Of course, the local variables of a thread function are local to each thread that runs the function. But when a thread invokes another function, the static or global variables used by that function store the same value for all threads. With thread local storage (TLS), one thread allocates an index that can be used by any thread of the process to store and retrieve a different value for each thread.
The following is a typical TLS scenario:
1. The TlsAlloc_win32_TlsAlloc function is used during process or dynamic-link library (DLL) initialization to allocate a TLS index.
2. Each thread that needs to use the TLS index allocates dynamic storage, then uses the TlsSetValue_win32_TlsSetValue function to associate the index with a pointer to the dynamic storage.
3. When a thread needs to access its storage, it specifies the TLS index in a call to the TlsGetValue_win32_TlsGetValue function to retrieve the pointer.
4. Each thread frees the dynamic storage that it has associated with a TLS index when it is no longer needed. When all threads have finished using a TLS index, the TlsFree_win32_TlsFree function frees the index.

The constant TLS_MINIMUM_AVAILABLE defines the minimum number of TLS indexes available in each process. This minimum is guaranteed to be at least 64 for all systems.
TLS can be useful with DLLs. The DllEntryPoint_win32_DllEntryPoint function performs the initial TLS operations in the context of the process or thread the DLL is attaching to or detaching from. When the DLL attaches to a new process, the entry-point function calls TlsAlloc_win32_TlsAlloc to allocate a TLS index for that process. When the DLL attaches to a new thread of that process, the entry-point function allocates dynamic memory for that thread and uses TlsSetValue_win32_TlsSetValue to save private data to the index. The DLL stores the TLS index in a global variable that is private to each attached process. The functions of the DLL can use the TLS index in a call to TlsGetValue_win32_TlsGetValue to access the private data for the calling thread.
For an example illustrating the use of thread local storage, see Using Thread Local Storage_win32_Using_Thread_Local_Storage.
+$#A Terminating a Thread
A thread runs until one of the following events occurs:
· It calls the ExitThread_win32_ExitThread function.
· The thread function returns. This results in an implicit call to the ExitProcess_win32_ExitProcess function for a primary thread, or to the ExitThread function for other threads.
· ExitProcess is invoked explicitly or implicitly by any thread of the process.
· The TerminateThread_win32_TerminateThread function is called with a handle to the thread.
· The TerminateProcess_win32_TerminateProcess function is called with a handle to the thread's process.

When a thread terminates, the state of the thread object changes to signaled, releasing any other threads that had been waiting for the thread to terminate. Then its termination status changes from STILL_ACTIVE to the exit value of the thread.
If a thread is terminated by ExitThread_win32_ExitThread, the entry-point function of all attached DLLs is invoked with a value indicating that the thread is detaching from the DLL. If a thread is terminated by ExitProcess_win32_ExitProcess, the DLL entry-point functions are invoked once, to indicate that the process is detaching. DLLs are not notified when a thread is terminated by TerminateThread_win32_TerminateThread or TerminateProcess. For more information about DLLs, see Dynamic-Link Libraries_win32_Dynamic_Link_Libraries.
The GetExitCodeThread_win32_GetExitCodeThread function returns the termination status of a thread. While a thread is executing, its termination status is STILL_ACTIVE. When a thread terminates, its termination status is the exit value of the thread, which is the value specified in the call to ExitThread, ExitProcess, or TerminateThread, or the value returned by the thread function.
+$#A Multiple Threads and Graphics Device Interface Objects
To enhance performance, access to graphics device interface (GDI) objects (such as palettes, device contexts (DC), regions, and the like) is not serialized. This creates a potential danger for processes that have multiple threads sharing these objects. For example, if one thread deletes a GDI object while another thread is using it, the results are unpredictable. This danger can be avoided simply by not sharing GDI objects. If sharing is unavoidable (or desirable), the application must provide its own mechanisms for synchronizing access. For more information about synchronizing access, see Synchronizing Execution of Multiple Threads_win32_Synchronizing_Execution_of_Multiple_Threads.
+$#A Creating Processes
The CreateProcess_win32_CreateProcess function creates a new process, which runs independently of the creating process. The function provides two methods for identifying the program to be executed. The most common method is to specify the image file's path and filename. If the lpszImageName parameter is NULL, the program is identified in the lpszCommandLine parameter.
If CreateProcess succeeds, it returns a PROCESS_INFORMATION_win32_PROCESS_INFORMATION_str structure containing handles and identification numbers for the new process and its primary thread. The thread and process handles are created with full access rights, although access can be restricted if a security descriptor is specified.
+$#A Inheritance
A child process can inherit a number of properties and resources from its parent process. You can also prevent a child process from inheriting anything from its parent process. The following can be inherited:
· Any inheritable open handle returned by the CreateFile_win32_CreateFile function. This includes handles to files, console input buffers, console screen buffers, named pipes, serial communication devices, and mailslots.
· Inheritable open handles to process, thread, mutex, event, semaphore, named pipe, anonymous pipe, and file mapping objects.
· Environment variables.
· The current directory.
· The console of parent process, unless the process is detached or a new console is created. A child console process also inherits the parent's standard handles, as well as access to the input buffer and the active screen buffer. For more about console processes, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support.

The fInheritHandles parameter of CreateProcess_win32_CreateProcess determines whether the inheritable handles of the parent process are inherited by the child process. The inheritability of the handles is specified in the functions used to create, open, or duplicate the handles. This allows a child process to inherit some of its parent's handles, while not inheriting others. For each of these object types there is a creation function (such as CreateProcess or CreateFile_win32_CreateFile) that takes a security attributes argument via a flag determines whether the handle can be inherited. If the security attributes argument is NULL, the handle is not inherited.
Some of these objects have an open function (such as OpenMutex_win32_OpenMutex or OpenEvent_win32_OpenEvent) with an inherit flag parameter that determines inheritability.
Any of these handles can be duplicated using the DuplicateHandle_win32_DuplicateHandle function by which you can specify whether the handle can be inherited. This can be useful if a process has an inheritable open handle that you do not want to be inherited by the child process. In this case, use DuplicateHandle to open a noninheritable duplicate of the handle, then use the CloseHandle_win32_CloseHandle function to close the inheritable handle. DuplicateHandle can also open an inheritable duplicate of a noninheritable handle.
An inherited handle has the same value and access privileges as the parent process's handle. To use a handle, the child process must retrieve the handle and know the object to which it refers. For most handles, the parent process must communicate this information to the child process. This can be done using the command line or any of the other methods for interprocess communication (such as pipes or shared memory).
An inherited handle refers to the same object as the parent process's handle does. When one process changes the state of the object, the change affects both processes. With file handles, for example, if one process uses a handle to read from a file, the current file position is moved for all processes using the same handle.
A child process inherits the environment variables and current directory of its parent process by default. However, CreateProcess_win32_CreateProcess enables the parent process to specify a different current directory or block of environment variables. For more information about environment variables, see Environment Variables_win32_Environment_Variables
The child process does not inherit the following:
· Priority class.
· Memory handles.
· Pseudohandles, as in the handles returned by the GetCurrentProcess_win32_GetCurrentProcess or GetCurrentThread_win32_GetCurrentThread function. These handles are valid only for the calling process. For more information about these functions, see Process and Thread Handles and Identifiers_win32_Process_and_Thread_Handles_and_Identifiers.
· DLL module handles returned by the LoadLibrary_win32_LoadLibrary function.
· GDI or USER handles, such as HBITMAP or HMENU. Refer to WINDEF.H for a complete list of these handles.

+$#A Environment Variables
Every process has an environment block that contains a set of environment variables and their values. The command processor for Microsoft® Windows NT™ (called CMD.EXE) provides the set command to display its environment block or to create new environment variables. Programs started by the command processor inherit the command processor's environment variables.
By default, a child process inherits the environment variables of its parent process. However, you can specify a different environment for the child process by creating a new environment block and passing a pointer to it as a parameter to the CreateProcess_win32_CreateProcess function.
The GetEnvironmentStrings_win32_GetEnvironmentStrings function returns a pointer to the environment block of the calling process. This should be treated as a read-only block; do not modify it directly. Instead, use the SetEnvironmentVariable_win32_SetEnvironmentVariable function to change an environment variable. The GetEnvironmentVariable_win32_GetEnvironmentVariable function determines whether a specified variable is defined in the environment of the calling process, and, if so, what its value is. For more information about environments, see the examples in Changing Environment Variables_win32_Changing_Environment_Variables.
+$#A Setting Window Properties Using STARTUPINFO
A parent process can specify properties associated with the main window of its child process. A pointer to a STARTUPINFO_win32_STARTUPINFO_str structure is passed as a parameter to the CreateProcess_win32_CreateProcess function. The members of this structure can be used to specify more than a dozen different characteristics of the child's main window. The dwFlags member contains a bit field that determines whether the other members of the structure are used. This allows the creating process to specify values for any subset of the window properties. Default values are used for the properties not specified by dwFlags. The dwFlags member can also force a feedback cursor to be displayed during the initialization of the new process. A process can use the GetStartupInfo_win32_GetStartupInfo function to retrieve the STARTUPINFO structure specified when it was created.
For GUI processes, the STARTUPINFO structure specifies the default values to be used the first time the new process calls the CreateWindow_win32_CreateWindow and ShowWindow_win32_ShowWindow functions to create and display an overlapped window. The following default values can be specified:
· The width and height, in pixels, of the window created by CreateWindow.
· The location in screen coordinates of the window created by CreateWindow.
· The nCmdShow parameter of ShowWindow.

For console processes, use the STARTUPINFO_win32_STARTUPINFO_str specifications only when a new console is created either by CreateProcess_win32_CreateProcess with CREATE_NEW_CONSOLE specified, or by the AllocConsole_win32_AllocConsole function. For more about console processes, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support. STARTUPINFO can specify the following console window properties:
· The size, in character cells, and the location in screen coordinates of the new console window.
· The size, in character cells, of the new console's screen buffer.
· The text and background color attributes of the new console's screen buffer.
· The title of the new console's window.

+$#A Terminating a Process
A process terminates when one of the following occurs:
· Any thread of the process calls the ExitProcess_win32_ExitProcess function.
· The primary thread of the process returns. This results in an implicit call to ExitProcess.
· The last thread of the process terminates.
· The TerminateProcess_win32_TerminateProcess function is called with a handle to the process.

When a process terminates, the following events occur:
· Open handles to files or other resources are closed automatically. Note that the objects referenced by these handles exist until all open handles are closed. This means that an object remains valid after a process closes, if another process has a handle to it.
· The state of the process object becomes signaled, satisfying any threads that are waiting for the process to terminate. For more about synchronization, see Synchronizing Execution of Multiple Threads_win32_Synchronizing_Execution_of_Multiple_Threads.
· The termination status of the process changes from STILL_ACTIVE to the exit value of the process.

Child processes are not automatically terminated when a parent process terminates.
ExitProcess_win32_ExitProcess is the preferred way to terminate a process, because it notifies all attached DLLs of the termination, and it ensures that all threads of the process are terminated. If a process is terminated by ExitProcess, the entry-point function of all attached DLLs is invoked with a value indicating that the process is detaching from the DLL. DLLs are not notified when a process is terminated by TerminateProcess_win32_TerminateProcess. For more information about DLLs, see Dynamic-Link Libraries_win32_Dynamic_Link_Libraries.
When a process is terminated by ExitProcess or TerminateProcess, all threads of the process are terminated immediately with no chance to run additional code (except for the DLL process detach code if ExitProcess is called). This means that the process does not execute code in termination handler blocks such as the finally block of the try-finally termination handler supported by Microsoft compilers. For more information about handlers, see Structured Exception Handling_win32_Structured_Exception_Handling.
The primary thread of a process differs from the other threads of a process in that ExitProcess (rather than the ExitThread_win32_ExitThread function) is invoked implicitly when a primary thread returns. Alternatively, the primary thread can avoid terminating other threads by explicitly calling ExitThread before it exits. In this case, one of the remaining threads explicitly calls ExitProcess, to ensure that all threads of the process are terminated. For example, it is possible for an attached DLL to create a thread that does not terminates unless ExitProcess is invoked.
ExitProcess can be invoked implicitly by the system. For most unhandled exceptions, the system's default action is to call ExitProcess. For console processes, ExitProcess is called by the default handler function that can be invoked when a CTRL+C or CTRL+BREAK signal is received. For more about console application, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support. Note that this default handling of console interrupt signals can terminate parent and child processes, because all processes attached to a console receive the signal. An interrupt signal received by a console window is propagated only to attached console processes. Detached processes and processes that have created new console windows are not affected. GUI processes are not affected by CTRL+C or CTRL+BREAK signals.
The GetExitCodeProcess_win32_GetExitCodeProcess function returns the termination status of a process. While a process is running, its termination status is STILL_ACTIVE. When a process terminates, its termination status is the exit value of the process, which is the value specified in the call to ExitProcess_win32_ExitProcess or TerminateProcess_win32_TerminateProcess, or the value returned by the main or WinMain_win32_WinMain function of the process. If a process is terminated in some other way (for example, by a fatal, unhandled exception or a CTRL+C signal) the process's termination value is the exception value that caused the termination. The process's exit value is also used as the exit value of all of the threads that had not previously terminated.
+$#A Process and Thread Handles and Identifiers
When a new thread is created by the CreateThread_win32_CreateThread or CreateRemoteThread_win32_CreateRemoteThread function, a handle of the thread is returned. Similarly, when a new process is created by the CreateProcess_win32_CreateProcess function, handles of the new process and its primary thread are returned. These handles are created with full access rights, and ¾ subject to security access checking ¾ can be used in any of the functions that accepts a thread or process handle. These handles can be inherited by child processes, depending on the inheritance flag specified when they are created. The handles are valid until closed, even after the process or thread they represent is terminated.
The OpenProcess_win32_OpenProcess function opens a handle to a process specified by its process identifier. OpenProcess enables you to specify the handle's access rights and inheritability.
A process can use the GetCurrentProcess_win32_GetCurrentProcess function to retrieve a pseudohandle of its own process object; a thread can use the GetCurrentThread_win32_GetCurrentThread function to retrieve a pseudohandle to its own thread object. These pseudohandles are valid only for the calling process; they cannot be inherited or duplicated for use by other processes.
The functions that create a process or thread also return an identifier that uniquely identifies the process or thread throughout the system. A process can use the GetCurrentProcessId_win32_GetCurrentProcessId function to get its own process identifier. Similarly, a thread can use the GetCurrentThreadId_win32_GetCurrentThreadId function to get its own thread identifier. The identifiers are valid from the time the thread or process is created until the thread or process terminates.
+$#A Using Processes and Threads
· Using a multithreaded multiple document interface application_win32_Using_a_Multithreaded_Multiple_Document_Interface_Application
· Creating a child process with redirected input and output_win32_Creating_a_Child_Process_with_Redirected_Input_and_Output
· Changing environment variables_win32_Changing_Environment_Variables
· Using thread local storage_win32_Using_Thread_Local_Storage

+$#A Using a Multithreaded Multiple Document Interface Application
The following example shows how to use multiple threads in a multiple document interface (MDI) process. The process has a single main window, but can have any number of child windows. The primary thread of the process performs initialization and also handles messages to all windows through the application-defined MainWndProc and ChildWndProc functions.
Each time a child window is created, a new thread is also created. In this example, the new thread only checks continually to see if it is time to terminate. In a real application, the new thread could be used to draw the contents of the window or to carry out the functions associated with the window.
The ThreadFunc function is specified in the CreateThread_win32_CreateThread function as the code for the new thread to execute. The handle of the child window associated with the thread is passed as a parameter to ThreadFunc. The child window's handle is also a parameter to ChildWndProc when a message is dispatched to the child window. A handle is necessary for any communication between a child window and its corresponding thread. Both ThreadFunc and ChildWndProc use the window handle in the SetWindowLong_win32_SetWindowLong function to access the 32-bit value that is reserved for application use in each window structure. In this example, the 32-bit value is a termination flag. When ChildWndProc gets the WM_CLOSE_win32_WM_CLOSE message, it sets the flag; ThreadFunc checks the flag each time through its loop.
This example shows how to use normal priority for the primary thread and below-normal priority for the other threads. Because the primary thread passes all messages for both the main window and the child windows, its higher relative priority ensures responsiveness to user input. (Even when the child threads are left at normal priority, the scheduler automatically boosts the priority of the input thread when necessary.)
When the user terminates the process by closing the main window, a global Boolean parameter is set to terminate the child threads. At this point, the primary thread waits for each child thread to terminate before proceeding. This is necessary only if you want the threads to clean up, save changes to a file, or detach from DLLs before closing. Without waiting, the primary thread terminates the process by returning before the lower priority threads were able to run any additional code.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#define MM_NEWWIN 8001
typedef struct _PTHREADLIST {
HANDLE hThread;
LPVOID lpvNext;
} THREADLIST, *PTHREADLIST;

/* Globals */

HANDLE hModule; /* handle to .EXE file for this process */
HWND hwndMain = NULL; /* handle to main window */
BOOL fKillAll = FALSE; /* sets TRUE to terminate all threads */
PTHREADLIST pHead = NULL; /* head of linked list of thread info. */

/* Forward declarations */

BOOL InitializeApp(VOID);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
DWORD ThreadFunc(HWND);
VOID AddThreadToList(HANDLE);
VOID ErrorExit(LPSTR);

/* Primary thread: Initialize the application and dispatch messages. */

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
LPSTR lpszCmdLn, int nShowCmd) {
MSG msg;
hModule = GetModuleHandle(NULL);
if (! InitializeApp())
ErrorExit("InitializeApp failure!");
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
UNREFERENCED_PARAMETER(hInst);
UNREFERENCED_PARAMETER(hPrevInst);
UNREFERENCED_PARAMETER(lpszCmdLn);
UNREFERENCED_PARAMETER(nShowCmd);
}

/* Register window classes and create the main window. */

BOOL InitializeApp(VOID) {
HMENU hmenuMain, hmenuPopup;
WNDCLASS wc;

/* Register a window class for the main window. */

wc.style = CS_OWNDC;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hModule;
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWindowClass";
if (! RegisterClass(&wc))
return FALSE;

/* Register a window class for child windows. */

wc.lpfnWndProc = ChildWndProc;
wc.lpszClassName = "ThreadWindowClass";
if (! RegisterClass(&wc))
return FALSE;

/* Create a menu for the main window. */

hmenuMain = CreateMenu();
hmenuPopup = CreateMenu();
if (! AppendMenu(hmenuPopup, MF_STRING, MM_NEWWIN, "&New Window"))
return FALSE;
if (! AppendMenu(hmenuMain, MF_POPUP, (UINT) hmenuPopup, "&Threads"))
return FALSE;

/* Create the main window. */

hwndMain = CreateWindow("MainWindowClass", "Primary Window",
WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_THICKFRAME |
WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN |
WS_VISIBLE | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, hmenuMain, hModule,
NULL);
if (hwndMain == NULL)
return FALSE;

/* Set the initial focus. */

SetFocus(hwndMain);
return TRUE;
}

/* Main window procedure: Handle messages for the main window. */

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uiMessage,
WPARAM wParam, LPARAM lParam) {
static HWND hwndClient;
static DWORD dwCount = 1;
CLIENTCREATESTRUCT ccsClientCreate;
HWND hwndChildWnd;
DWORD IDThread;
PTHREADLIST pNode;

switch (uiMessage) {

/* Create a client window to receive child window messages. */

case WM_CREATE:

ccsClientCreate.hWindowMenu = NULL;
ccsClientCreate.idFirstChild = 1;
hwndClient = CreateWindow("MDICLIENT", NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 0, 0,
hwnd, NULL, hModule, (LPVOID)&ccsClientCreate);
return 0L;

/*
* Close the main window. First set fKillAll to TRUE to
* terminate all threads. Then wait for the threads to exit
* before passing a close message to a default handler. If you
* don't wait for threads to terminate, process terminates
* with no chance for thread cleanup.
*/

case WM_CLOSE:

fKillAll = TRUE;
pNode = pHead;
while (pNode) {
DWORD dwRes;
SetThreadPriority(pNode->hThread,
THREAD_PRIORITY_HIGHEST);
dwRes = WaitForSingleObject(pNode->hThread,
INFINITE);
pNode = (PTHREADLIST) pNode->lpvNext;
}
return DefFrameProc(hwnd, hwndClient, uiMessage,
wParam, lParam);

/* Terminate the process. */

case WM_DESTROY:

PostQuitMessage(0);
return 0L;

/* Handle the menu commands. */

case WM_COMMAND:

switch (LOWORD(wParam)) {

/* Create a child window and start a thread for it. */

case MM_NEWWIN: {
HANDLE hThrd;
MDICREATESTRUCT mdicCreate;
TCHAR tchTitleBarText[32];
LONG lPrev;

sprintf(tchTitleBarText, "Thread Window %d", dwCount);
mdicCreate.szClass = "ThreadWindowClass";
mdicCreate.szTitle = tchTitleBarText;
mdicCreate.hOwner = hModule;
mdicCreate.x = mdicCreate.y =
mdicCreate.cx = mdicCreate.cy = CW_USEDEFAULT;
mdicCreate.style = mdicCreate.lParam = 0L;

/*
* Send a "create child window" message to the
* client window.
*/

hwndChildWnd = (HWND) SendMessage(hwndClient,
WM_MDICREATE, 0L, (LONG)&mdicCreate);
if (hwndChildWnd == NULL)
ErrorExit("Failed in Creating Thread Window!");

/*
* Window structure used to pass a quit message to
* the thread.
*/

lPrev = SetWindowLong(hwndChildWnd, GWL_USERDATA, 0);

/* Create suspended, alter priority before resuming. */

hThrd = CreateThread(NULL, /* no security attributes */
0, /* use default stack size */
(LPTHREAD_START_ROUTINE) ThreadFunc,
(LPVOID)hwndChildWnd, /* param to thrd func */
CREATE_SUSPENDED, /* creation flag */
&IDThread); /* returns thread id */
if (hThrd == NULL)
ErrorExit("CreateThread Failed!");
AddThreadToList(hThrd);
dwCount++;

/*
* Set the priority lower than the primary (input)
* thread, so that the process is responsive to user
* input. Then resume the thread.
*/

if (! SetThreadPriority(hThrd,
THREAD_PRIORITY_BELOW_NORMAL))
ErrorExit("SetThreadPriority failed!");
if ((ResumeThread(hThrd)) == -1)
ErrorExit("ResumeThread failed!");
return 0L;
}

default:

return DefFrameProc(hwnd, hwndClient, uiMessage,
wParam, lParam);
}

default:

return DefFrameProc(hwnd, hwndClient, uiMessage,
wParam, lParam);
}
}

/* Handle messages for the child windows. */

LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT uiMessage, WPARAM wParam,
LPARAM lParam) {
LONG lPrevLong;
switch (uiMessage) {

/* Use a window structure to pass "close" message to thread. */

case WM_CLOSE:

lPrevLong = SetWindowLong(hwnd, GWL_USERDATA, 1);
return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);

case WM_DESTROY:

return 0L;

default:

return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);
}
}

/*
* Each child window has a thread that can be used to perform tasks
* associated with that window--for example, drawing its contents.
*/

DWORD ThreadFunc(HWND hwnd) {
LONG lKillMe = 0L;
while (TRUE) {
lKillMe = GetWindowLong(hwnd, GWL_USERDATA);
if (fKillAll || lKillMe) break;

/* Perform tasks. */

}

/* Perform actions needed before thread termination. */

return 0;
}

VOID AddThreadToList(HANDLE hThread) {
PTHREADLIST pNode;
pNode = (PTHREADLIST) LocalAlloc(LPTR, sizeof(PTHREADLIST));
if (pNode == NULL)
ErrorExit("malloc Failed!");
pNode->hThread = hThread;
pNode->lpvNext = (LPVOID) pHead;
pHead = pNode;
}

VOID ErrorExit(LPSTR lpszMessage) {
MessageBox(hwndMain, lpszMessage, "Error", MB_OK);
ExitProcess(0);
}

+$#A Creating a Child Process with Redirected Input and Output
The following example shows the creation of a child process by a console process and illustrates a technique for using anonymous pipes to redirect the child process's standard input and output. If you have a program that can read its input from the standard input (STDIN) or write its results to the standard output (STDOUT), redirected I/O enables you to execute the program as a child process while controlling its input and receiving its output.
The example shows how to use a SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure to create handles that can be inherited by the child process. The structure is used in the CreatePipe_win32_CreatePipe function to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the child's standard output. These pipe handles are specified in the SetStdHandle_win32_SetStdHandle function, which makes them the standard handles inherited by the child process. After the child process is created, SetStdHandle is used again to restore the original standard handles for the parent process.
The parent process uses the other ends of the pipes to write to the child's input and read the child's output. The handles to these ends of the pipe are also inheritable. However, the handle must not be inherited. Before creating the child process, the parent process must use DuplicateHandle_win32_DuplicateHandle to create a noninheritable duplicate of the application-defined hChildStdinWr global variable. It then uses CloseHandle_win32_CloseHandle to close the inheritable handle. This leaves the parent process with a pipe handle it can use, but which the child process does not inherit. For more information about pipes, see Pipes_win32_Pipes.
The following is the parent process.
#include <stdio.h>
#include <windows.h>

#define BUFSIZE 4096

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr,
hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID);
VOID WriteToPipe(VOID);
VOID ReadFromPipe(VOID);
VOID ErrorExit(LPTSTR);
VOID ErrMsg(LPTSTR, BOOL);

DWORD main(int argc, char *argv[]) {
SECURITY_ATTRIBUTES saAttr;
BOOL fSuccess;

/* Set the bInheritHandle flag so pipe handles are inherited. */

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;

/*
* The steps for redirecting child's STDOUT:
* 1. Save current STDOUT, to be restored later.
* 2. Create anonymous pipe to be STDOUT for child.
* 3. Set STDOUT of parent to be write handle of pipe, so
* it is inherited by child.
*/

/* Save the handle to the current STDOUT. */

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Create a pipe for the child's STDOUT. */

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
ErrorExit("Stdout pipe creation failed\n");

/* Set a write handle to the pipe to be STDOUT. */

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
ErrorExit("Redirecting STDOUT failed");

/*
* The steps for redirecting child's STDIN:
* 1. Save current STDIN, to be restored later.
* 2. Create anonymous pipe to be STDIN for child.
* 3. Set STDIN of parent to be read handle of pipe, so
* it is inherited by child.
* 4. Create a noninheritable duplicate of write handle,
* and close the inheritable write handle.
*/

/* Save the handle to the current STDIN. */

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

/* Create a pipe for the child's STDIN. */

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
ErrorExit("Stdin pipe creation failed\n");

/* Set a read handle to the pipe to be STDIN. */

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
ErrorExit("Redirecting Stdin failed");

/* Duplicate the write handle to the pipe so it is not inherited. */

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
GetCurrentProcess(), &hChildStdinWrDup, 0,
FALSE, /* not inherited */
DUPLICATE_SAME_ACCESS);
if (! fSuccess)
ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdinWr);

/* Now create the child process. */

if (! CreateChildProcess())
ErrorExit("Create process failed");

/* After process creation, restore the saved STDIN and STDOUT. */

if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
ErrorExit("Re-redirecting Stdin failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
ErrorExit("Re-redirecting Stdout failed\n");

/* Get a handle to the parent's input file. */

if (argc > 1)
hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
else
hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)
ErrorExit("no input file\n");

/* Write to pipe that is the standard input for a child process. */

WriteToPipe();

/* Read from pipe that is the standard output for child process. */

ReadFromPipe();

return 0;
}

BOOL CreateChildProcess() {
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;

/* Set up members of STARTUPINFO structure. */

siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.lpReserved = NULL;
siStartInfo.lpReserved2 = NULL;
siStartInfo.cbReserved2 = 0;
siStartInfo.lpDesktop = NULL;
siStartInfo.dwFlags = 0;

/* Create the child process. */

return CreateProcess(NULL,
"child", /* command line */
NULL, /* process security attributes */
NULL, /* primary thread security attributes */
TRUE, /* handles are inherited */
0, /* creation flags */
NULL, /* use parent's environment */
NULL, /* use parent's current directory */
&siStartInfo, /* STARTUPINFO pointer */
&piProcInfo); /* receives PROCESS_INFORMATION */

VOID WriteToPipe(VOID) {
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];

/* Read from a file and write its contents to a pipe. */

for (;;) {
if (! ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ||
dwRead == 0) break;
if (! WriteFile(hChildStdinWrDup, chBuf, dwRead,
&dwWritten, NULL)) break;
}

/* Close the pipe handle so the child stops reading. */

if (! CloseHandle(hChildStdinWrDup))
ErrorExit("Close pipe failed\n");
}

VOID ReadFromPipe(VOID) {
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/*
* Close the write end of the pipe before reading from the
* read end of the pipe.
*/

if (! CloseHandle(hChildStdoutWr))
ErrorExit("Closing handle failed");

/* Read output from child, and write it to parent's STDOUT. */

for (;;) {
if (! ReadFile(hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) ||
dwRead == 0) break;
if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))
break;
}
}

VOID ErrorExit (LPTSTR lpszMessage) {
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}

Here's the child process:
#include <windows.h>
#define BUFSIZE 4096

VOID main(VOID) {
CHAR chBuf[BUFSIZE];
DWORD dwRead, dwWritten;
HANDLE hStdin, hStdout;
BOOL fSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if ((hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE))
ExitProcess(1);

for (;;) {

/* Read from standard input. */

fSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
if (! fSuccess || dwRead == 0)
break;

/* Write to standard output. */

fSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);
if (! fSuccess)
break;
}
}

+$#A Changing Environment Variables
The environment block consists of a null-terminated block of null-terminated strings (that is, two null bytes at the end of the block), where each string is in the form:
name=value

Because the equal sign is a separator, it must not be used in the name of an environment variable. The following example creates a new environment block to pass to a child process.
TCHAR tchNewEnv[BUFSIZE];
LPTSTR lpszCurrentVariable;
BOOL fSuccess;

/* Copy name=value environment variable pairs into environment block. */

lpszCurrentVariable = tchNewEnv;
if (lstrcpy(lpszCurrentVariable, "OperatingSystem=Windows") == NULL)
ErrorExit("lstrcpy failed");

lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
if (lstrcpy(lpszCurrentVariable, "API=Win32") == NULL)
ErrorExit("lstrcpy failed");

/* Terminate the block with a NULL byte. */

lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
*lpszCurrentVariable = '\0';

/* Create the child process, specifying a new environment block. */

fSuccess = CreateProcess(NULL, "childenv", NULL, NULL, TRUE, 0,
(LPVOID) tchNewEnv, /* new environment block */
NULL, &siStartInfo, &piProcInfo);

if (! fSuccess)
ErrorExit("CreateProcess failed");

If you want the child process to inherit most of the parent's environment with only a few changes, save the current values, make changes for the child to inherit, and then restore the saved values for example.
LPTSTR lpszOldValue;
TCHAR tchBuf[BUFSIZE];
BOOL fSuccess;

/*
* lpszOldValue gets current value of "varname", or NULL if "varname"
* environment variable does not exist. Set "varname" to new value,
* create child process, then use SetEnvironmentVariable to restore
* original value of "varname". If lpszOldValue is NULL, the "varname"
* variable will be deleted.
*/

lpszOldValue = ((GetEnvironmentVariable("varname",
tchBuf, BUFSIZE) > 0) ? tchBuf : NULL);

/* Set a value for the child to inherit. */

if (! SetEnvironmentVariable("varname", "newvalue"))
ErrorExit("SetEnvironmentVariable failed");

/* Create a child process. */

fSuccess = CreateProcess(NULL, "childenv", NULL, NULL, TRUE, 0,
NULL, /* inherit parent's environment */
NULL, &siStartInfo, &piProcInfo);
if (! fSuccess)
ErrorExit("CreateProcess failed");

/* Restore the parent's environment. */

if (! SetEnvironmentVariable("varname", lpszOldValue))
ErrorExit("SetEnvironmentVariable failed");

The following example, taken from a console process, prints the contents of the process's environment block.
LPTSTR lpszVariable;
LPVOID lpvEnv;

/* Get a pointer to the environment block. */

lpvEnv = GetEnvironmentStrings();

/*
* Variable strings are separated by NULL byte, and the block is
* terminated by a NULL byte.
*/

for (lpszVariable = (LPTSTR) lpvEnv; *lpszVariable; lpszVariable++) {
while (*lpszVariable)
putchar(*lpszVariable++);
putchar('\n');
}

+$#A Using Thread Local Storage
Thread local storage (TLS) enables multiple threads of the same process to use an index allocated by the TlsAlloc_win32_TlsAlloc function to store and retrieve a value that is local to the thread. In this example, an index is allocated when the process starts. When each thread starts, it allocates a block of dynamic memory and stores a pointer to this memory by using the TLS index. The TLS index is used by the locally defined CommonFunc function to access the data local to the calling thread. Before each thread terminates, it releases its dynamic memory.
#include <stdio.h>
#include <windows.h>

#define THREADCOUNT 4
DWORD dwTlsIndex;

VOID ErrorExit(LPTSTR);

VOID CommonFunc(VOID) {
LPVOID lpvData;

/* Retrieve a data pointer for the current thread. */

lpvData = TlsGetValue(dwTlsIndex);
if ((lpvData == 0) && (GetLastError() != 0))
ErrorExit("TlsGetValue error");

/*
* Use the data stored for the current thread.
*/

printf("common: thread %d: lpvData=%lx\n",
GetCurrentThreadId(), lpvData);

Sleep(5000);
}

DWORD ThreadFunc(VOID) {
LPVOID lpvData;

/* Initialize the TLS index for this thread. */

lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (! TlsSetValue(dwTlsIndex, lpvData))
ErrorExit("TlsSetValue error");

printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);

CommonFunc();

/* Release the dynamic memory before the thread returns. */

lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
LocalFree((HLOCAL) lpvData);

return 0;

}

DWORD main(VOID) {
DWORD IDThread;
HANDLE hThread[THREADCOUNT];
int i;

/* Allocate a TLS index. */

if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF)
ErrorExit("TlsAlloc failed");

/* Create multiple threads. */

for (i = 0; i < THREADCOUNT; i++) {
hThread[i] = CreateThread(NULL,/* no security attributes */
0, /* use default stack size */
(LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */
NULL, /* no thread function argument */
0, /* use default creation flags */
&IDThread); /* returns thread identifier */

/* Check the return value for success. */

if (hThread[i] == NULL)
ErrorExit("CreateThread error\n");
}

for (i = 0; i < THREADCOUNT; i++)
WaitForSingleObject(hThread[i], INFINITE);

return 0;

}

VOID ErrorExit (LPTSTR lpszMessage) {
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}

+$#A Process and Thread Functions
Following are the functions used with processes and threads.
AttachThreadInput_win32_AttachThreadInputCommandLineToArgvW_win32_CommandLineToArgvWCreateProcess_win32_CreateProcessCreateRemoteThread_win32_CreateRemoteThreadCreateThread_win32_CreateThreadExitProcess_win32_ExitProcessExitThread_win32_ExitThreadFreeEnvironmentStrings_win32_FreeEnvironmentStringsGetCommandLine_win32_GetCommandLineGetCurrentProcess_win32_GetCurrentProcessGetCurrentProcessId_win32_GetCurrentProcessIdGetCurrentThread_win32_GetCurrentThreadGetCurrentThreadId_win32_GetCurrentThreadIdGetEnvironmentStrings_win32_GetEnvironmentStringsGetEnvironmentVariable_win32_GetEnvironmentVariableGetExitCodeProcess_win32_GetExitCodeProcessGetExitCodeThread_win32_GetExitCodeThreadGetPriorityClass_win32_GetPriorityClassGetProcessAffinityMask_win32_GetProcessAffinityMaskGetProcessShutdownParameters_win32_GetProcessShutdownParametersGetProcessTimes_win32_GetProcessTimesGetProcessVersion_win32_GetProcessVersionGetProcessWorkingSetSize_win32_GetProcessWorkingSetSizeGetStartupInfo_win32_GetStartupInfoGetThreadPriority_win32_GetThreadPriorityGetThreadTimes_win32_GetThreadTimesOpenProcess_win32_OpenProcessResumeThread_win32_ResumeThreadSetEnvironmentVariable_win32_SetEnvironmentVariableSetPriorityClass_win32_SetPriorityClassSetProcessShutdownParameters_win32_SetProcessShutdownParametersSetThreadAffinityMask_win32_SetThreadAffinityMaskSetThreadPriority_win32_SetThreadPrioritySleep_win32_SleepSleepEx_win32_SleepExSetProcessWorkingSetSize_win32_SetProcessWorkingSetSizeSuspendThread_win32_SuspendThreadTerminateProcess_win32_TerminateProcessTerminateThread_win32_TerminateThreadTlsAlloc_win32_TlsAllocTlsFree_win32_TlsFreeTlsGetValue_win32_TlsGetValueTlsSetValue_win32_TlsSetValueWaitForInputIdle_win32_WaitForInputIdleWinExec_win32_WinExec
Obsolete or Deleted Functions
Yield_win32_Yield

+$#AK Registry
The registry is a system-defined database that applications and Microsoft® Windows® system components use to store and retrieve configuration data.
+$#A About the Registry
The registry stores data in binary files. To manipulate registry data, an application must use the registry functions. This overview describes the registry and the functions that applications use to access and manipulate the data stored there. The data stored in the registry varies according to the Windows platform that is used. This overview contains a description of registry entries that can exist on any platform.
+$#A Structure of the Registry
The registry stores data in a hierarchically structured tree. Each node in the tree is called a key. Each key can contain both subkeys and data entries called values. Sometimes, the presence of a key is all the data that an application requires; other times, an application opens a key and uses the values associated with the key. A key can have any number of values, and the values can be in any form.
Each key has a name consisting of one or more printable ANSI characters ¾ that is, characters ranging from values 32 through 127. Key names cannot include a space, a backslash (\), or a wildcard character (* or ?). Key names beginning with a period (.) are reserved. The name of each subkey is unique with respect to the key that is immediately above it in the hierarchy. Key names are not localized into other languages, although values may be.
+$#A Registry Storage Space
Although there are few technical limits to the type and size of data an application can store in the registry, certain practical guidelines exist to promote system efficiency. An application should store configuration and initialization data in the registry, but other kinds of data should be stored elsewhere.
Generally, data consisting of more than one or two kilobytes (K) should be stored as a file and referred to by using a key in the registry rather than being stored as a value. Instead of duplicating large pieces of data in the registry, an application should save the data as a file and refer to the file. Executable binary code should never be stored in the registry.
A value entry uses much less registry space than a key. To save space, an application should group similar data together as a structure and store the structure as a value rather than storing each of the structure members as a separate key. (Storing the data in binary form allows an application to store data in one value that would otherwise be made up of several incompatible types.)
+$#A Predefined Keys
An application must open a key before it can add data to the registry. To open a key, an application must supply the handle of another key in the registry that is already open. The system defines standard handles that are always open. An application can use these predefined handles as entry points to the registry.
The system provides two predefined keys at the root of the registry: HKEY_LOCAL_MACHINE and HKEY_USERS. In addition, the system defines two subkeys: HKEY_CLASSES_ROOT (a subkey of HKEY_LOCAL_MACHINE) and HKEY_CURRENT_USER (a subkey of HKEY_USERS). These registry handles are valid for all Win32 implementations of the registry, although the use of the handles may vary from platform to platform.
Predefined keys help an application navigate in the registry and make it possible to develop tools that allow a system administrator to manipulate categories of data. Applications that add data to the registry should always work within the framework of predefined keys, so administrative tools can find and use the new data.
These predefined keys are used as entry points to the registry.
Entry point Use
HKEY_CLASSES_ROOT Registry entries subordinate to this key define types (or classes) of documents and the properties associated with those types. Data stored under this key is used by Windows shell applications and by object linking and embedding (OLE) applications.
HKEY_CURRENT_USER Registry entries subordinate to this key define the preferences of the current user. These preferences include the settings of environment variables, data about program groups, colors, printers, network connections, and application preferences.
HKEY_LOCAL_MACHINE Registry entries subordinate to this key define the physical state of the computer, including data about the bus type, system memory, and installed hardware and software.
HKEY_USERS Registry entries subordinate to this key define the default user configuration for new users on the local computer and the user configuration for the current user.

The use of HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and HKEY_USERS varies depending on the implementation of the registry. In addition, other predefined handles have been defined for specific Windows platforms.
+$#A Categories of Data
Before putting data into the registry, an application should divide the data into two categories: computer-specific data and user-specific data. By making this distinction, an application can support multiple users, and yet locate user-specific data over a network and use that data in different locations, allowing location-independent user profile data. (A user profile is a set of configuration data saved for every user.)
When the application is installed, it should record the computer-specific data under the HKEY_LOCAL_MACHINE key. In particular, it should create keys for the company name, product name, and version number, as shown in the following example:
HKEY_LOCAL_MACHINE\Software\MyCompany\MyProduct\1.0

If the application supports OLE, it should record that data under HKEY_LOCAL_MACHINE\Software\Classes.
An application should record user-specific data under the HKEY_CURRENT_USER key, as shown in the following example:
HKEY_CURRENT_USER\Software\MyCompany\MyProduct\1.0\...

+$#A Opening, Creating, and Closing Keys
Before an application can add data to the registry, it must create or open a key. To create or open a key, an application always refers to the key as a subkey of a currently open key. The four predefined keys (HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS, and HKEY_CURRENT_USER) are always open. An application uses the RegOpenKey_win32_RegOpenKey or RegOpenKeyEx_win32_RegOpenKeyEx function to open a key and the RegCreateKey_win32_RegCreateKey or RegCreateKeyEx_win32_RegCreateKeyEx function to create a key.
An application can use the RegCloseKey_win32_RegCloseKey function to close a key and write the data it contains into the registry. RegCloseKey does not necessarily write the data to the registry before returning; it can take as much as several seconds for the cache to be flushed to the hard disk. If an application must explicitly write registry data to the hard disk, it can use the RegFlushKey_win32_RegFlushKey function. RegFlushKey, however, uses many system resources and should be called only when absolutely necessary.
+$#A Writing and Deleting Registry Data
An application can use either the RegSetValue_win32_RegSetValue or RegSetValueEx_win32_RegSetValueEx function to associate a value and its data with a key. RegSetValue works only with strings (values having the REG_SZ type). RegSetValueEx, however, can write values with any type of data. Either of these functions can create a key and its value at the same time.
To delete a value from a key, an application can use the RegDeleteValue_win32_RegDeleteValue function. To delete a key, it can use the RegDeleteKey_win32_RegDeleteKey function. A deleted key is not removed until the last handle to it has been closed. Subkeys and values cannot be created under a deleted key.
To change a key's security information, an application can use the RegSetKeySecurity_win32_RegSetKeySecurity function.
+$#A Retrieving Data from the Registry
To retrieve data from the registry, an application typically enumerates the subkeys of a key until it finds a particular one and then retrieves data from the value or values associated with it. An application can call either the RegEnumKey_win32_RegEnumKey or RegEnumKeyEx_win32_RegEnumKeyEx function to enumerate the subkeys of a given key. RegEnumKeyEx returns a subkey and its class, but RegEnumKey returns only the subkey, not the class.
To retrieve detailed data about a particular subkey, an application can call the RegQueryInfoKey_win32_RegQueryInfoKey function. The RegGetKeySecurity_win32_RegGetKeySecurity function retrieves a copy of the security descriptor protecting a key.
An application can use the RegEnumValue_win32_RegEnumValue function to enumerate the values for a given key, and the RegQueryValue_win32_RegQueryValue or RegQueryValueEx_win32_RegQueryValueEx function to retrieve a particular value for a key. An application typically calls RegEnumValue to determine the value names and then RegQueryValueEx to retrieve the data for the names.
RegQueryValue and RegQueryValueEx differ in how they treat unexpanded references to environment variables. If an unnamed value contains an unexpanded environment variable (for example, %PATH%), RegQueryValue expands the variable into the storage buffer provided as one of its parameters. RegQueryValueEx, however, does not expand these references. (Applications can also use the ExpandEnvironmentStrings_win32_ExpandEnvironmentStrings function to expand environment variables.)
+$#A Registry Files
Applications can save part of the registry in a file and then load the contents of the file back into the registry. A registry file is useful when a large amount of data is being manipulated, when many entries are being made in the registry, or when the data is transitory and must be loaded and then unloaded again. Applications that back up and restore parts of the registry are likely to use registry files.
To save a key and its subkeys and values to a registry file, an application can call the RegSaveKey_win32_RegSaveKey function. To write the registry file back to the registry, an application can use the RegLoadKey_win32_RegLoadKey, RegReplaceKey_win32_RegReplaceKey, or RegRestoreKey_win32_RegRestoreKey function.
RegLoadKey loads registry data from a specified file into a specified subkey under HKEY_USERS or HKEY_LOCAL_MACHINE on the calling application's computer or on a remote computer. The function creates the specified subkey if it does not already exist. After calling this function, an application can use the RegUnLoadKey_win32_RegUnLoadKey function to restore the registry to its previous state.
RegReplaceKey replaces a key and all its subkeys and values in the registry with the data contained in a specified file. The new data takes effect the next time the system is started.
RegRestoreKey loads registry data from a specified file into a specified key on the calling application's computer or on a remote computer. This function replaces the subkeys and values below the specified key with the subkeys and values that follow the top-level key in the file.
+$#A Using the Registry
The following example demonstrates the use of the RegQueryInfoKey_win32_RegQueryInfoKey, RegEnumKey_win32_RegEnumKey, and RegEnumValue_win32_RegEnumValue functions. The hKey parameter passed to each function is the handle of an open key. This key must be opened before the function call and closed afterward.
// QueryKey - enumerates the subkeys of a given key and the associated
// values and then copies the information about the keys and values
// into a pair of edit controls and list boxes.
// hDlg - dialog box that contains the edit controls and list boxes
// hKey - key whose subkeys and values are to be enumerated

VOID QueryKey(HWND hDlg, HANDLE hKey)
{
CHAR achKey[MAX_PATH];
CHAR achClass[MAX_PATH] = ""; /* buffer for class name */
DWORD cchClassName = MAX_PATH; /* length of class string */
DWORD cSubKeys; /* number of subkeys */
DWORD cbMaxSubKey; /* longest subkey size */
DWORD cchMaxClass; /* longest class string */
DWORD cValues; /* number of values for key */
DWORD cchMaxValue; /* longest value name */
DWORD cbMaxValueData; /* longest value data */
DWORD cbSecurityDescriptor; /* size of security descriptor */
FILETIME ftLastWriteTime; /* last write time */

DWORD i, j;
DWORD retCode, retValue;

CHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
CHAR achBuff[80];

// Get the class name and the value count.
RegQueryInfoKey(hKey, /* key handle */
achClass, /* buffer for class name */
&cchClassName, /* length of class string */
NULL, /* reserved */
&cSubKeys, /* number of subkeys */
&cbMaxSubKey, /* longest subkey size */
&cchMaxClass, /* longest class string */
&cValues, /* number of values for this key */
&cchMaxValue, /* longest value name */
&cbMaxValueData, /* longest value data */
&cbSecurityDescriptor, /* security descriptor */
&ftLastWriteTime); /* last write time */

SetDlgItemText(hDlg, IDE_CLASS, achClass);
SetDlgItemInt(hDlg, IDE_CVALUES, cValues, FALSE);

SendMessage(GetDlgItem(hDlg, IDL_LISTBOX),
LB_ADDSTRING, 0, (LONG) "..");

// Enumerate the child keys, looping until RegEnumKey fails. Then
// get the name of each child key and copy it into the list box.
SetCursor(LoadCursor(NULL, IDC_WAIT));
for (i = 0, retCode = ERROR_SUCCESS;
retCode == ERROR_SUCCESS; i++) {
retCode = RegEnumKey(hKey, i, achKey, MAX_PATH);
if (retCode == (DWORD)ERROR_SUCCESS)
SendMessage(GetDlgItem(hDlg, IDL_LISTBOX),
LB_ADDSTRING, 0, (LONG) achKey);
}
SetCursor(LoadCursor (NULL, IDC_ARROW));

// Enumerate the key values.
SetCursor(LoadCursor(NULL, IDC_WAIT));

if (cValues)
for (j = 0, retValue = ERROR_SUCCESS;
j < cValues; j++) {
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
retValue = RegEnumValue(hKey, j, achValue,
&cchValue,
NULL,
NULL, /* &dwType, */
NULL, /* &bData, */
NULL); /* &bcData */

if (retValue != (DWORD) ERROR_SUCCESS &&
retValue != ERROR_INSUFFICIENT_BUFFER) {
wsprintf (achBuff,
"Line:%d 0 based index = %d, retValue = %d, "
"ValueLen = %d",
__LINE__, j, retValue, cchValue);
MessageBox (hDlg, achBuff, "Debug", MB_OK);
}

achBuff[0] = '\0';

// Add each value to a list box.
if (!lstrlen(achValue))
lstrcpy(achValue, "<NO NAME>");
wsprintf(achBuff, "%d) %s ", j, achValue);
SendMessage(GetDlgItem(hDlg,IDL_LISTBOX2),
LB_ADDSTRING, 0, (LONG) achBuff);
}

SetCursor(LoadCursor(NULL, IDC_ARROW));

}

+$#A Registry Functions
Following are the functions used with the registry:
RegCloseKey_win32_RegCloseKeyRegConnectRegistry_win32_RegConnectRegistryRegCreateKey_win32_RegCreateKeyRegCreateKeyEx_win32_RegCreateKeyExRegDeleteKey_win32_RegDeleteKeyRegDeleteValue_win32_RegDeleteValueRegEnumKey_win32_RegEnumKeyRegEnumKeyEx_win32_RegEnumKeyExRegEnumValue_win32_RegEnumValueRegFlushKey_win32_RegFlushKeyRegGetKeySecurity_win32_RegGetKeySecurityRegLoadKey_win32_RegLoadKeyRegNotifyChangeKeyValue_win32_RegNotifyChangeKeyValueRegOpenKey_win32_RegOpenKeyRegOpenKeyEx_win32_RegOpenKeyExRegQueryInfoKey_win32_RegQueryInfoKeyRegQueryValue_win32_RegQueryValueRegQueryValueEx_win32_RegQueryValueExRegReplaceKey_win32_RegReplaceKeyRegRestoreKey_win32_RegRestoreKeyRegSaveKey_win32_RegSaveKeyRegSetKeySecurity_win32_RegSetKeySecurityRegSetValue_win32_RegSetValueRegSetValueEx_win32_RegSetValueExRegUnLoadKey_win32_RegUnLoadKey

+$#AK Remote Access Service
Remote Access Service (RAS) lets users at remote locations work as if connected directly to a computer network, accessing one or more RAS servers.
+$#A About Remote Access Service
Win32 RAS support enables a RAS client application to start a RAS connection operation, get information about existing RAS connections, and end a RAS connection.
Microsoft® Win32® application programming interface (API) does not currently provide support for applications to administer the Remote Access Server.
This overview discusses the following topics:
· Types of RAS connection operations_win32_RAS_Connection_Operations
· Phonebook files_win32_Phonebook_Files_and_Connection_Information and the information needed to establish a connection
· Connection states_win32_Connection_States during a connection operation
· Notification handlers_win32_Notification_Handlers for tracking the progress of a connection operation
· Paused states_win32_Paused_States during a connection operation that enable the RAS client to collect additional information from the user
· Support for connection operations that require a callback_win32_Callback_Connections from the remote server
· Ending a RAS connection_win32_Disconnecting

+$#A RAS Connection Operations
A RAS client application uses the RasDial_win32_RasDial function to establish a connection to a RAS server. The RasDial function starts the connection operation, which is then carried out by the Remote Access Connection Manager.
The Remote Access Connection Manager is a service that handles the details of establishing the connection to the remote server. This service also provides the client with status information during the connection operation. The Remote Access Connection Manager starts automatically when an application loads the RASAPI32.DLL.
The RasDial_win32_RasDial call specifies the following information when it starts a connection operation:
· The connection information_win32_Phonebook_Files_and_Connection_Information that the Remote Access Connection Manager needs to establish the connection.
· An optional notification handler_win32_Notification_Handlers that receives progress notifications during the connection operation. If the RasDial_win32_RasDial call specifies a notification handler, the call is asynchronous_win32_Asynchronous_Operations; otherwise, it is synchronous_win32_Synchronous_Operations.
· An optional RASDIALEXTENSIONS_win32_RASDIALEXTENSIONS_str structure to enable or disable extensions to the RasDial operation. The extensions permit a RAS client to directly enable some modem settings, to control whether RAS uses the prefixes and suffixes in a phonebook entry, and to support paused states_win32_Paused_States during the connection operation.

+$#A Synchronous Operations
When RasDial_win32_RasDial is invoked as a synchronous operation, the function does not return until the connection has been established or an error occurs. Synchronous mode provides a simple way for a RAS client to establish a connection. The client can simply call RasDial, wait for the function to return, and then call the RasGetConnectStatus_win32_RasGetConnectStatus function to determine whether the connection operation was successful. Once the connection has been established, the client application can terminate without breaking the connection. If an error occurs, the client application must shut down the connection operation_win32_Disconnecting before terminating.
The disadvantage of synchronous mode is that the client does not receive progress notifications as the connection operation proceeds. As a workaround for this lack of progress notifications, a synchronous mode client can use a separate thread that calls RasGetConnectStatus to poll for and display the current state. However, for RAS clients that want to receive progress information, the preferred technique is to invoke RasDial asynchronously.
+$#A Asynchronous Operations
When RasDial_win32_RasDial is invoked as an asynchronous operation, the function returns immediately. In asynchronous mode, the RasDial call must specify a notification handler_win32_Notification_Handlers that the Remote Access Connection Manager uses to inform the client whenever the connection operation changes states or an error occurs.
The notification handler can be a window to receive messages, or a RasDialFunc_win32_RasDialFunc or RasDialFunc1_win32_RasDialFunc1 callback function. The Remote Access Connection Manager makes its asynchronous notifications in the context of the thread that made the RasDial call. For this reason, the calling thread must not terminate until the connection operation has been successfully established or an error occurs. As in synchronous mode, the client application can safely terminate once the connection has been established, and it must shut down the connection operation_win32_Disconnecting if an error occurs.
+$#A Phonebook Files and Connection Information
A RasDial_win32_RasDial call must specify the information that the Remote Access Connection Manager needs to establish the connection. Typically, the RasDial call provides the connection information by specifying a phonebook entry in a phonebook file_win32_Phonebook_Files. The connection information in a phonebook entry includes phone numbers, bps rates, user authentication information_win32_User_Authentication_Information, and other connection information_win32_Other_Connection_Information.
A RAS client uses the parameters of the RasDial function to specify a phonebook file and an entry in that file. The lpszPhonebookPath parameter can specify the name of a phonebook file, or it can be NULL to indicate that the default phonebook file should be used. The lpRasDialParams parameter points to a RASDIALPARAMS_win32_RASDIALPARAMS_str structure that specifies the name of the phonebook entry to use.
To display a list of phonebook entries from which the user can select a connection, a RAS client can call the RasEnumEntries_win32_RasEnumEntries function to enumerate the entries in a phonebook file.
To make a connection without using a phonebook entry, the RasDial call can specify an empty string for the szEntryName member of the RASDIALPARAMS structure. The RASDIALPARAMS.szPhoneNumber member must contain the number to call. In this case, the Remote Access Connection Manager uses the first available modem port and default values for all other settings.
+$#A Phonebook Files
Phonebook files provide a standard way to collect and specify the information that the Remote Access Connection Manager needs to establish a remote connection. A phonebook file is a text file that associates entry names with information such as phone numbers, COM ports, and modem settings. Each phonebook entry contains the information needed to establish a RAS connection.
The Win32 API does not currently provide support for creating a phonebook file.
Windows 95: Dial-up networking stores phonebook entries in the registry rather than in a phonebook file. A user or administrator can use the Dial-Up Networking folder to create and edit phonebook entries. You can use the RasCreatePhonebookEntry_win32_RasCreatePhonebookEntry and RasEditPhonebookEntry_win32_RasEditPhonebookEntry functions to create or edit a phonebook entry. These functions display a dialog box in which the user can specify information about the phonebook entry. You can use the RasGetEntryDialParams_win32_RasGetEntryDialParams and RasSetEntryDialParams_win32_RasSetEntryDialParams functions to set or retrieve the connection parameters for a phonebook entry.
Windows NT: The functions for creating and editing a phonebook entry are not supported. Microsoft's remote access application, RASPHONE.EXE, creates a default phonebook file called RASPHONE.PBK, and can also create personal phonebook files. A user or administrator can use RASPHONE.EXE to create and edit entries in a phonebook file. Using RASPHONE.EXE is currently the only safe way to create a phonebook file or to create or edit a phonebook entry.
+$#A User Authentication Information
The Remote Access Connection Manager service on the client computer sends a user name and password to the RAS server on the remote computer. Before it will establish a connection, the remote server uses this information to authenticate the user. By default, the Remote Access Connection Manager sends the user name and password of the currently logged-on user. The RAS client can use the RASDIALPARAMS_win32_RASDIALPARAMS_str structure specified in the RasDial_win32_RasDial call to specify a different user name and password.
If the remote server cannot authenticate the user with the specified information, it can allow the connection operation to enter a paused state_win32_Paused_States to enable the RAS client to collect different authentication data from the user.
+$#A Other Connection Information
The members of the RASDIALPARAMS_win32_RASDIALPARAMS_str structure can also specify the following connection information:
· A phone number to override the number in the phonebook entry.
· A callback_win32_Callback_Connections phone number that the remote server can call back to establish the connection.
· The name of the remote network domain on which the authentication is to occur.
For the callback number and the domain, the RASDIALPARAMS members can either indicate that RAS should use the information in the phonebook entry, or it can provide information that overrides the phonebook data.
A RAS client can use the lpRasDialExtensions parameter of the RasDial_win32_RasDial function to control whether RAS uses a phone number prefix or suffix specified in a phonebook entry.
+$#A Connection States
During the process of connecting to a remote server, the Remote Access Connection Manager and the RAS server on the remote computer perform several steps to establish the connection. Each of these steps is identified by a connection state. The RASCONNSTATE_win32_RASCONNSTATE_str enumeration is a set of values that correspond to these connection states. The connection states can be divided into the following three groups:
Running states The running states are the parts of the connection operation that RAS handles automatically, such as connecting to the necessary devices, authenticating the user, and waiting for a callback from the remote server. Unless an error occurs, the RAS client need take no action other than to pass the notification on to the user.
Paused states The paused states_win32_Paused_States occur when the remote server pauses the connection operation to get additional input from the user. During a paused state, the user can type a callback_win32_Callback_Connections number, a different user name and password if the user authentication fails, or a new password if the old one has expired.
Terminal states The terminal states occur when the connection has been successfully established, the connection operation has failed, or the connection has been broken by a RasHangUp call.

There are several mechanisms that a RAS client can use to determine the current state of a connection operation. When a RAS client executes the RasDial_win32_RasDial function asynchronously, the Remote Access Connection Manager sends progress notifications to the client's notification handler_win32_Notification_Handlers whenever the connection state changes. In addition, the client can use the RasGetConnectStatus_win32_RasGetConnectStatus function to get the current state of any RAS connection operation.
+$#A Notification Handlers
An asynchronous RasDial_win32_RasDial call must specify a notification handler. During an asynchronous connection operation, the Remote Access Connection Manager uses the notification handler to inform the RAS client whenever the connection state_win32_Connection_States changes or an error occurs.
The actions performed by a notification handler can be divided into the following categories:
· Handling errors_win32_Handling_RAS_Errors.
· Providing feedback to the user as the connection operation proceeds through the various connection states. See Informational Notifications_win32_Informational_Notifications.
· Handling paused states_win32_Paused_States.
· Signaling the RAS client application when the connection operation has been completed. See Completion Notifications_win32_Completion_Notifications.
There are three types of notification handlers, each of which receives the same basic information: the current connection state and an error code that is nonzero only if an error has occurred.
RasDialFunc_win32_RasDialFunc A callback function prototype that receives only the current connection state and error code information.
RasDialFunc1_win32_RasDialFunc1 A callback function prototype that receives the HRASCONN connection handle and extended error information in addition to the basic information. The connection handle parameter makes RasDialFunc1 useful for client applications that support multiple simultaneous connection operations. This allows the client to specify the same callback function for all operations, and enables the callback function to determine which connection is changing states.
Window handle A window handle to which RAS sends WM_RASDIALEVENT_win32_WM_RASDIALEVENT messages containing the current connection state and error code information. Use this method if your source code must be compatible with 16-bit Windows, because 16-bit Windows does not support either of the callback functions.

The Remote Access Connection Manager suspends the connection operation until the notification handler returns. For this reason, the handler should return as soon as possible unless an error has occurred.
The RasDial_win32_RasDial function should not be called from within a notification handler. The other remote access functions (RasGetConnectStatus_win32_RasGetConnectStatus, RasEnumEntries_win32_RasEnumEntries, RasEnumConnections_win32_RasEnumConnections, RasGetErrorString_win32_RasGetErrorString, and RasHangUp_win32_RasHangUp) can be called from within a handler.
+$#A Handling RAS Errors
When an error occurs, the Remote Access Connection Manager invokes the client's notification handler. The notification indicates the connection state when the error occurred, and a code that identifies the error. In these cases, the notification handler should call RasHangUp_win32_RasHangUp to end the RAS connection.
The RAS client can use the RasGetErrorString_win32_RasGetErrorString function to get a display string describing the error.
+$#A Informational Notifications
For the connection states_win32_Connection_States known as running states, no action is required of the notification handler unless an error occurs. Running states occur during the parts of the connection operation that RAS handles automatically, such as connecting to the necessary devices, authenticating the user, and waiting for a callback from the remote server. The notification is simply a progress report to the client.
The client can choose to pass these informational notifications on to the user. In some running states, the client may want to display additional information. For example, a notification handler that receives a RASCS_ConnectDevice notification can call the RasGetConnectStatus_win32_RasGetConnectStatus function to get the name and type of the device being connected to. Another example is when the client receives a RASCS_Projected notification. This occurs when the RAS projection phase of the connection operation has been completed. The client can call the RasGetProjectionInfo_win32_RasGetProjectionInfo function to get additional information about the projection. The client can use this information to notify the user as to which network protocols can be used by this connection.
You should avoid writing code that depends on the order or occurrence of particular informational states, because this may vary between platforms.
+$#A Completion Notifications
The Remote Access Connection Manager continues progress notifications until the connection operation has been completed. This occurs in the following situations:
· The connection is established. The handler receives a RASCS_Connected notification. The RAS client application can exit without breaking the connection.
· An error occurs. The handler receives a notification indicating the error and the connection state when the error occurred.
· The connection operation is interrupted by a RasHangUp_win32_RasHangUp call.
+$#A Paused States
During a connection operation, there can be times when the remote server cannot proceed without additional information from the local user. Beginning with Microsoft® Windows NT™ version 3.5, the RasDial_win32_RasDial function supports paused states. A paused state allows the Remote Access Connection Manager to suspend a connection operation so the RAS client application can collect information from the user.
Paused states are useful in the following situations:
· When the user needs to provide a callback_win32_Callback_Connections number.
· When the user authentication fails, the user can type in a different user name and password.
· When the user's password has expired, the user can provide a new password.
By default, paused state support is disabled. RAS clients that want to support paused states must set the RDEOPTS_PausedStates flag in the RASDIALEXTENSIONS_win32_RASDIALEXTENSIONS_str structure passed as a parameter to RasDial.
When a paused state occurs, the Remote Access Connection Manager invokes the client's notification handler. If paused state support is disabled, the notification message indicates an error, and the connection operation fails. If it is enabled, the Connection Manager pauses the connection operation to wait for the RAS client's response. The RAS client can resume the connection operation by a second RasDial call, or terminate it by calling the RasHangUp_win32_RasHangUp function.
After getting the user's input, the RAS client restarts the connection operation by calling RasDial again. This second RasDial call must specify the following information:
· The connection handle that was returned by the original RasDial call.
· The same notification handler as the original RasDial call.
· The user's input in the appropriate members of the RASDIALPARAMS_win32_RASDIALPARAMS_str structure. Other members of the RASDIALPARAMS structure should have the same information as specified in the original RasDial call.
The second RasDial call cannot be made from within the notification handler.
+$#A Callback Connections
RAS supports connections in which the remote server hangs up and then calls back to the client to establish the connection.
For each user that can connect to a RAS server, the server stores a callback attribute that controls how the connection is made. The default attribute is No Callback, which means that the user can connect to the RAS server without a callback. Alternatively, the administrator of the RAS server can assign to a user either the Preset_win32_Preset or Set By Caller_win32_Set_by_Caller callback attribute.
+$#A Preset
For a user assigned the Preset restriction, the administrator specifies a phone number that the RAS server must call back to establish a connection. The user cannot specify a different number, and the connection cannot be made without a callback.
A Preset callback operation is handled automatically by the Remote Access Connection Manager and the remote server. The RAS client application does not need to do anything other than provide feedback to the user when the notification handler is called during the various states of the callback operation.
+$#A Set by Caller
A user assigned the Set By Caller privilege can choose to connect either with or without a callback. The RasDial_win32_RasDial call uses the szCallBackNumber member of the RASDIALPARAMS_win32_RASDIALPARAMS_str structure to indicate the choice.
The szCallBackNumber member can simply specify the callback number; or, to establish the connection without a callback, szCallBackNumber can point to an empty string, "". In either of these cases, the Remote Access Connection Manager handles the connection operation automatically. As with a Preset callback operation, the RAS client does not need to perform any action other than to provide feedback to the user.
If the RasDial call enables paused states_win32_Paused_States, szCallBackNumber can point to an asterisk string, "*", to indicate that the connection operation should enter a paused state to allow the user to type in the callback number. In this case, the connection operation for a Set By Caller user enters a paused state after the remote server has authenticated the user. During the paused state, the RAS client gets the callback number input from the user. The client then resumes the connection operation by making a second RasDial call in which szCallBackNumber specifies the number supplied by the user.
Note that if paused states are not enabled there is a different meaning when szCallBackNumber points to an asterisk string, "*". In this case, the asterisk indicates that the callback number is stored in the phonebook file specified by the RasDial call.
+$#A Disconnecting
When a RAS client application starts a connection operation, the RasDial_win32_RasDial call receives an HRASCONN connection handle to identify the connection. If the returned handle is not NULL, the client must eventually call the RasHangUp_win32_RasHangUp function to end the connection. If an error occurs during the connection operation, the client must call RasHangUp even though the connection was never established.
The application that calls RasHangUp should not exit immediately, because the Remote Access Connection Manager needs time to properly terminate the connection. Instead, the application should wait until the RasGetConnectStatus_win32_RasGetConnectStatus function returns ERROR_INVALID_HANDLE, indicating that the connection has been deleted.
A RAS client application might need to end a connection even though it does not have the handle returned by RasDial. For example, the application that called RasDial might have exited once the connection was successfully established. In this case, the disconnecting application can use the RasEnumConnections_win32_RasEnumConnections function to get all the current connections. For each connection, RasEnumConnections returns a RASCONN_win32_RASCONN_str structure containing the HRASCONN connection handle and the phonebook entry name or phone number specified when the connection operation was started. This information can be used to display a list of connections from which the user can select the connection to end.
+$#A Using Remote Access Service
The following topic explains how to use Remote Access Service features in an application.
· Linking to the remote access DLL_win32_Linking_to_the_Remote_Access_DLL

+$#A Linking to the Remote Access DLL
If an application links statically to the RASAPI32 DLL, the application will fail to load if Remote Access Service is not installed. A RAS application can load when RAS is not installed by using LoadLibrary_win32_LoadLibrary to load the DLL, and GetProcAddress_win32_GetProcAddress to obtain pointers to the RAS functions.
The Win32 RAS functions are in RASAPI32.DLL. The import library for these functions is RASAPI32.LIB. To use the RAS functions, your programs must include the following files:
File Description
RAS.H Contains the RAS function prototypes, constants, and structure definitions.
RASERROR.H Contains the RAS error codes.
+$#AK Remote Access Service Reference
The following functions, strucures, messages, and enumeration types are associated with remote access service.
+$#A Remote Access Service Functions
Use these functions to implement Remote Access Service functionality:
RasCreatePhonebookEntry_win32_RasCreatePhonebookEntryRasDial_win32_RasDialRasDialFunc_win32_RasDialFuncRasDialFunc1_win32_RasDialFunc1RasEditPhonebookEntry_win32_RasEditPhonebookEntryRasEnumConnections_win32_RasEnumConnectionsRasEnumEntries_win32_RasEnumEntriesRasGetConnectStatus_win32_RasGetConnectStatusRasGetEntryDialParams_win32_RasGetEntryDialParamsRasGetErrorString_win32_RasGetErrorStringRasGetProjectionInfo_win32_RasGetProjectionInfo RasHangUp_win32_RasHangUpRasSetEntryDialParams_win32_RasSetEntryDialParams
+$#AK Remote Access Service Structures
Use these structures to implement Remote Access Service functionality:
RASAMB_win32_RASAMB_strRASCONN_win32_RASCONN_strRASCONNSTATUS_win32_RASCONNSTATUS_strRASDIALEXTENSIONS_win32_RASDIALEXTENSIONS_strRASDIALPARAMS_win32_RASDIALPARAMS_strRASENTRYNAME_win32_RASENTRYNAME_strRASPPPIP_win32_RASPPPIP_strRASPPPIPX_win32_RASPPPIPX_strRASPPPNBF_win32_RASPPPNBF_str
+$#A Remote Access Service Messages
Use this message to implement Remote Access Service functionality:
WM_RASDIALEVENT_win32_WM_RASDIALEVENT
+$#A Remote Access Service Enumeration Types
Use these enumeration types to implement Remote Access Service functionality:
RASCONNSTATE_win32_RASCONNSTATE_strRASPROJECTION_win32_RASPROJECTION_str

+$#AK Resources
A resource is binary data that a resource compiler or developer adds to an application's executable file. A resource can be either standard or defined. The data in a standard resource describes an icon, cursor, menu, dialog box, bitmap, enhanced metafile, font, accelerator table, message-table entry, string-table entry, or version. An application-defined resource, also called a custom resource, contains any data required by a specific application.
+$#A About Resources
This overview describes the functions that enable applications to find a resource in a module; load a resource into memory; add, delete or replace a resource in an executable file; and generate a list of the resources contained in a module. For specific information about the organization of resource data within executable files, refer to the resource formats documentation.
For information about how to create standard resources, refer to the following table.
Resource Topic
Accelerator table Keyboard Accelerators_win32_Keyboard_Accelerators
Bitmap Bitmaps_win32_Bitmaps
Cursor Cursors_win32_Cursors
Dialog box Dialog Boxes_win32_Dialog_Boxes
Enhanced metafile Metafiles_win32_Metafiles
Font Fonts and Text_win32_Fonts_and_Text
Icon Icons_win32_Icons
Menu Menus (Windows NT)_win32_Menus_Windows_NT_
Message-table entry Your message-compiler documentation
String-table entry String Manipulation_win32_String_Manipulation
Version information File Installation Library_win32_File_Installation_Library

For information about how to include resource data in a Microsoft® Windows® executable file, refer to the documentation for your resource compiler.
+$#A Finding and Loading Resources
Before using a resource, an application must load it into memory. The FindResource_win32_FindResource and FindResourceEx_win32_FindResourceEx functions find a resource in a module and return a handle to the binary resource data. FindResource locates a resource by type and name. FindResourceEx locates the resource by type, name, and language. Information about FindResource in this topic also applies to FindResourceEx.
The LoadResource_win32_LoadResource function uses the resource handle returned by FindResource to load the resource into memory. After an application loads a resource by using LoadResource, Windows automatically unloads and reloads the resource as memory conditions and application execution require. Thus, an application need not explicitly unload a resource it no longer needs.
An application can use FindResource and LoadResource to find and load any type of resource, but these functions should be used only if the application must access the binary resource data for subsequent function calls. To use a resource immediately, an application should use one of the following resource-specific functions to find and load resources in one call.
Function Action
FormatMessage_win32_FormatMessage Loads and formats a message-table entry.
LoadAccelerators_win32_LoadAccelerators Loads an accelerator table.
LoadBitmap_win32_LoadBitmap Loads a bitmap resource.
LoadCursor_win32_LoadCursor Loads a cursor resource.
LoadIcon_win32_LoadIcon Loads an icon resource.
LoadImage_win32_LoadImage Loads an icon, cursor, bitmap, or enhanced metafile resource.
LoadMenu_win32_LoadMenu Loads a menu resource.
LoadString_win32_LoadString Loads a string-table entry.

Before terminating, an application should release the memory occupied by accelerator tables, bitmaps, cursors, icons, and menus by using one of the functions in the following table.
Resource Release function
Accelerator table DestroyAcceleratorTable_win32_DestroyAcceleratorTable
Bitmap DeleteObject_win32_DeleteObject
Cursor DestroyCursor_win32_DestroyCursor
Icon DestroyIcon_win32_DestroyIcon
Menu DestroyMenu_win32_DestroyMenu

When the application terminates, Windows automatically releases the memory occupied by the other types of resources.
+$#A Adding, Deleting, and Replacing Resources
Applications must frequently add, delete, or replace resources in executable files. Two methods can be used to accomplish these tasks. The first is to edit the resource-definition file, recompile the resources, and add the recompiled resources to the application's executable file. The second method is to copy the resource data directly into the application's executable file.
For example, to localize an English-language application for use in Norway, it may be necessary to replace the English dialog box with one using Norwegian. A developer creates an appropriate dialog box by using a dialog box editor or by writing a template in the resource-definition file. The developer then recompiles the resources and adds the new resources to the application's executable file.
If an appropriate dialog box exists in binary form, however, the developer can copy the data directly to the executable file being localized by using three Windows functions. The BeginUpdateResource_win32_BeginUpdateResource function creates an update handle for the executable file whose resources are to be changed. The UpdateResource_win32_UpdateResource function uses this handle to add, delete, or replace a resource in the executable file. The EndUpdateResource_win32_EndUpdateResource function closes the handle.
After an update handle to an executable file is created by BeginUpdateResource, an application can use UpdateResource repeatedly to make changes to the resource data. Each call to UpdateResource contributes to an internal list of additions, deletions, and replacements but does not actually write the data to the executable file. Immediately before closing the update handle, EndUpdateResource writes the accumulated changes to the executable file.
Sometimes, an application must copy resources or find resource sizes. The LoadLibrary_win32_LoadLibrary function provides a module handle to an executable file whose resources are to be copied, and the LockResource_win32_LockResource function provides a pointer to the resource data in the specified module. The SizeofResource_win32_SizeofResource function returns the size, in bytes, of a specified resource.
+$#A Enumerating Resources
Three Windows functions enable an application to obtain lists of resource types, names, and languages in a specified module. The EnumResourceTypes_win32_EnumResourceTypes function provides a list of resource types found in the module, the EnumResourceNames_win32_EnumResourceNames function provides the name of each resource within a given type, and the EnumResourceLanguages_win32_EnumResourceLanguages function provides the language of each resource of a given name and type. These functions and their associated callback functions enable applications to create a list of all resources in a module. This process is described in Creating a Resource List_win32_Creating_a_Resource_List.
+$#A Using Resources
· Updating resources_win32_Updating_Resources
· Creating a resource list_win32_Creating_a_Resource_List

+$#A Updating Resources
The following example copies a dialog box resource from one executable file, HAND.EXE, to another, FOOT.EXE, by following these steps:
1. The LoadLibrary_win32_LoadLibrary function loads the executable file HAND.EXE.
2. The FindResource_win32_FindResource and LoadResource_win32_LoadResource functions locate and load the dialog box resource named If from the file.
3. The LockResource_win32_LockResource function returns a pointer to the dialog box resource data.
4. The BeginUpdateResource_win32_BeginUpdateResource function opens an update handle FOOT.EXE.
5. The UpdateResource_win32_UpdateResource function copies the dialog box resource from HAND.EXE to FOOT.EXE.
6. The EndUpdateResource_win32_EndUpdateResource function completes the update.

The following code implements these steps.
HRSRC hResLoad; // handle to loaded resource
HANDLE hExe; // handle to existing .EXE file
HRSRC hRes; // handle/ptr. to res. info. in hExe
HANDLE hUpdateRes; // update resource handle
char *lpResLock; // pointer to resource data
BOOL result;

// Load the .EXE file that contains the dialog box you want to copy.
hExe = LoadLibrary("hand.exe");

if (hExe == NULL) {
ErrorHandler("Could not load exe.");
}

// Locate the dialog box resource in the .EXE file.
hRes = FindResource(hExe, "AboutBox", RT_DIALOG);

if (hRes == NULL) {
ErrorHandler("Could not locate dialog box.");
}

// Load the dialog box into global memory.
hResLoad = LoadResource(hExe, hRes);

if (hResLoad == NULL) {
ErrorHandler("Could not load dialog box.");
}

// Lock the dialog box into global memory.
lpResLock = LockResource(hRes);

if (lpResLock == NULL) {
ErrorHandler("Could not lock dialog box.");
}

// Open the file to which you want to add the dialog box resource.
hUpdateRes = BeginUpdateResource("foot.exe", FALSE);

if (hUpdateRes == NULL) {
ErrorHandler("Could not open file for writing.");
}

// Add the dialog box resource to the update list.
result = UpdateResource(hUpdateRes, // update resource handle
RT_DIALOG, // change dialog box resource
"AboutBox", // dialog box name
MAKELANGID(LANG_NEUTRAL,
SUBLANG_NEUTRAL), // neutral language ID
lpResLock, // ptr to resource info
SizeofResource(hExe, hRes)); // size of resource info.

if (result == FALSE) {
ErrorHandler("Could not add resource.");
}

// Write changes to FOOT.EXE and then close it.
if (!EndUpdateResource(hUpdateRes, FALSE)) {
ErrorHandler("Could not write changes to file.");
}

// Clean up.
if (!FreeLibrary(hExe)) {
ErrorHandler("Could not free executable.");
}

+$#A Creating a Resource List
The following example creates a list of every resource in the HAND.EXE file. The list is written to the RESINFO.TXT file.
The code demonstrates how to load the executable file, create a file in which to write resource information, and call the EnumResourceTypes_win32_EnumResourceTypes function to send each resource type found in the module to the application-defined callback function EnumTypesFunc. See EnumResTypeProc_win32_EnumResTypeProc for information on callback functions of this type. This callback function uses the EnumResourceNames_win32_EnumResourceNames function to pass the name of every resource within the specified type to another application-defined callback function, EnumNamesFunc. See EnumResNameProc_win32_EnumResNameProc for information on callback functions of this type. EnumNamesFunc uses the EnumResourceLanguages_win32_EnumResourceLanguages function to pass the language of every resource of the specified type and name to a third callback function, EnumLangsFunc. See EnumResLangProc_win32_EnumResLangProc for information on callback functions of this type. EnumLangsFunc writes information about the resource of the specified type, name, and language to the RESINFO.TXT file.
char szBuffer[80]; // print buffer for EnumResourceTypes
DWORD cbWritten; // number of bytes written to res. info. file
int cbString; // length of string in sprintf

// Declare callback functions.
BOOL EnumTypesFunc(
HANDLE hModule,
LPTSTR lpType,
LONG lParam);

BOOL EnumNamesFunc(
HANDLE hModule,
LPCTSTR lpType,
LPTSTR lpName,
LONG lParam);

BOOL EnumLangsFunc(
HANDLE hModule,
LPCTSTR lpType,
LPCTSTR lpName,
WORD wLang,
LONG lParam);

// Load the .EXE whose resources you want to list.
hExe = LoadLibrary("hand.exe");

if (hExe == NULL) {
ErrorHandler("Could not load .EXE.");
}

// Create a file to contain the resource info.
hFile = CreateFile("resinfo.txt", // name of file
GENERIC_READ | GENERIC_WRITE, // access mode
0, // share mode
(LPSECURITY_ATTRIBUTES) NULL, // no security
CREATE_ALWAYS, // create flags
FILE_ATTRIBUTE_NORMAL, // file attributes
(HANDLE) NULL); // no template
if (hFile == INVALID_HANDLE_VALUE) {
ErrorHandler("Could not open file.");
}

// Find all of the loaded file's resources.
cbString = sprintf(szBuffer,
"The file contains the following resources:\n\n");

WriteFile(hFile, // file to hold resource info.
szBuffer, // what to write to the file
(DWORD) cbString, // number of bytes in szBuffer
&cbWritten, // number of bytes written
NULL); // no overlapped I/O

EnumResourceTypes(hExe, // module handle
(ENUMRESTYPEPROC)EnumTypesFunc, // callback function
0); // extra parameter

// Unload the executable file whose resources were
// enumerated and close the file created to contain
// the resource information.

FreeLibrary(hExe);
CloseHandle(hFile);

// FUNCTION: EnumTypesFunc(HANDLE, LPSTR, LONG)
//
// PURPOSE: Resource type callback

BOOL EnumTypesFunc(
HANDLE hModule, // module handle
LPTSTR lpType, // address of resource type
LONG lParam) // extra parameter, could be
// used for error checking
{
int cbString;

// Write the resource type to a resource information file.
// The type may be a string or an unsigned decimal
// integer, so test before printing.

if ((ULONG)lpType & 0xFFFF0000) {
cbString = sprintf(szBuffer, "Type: %s\n", lpType);
}
else {
cbString = sprintf(szBuffer, "Type: %u\n", (USHORT)lpType);
}

WriteFile(hFile, szBuffer, (DWORD) cbString,
&cbWritten, NULL);

// Find the names of all resources of type lpType.
EnumResourceNames(hModule,
lpType,
(ENUMRESNAMEPROC)EnumNamesFunc,
0);

return TRUE;
}

// FUNCTION: EnumNamesFunc(HANDLE, LPSTR, LPSTR, LONG)
//
// PURPOSE: Resource name callback

BOOL EnumNamesFunc(
HANDLE hModule, // module handle
LPCTSTR lpType, // address of resource type
LPTSTR lpName, // address of resource name
LONG lParam) // extra parameter, could be
// used for error checking
{
int cbString;

// Write the resource name to a resource information file.
// The name may be a string or an unsigned decimal
// integer, so test before printing.

if ((ULONG)lpName & 0xFFFF0000) {
cbString = sprintf(szBuffer, "\tName: %s\n", lpName);
}
else {
cbString = sprintf(szBuffer, "\tName: %u\n",
(USHORT)lpName);
}

WriteFile(hFile, szBuffer, (DWORD) cbString,
&cbWritten, NULL);

// Find the languages of all resources of type
// lpType and name lpName.

EnumResourceLanguages(hModule,
lpType,
lpName,
(ENUMRESLANGPROC)EnumLangsFunc,
0);

return TRUE;
}

// FUNCTION: EnumLangsFunc(HANDLE, LPSTR, LPSTR, WORD, LONG)
//
// PURPOSE: Resource language callback

BOOL EnumLangsFunc(
HANDLE hModule, // module handle
LPCTSTR lpType, // address of resource type
LPCTSTR lpName, // address of resource name
WORD wLang, // resource language
LONG lParam) // extra parameter, could be
used for error checking
{
HANDLE hResInfo;
char szBuffer[80];
int cbString = 0;

hResInfo = FindResourceEx(hModule, lpType, lpName, wLang);

// Write the resource language to the resource information file.
cbString = sprintf(szBuffer, "\t\tLanguage: %u\n",
(USHORT)wLang);

WriteFile(hFile, szBuffer, (DWORD) cbString,
&cbWritten, NULL);

// Write the resource handle and size to buffer.
cbString = sprintf(szBuffer,
"\t\thResInfo == %lx, Size == %lu\n\n",
hResInfo,
SizeofResource(hModule, hResInfo));

WriteFile(hFile, szBuffer, (DWORD) cbString,
&cbWritten, NULL);

return TRUE;
}

+$#A Resource Functions
Following are the functions used with resources.
BeginUpdateResource_win32_BeginUpdateResourceCopyImage_win32_CopyImageEndUpdateResource_win32_EndUpdateResourceEnumResLangProc_win32_EnumResLangProcEnumResNameProc_win32_EnumResNameProcEnumResourceLanguages_win32_EnumResourceLanguagesEnumResourceNames_win32_EnumResourceNamesEnumResourceTypes_win32_EnumResourceTypesEnumResTypeProc_win32_EnumResTypeProcFindResource_win32_FindResourceFindResourceEx_win32_FindResourceExLoadImage_win32_LoadImageLoadResource_win32_LoadResourceLockResource_win32_LockResourceSizeofResource_win32_SizeofResourceUpdateResource_win32_UpdateResource
Obsolete or Deleted Functions
FreeResource_win32_FreeResourceSetResourceHandler_win32_SetResourceHandlerUnlockResource_win32_UnlockResource

+$#AK Security
The security provisions of Microsoft® Windows NT™ are available to Windows-based applications automatically. Every application running on the system is subject to the security imposed by the particular configuration of the local implementation of Windows NT.
+$#AK About Security
The security functions in the Microsoft Win32® application programming interface (API) allow an application to selectively grant and deny access to an object. An application can specify many different kinds of access for particular users and groups of users. The operating system grants or denies access to an object based on a comparison of the security provisions stored with an object with the access rights specified in a token associated with the process or thread requesting the access. These security functions allow an application to query and manipulate the security features of both an object and a process or thread.
The impact of Windows security on most Windows functions is minimal, and a Windows-based application not requiring security functionality usually does not need to incorporate any special code. However, a developer can use the security features of Windows NT to provide a number of services in a Windows-based application. Generally, any application that manipulates a system-wide resource such as the system time, must use the security system to gain access to that resource. A security-aware application might allow the user to query the security attributes of a file, provide specialized feedback when access to a secure file is denied, or customize the security attributes of a file or group of files so that only a subset of other users on a network has access to the information.
Windows NT is designed to support C2-level security as defined by the US Department of Defense. Some of the most important requirements of C2-level security are shown in the following list.
· It must be possible to control access to a resource. This access control must include or exclude individual users or named groups of users.
· Memory must be protected so its contents cannot be read after it is freed by a process.
· Users must identify themselves in a unique manner when they log on. All auditable actions must identify the user performing the action.
· System administrators must be able to audit security-related events. Access to this audit data must be limited to authorized administrators.
· The system must protect itself from external interference or tampering, such as modification of the running system or of system files stored on disk.

+$#A Security Model
All named objects in Windows NT, and some unnamed objects, can be secured. The security attributes of each securable object in Windows are described by a security descriptor_win32_Security_Descriptors, that contains information about the owner of the object and by an access-control list (ACL), identifying the users and groups allowed or denied access to the object. An ACL contains an entry for each user, global group, or local group (alias) being allowed or denied access to the object. Each of these entries is an access-control entry (ACE).
At logon, a user is assigned an access token_win32_Access_Tokens containing identifiers that represent the user and any groups to which the user belongs. Every process run on behalf of this user will have a copy of this particular access token. When a process attempts to use an object, the system compares the security attributes listed in the access token with the ACEs in the object's ACL. The system compares the access token with each ACE until access is either granted or denied or until there are no more ACEs to check. Conceivably, several ACEs could apply to a token. And, if this occurs, the access rightsaccess rights_win32_Access_Masks_and_Access_Rights granted by each ACE accumulate. For example, if one ACE grants read access to a group in an access token and another ACE grants write access to the user, who is also a member of the group, the user will have both read and write access to the object when the access check is complete.
The following illustration shows the relationship between these blocks of security information:
{ewc msdncd, EWGraphic, group10764 0 /a "SDK_01.bmp"}

Typically, the application protecting an object is a server in that it defines the users and groups with access to the object. The application interacts with clients when they attempt to gain access to the object. Users and groups are identified by security identifiers (SIDs)_win32_Security_Identifiers_SIDs_. An SID is a structure of variable length that uniquely identifies a user or group. SIDs are stored in a security database that an application can query by calling Win32 functions. With one exception, an SID is used to identify a user or group is never reassigned to another user or group. For a given account, the only SID that will not be the same from logon to logon is the logon-identifier SID. In the model represented by the preceding illustration, SIDs would be used to identify the following:
· The owner and group in the security descriptor
· The recipient of the access being granted by each ACE
· The user and groups in the access token

Security descriptors, SIDs, and ACLs are treated by applications as opaque structures and are intended to be manipulated only by using Win32 functions. This helps ensure that these structures remain syntactically accurate and prevents future enhancements to the security system from breaking existing code.
Pointers to doubleword values or structures must be aligned on doubleword boundaries. However, the exception is strings, for which alignment is not critical. All Win32 memory-allocation functions return handles of doubleword-aligned memory objects.
+$#A Access Tokens
When a user logs on, the system verifies the user's password by comparing it with information stored in a security database. If the password is authenticated, the system produces an access token and attaches it to the user's process. This access token identifies the user in all subsequent interactions with securable objects and contains the following information about a process:
· The user's SID
· Group SIDs
· Privileges
· An owner SID
· The SID for the primary group
· The default discretionary access-control list (ACL)
· The source of the access token
· Whether the token is a primary or impersonation token
· Current impersonation levels
· Other statistics

Every process must have an access token, but even a thread can have one assigned to it. When a thread has an access token, it supersedes the token of the process that owns the thread. This allows individual threads of a server process to impersonate clients by associating a client's access token with the thread. The only exception to this is any action requiring the SE_TCB_NAME privilege. This privilege is always checked against the process token, even if the current thread has an impersonation token.
For more information about privileges, see Privileges_win32_Privileges. For more information about security identifiers (SIDs), see Security Identifiers (SIDs)_win32_Security_Identifiers_SIDs_. For more information about discretionary ACLs, see Access-control Lists (ACLs)_win32_Access_control_Lists_ACLs_. For more information about the components of an access token, see the following token structures and enumerated types.
Structure or type Specifies
TOKEN_CONTROL_win32_TOKEN_CONTROL_str Information useful in identifying an access token.
TOKEN_DEFAULT_DACL_win32_TOKEN_DEFAULT_DACL_str The default discretionary ACL for an access token.
TOKEN_GROUPS_win32_TOKEN_GROUPS_str Specifies the SIDs of the access token's groups and whether any privileges are enabled.
TOKEN_INFORMATION_CLASS_win32_TOKEN_INFORMATION_CLASS_str Information being set in or retrieved from an access token.
TOKEN_OWNER_win32_TOKEN_OWNER_str The SID of an access token's owner.
TOKEN_PRIMARY_GROUP_win32_TOKEN_PRIMARY_GROUP_str The SID of the access token's primary group.
TOKEN_PRIVILEGES_win32_TOKEN_PRIVILEGES_str The privileges associated with an access token and whether the privileges are enabled.
TOKEN_SOURCE_win32_TOKEN_SOURCE_str The source of an access token.
TOKEN_STATISTICS_win32_TOKEN_STATISTICS_str Statistics associated with an access token.
TOKEN_TYPE_win32_TOKEN_TYPE_str Whether an access token is being used as an impersonation token.
TOKEN_USER_win32_TOKEN_USER_str The SID of an access token's user.

Following are the functions an application can use to manipulate access tokens.
Function Description
AdjustTokenGroups_win32_AdjustTokenGroups Changes the group information in an access token.
AdjustTokenPrivileges_win32_AdjustTokenPrivileges Changes the privileges in an access token.
GetTokenInformation_win32_GetTokenInformation Retrieves information about a token.
OpenProcessToken_win32_OpenProcessToken Retrieves the handle of the access token for a process.
OpenThreadToken_win32_OpenThreadToken Retrieves the handle of the access token for a thread.
SetTokenInformation_win32_SetTokenInformation Changes a token's owner, primary group, or default discretionary ACL.

+$#A Security Descriptors
A security descriptor contains the security information associated with an object. This information can include an own er, a primary group, a discretionary access-control list, and a system access-control list. This information is stored in the form of security identifiers (SIDs) and access-control lists (ACLs). For more information about access-control lists, see Access-control Lists (ACLs)_win32_Access_control_Lists_ACLs_.
There are five groups of Win32 functions that manipulate security descriptors, corresponding to five different kinds of objects:
· Files
· User Objects (also called window-management objects)
· Kernel objects
· Registry objects
· Private objects

To manipulate security descriptors for files and directories, an application can use the GetFileSecurity_win32_GetFileSecurity and SetFileSecurity_win32_SetFileSecurity functions. These functions identify files and directories by using character strings, instead of using the handles required by other security functions.
Kernel objects and user objects are manipulated by two pairs of functions: GetKernelObjectSecurity_win32_GetKernelObjectSecurity and SetKernelObjectSecurity_win32_SetKernelObjectSecurity, and GetUserObjectSecurity_win32_GetUserObjectSecurity and SetUserObjectSecurity_win32_SetUserObjectSecurity. Kernel and user objects are separated in this way because it is possible for a file handle and a window handle to have the same value. An application can use these functions to ensure that the correct object referenced.
An application can use the RegGetKeySecurity_win32_RegGetKeySecurity and RegSetKeySecurity_win32_RegSetKeySecurity functions to retrieve and set security descriptors for keys in the registry.
The functions that manipulate private objects allow an application, usually a server, to associate a security descriptor with its own object type. The functions provide the ability to retrieve default information from an access token, support inheritance, and manipulate specific parts of the security descriptor. These functions are CreatePrivateObjectSecurity_win32_CreatePrivateObjectSecurity, GetPrivateObjectSecurity_win32_GetPrivateObjectSecurity, SetPrivateObjectSecurity_win32_SetPrivateObjectSecurity, and DestroyPrivateObjectSecurity_win32_DestroyPrivateObjectSecurity.
The Win32 functions in the following table enable an application to query a security descriptor or set specific information in it.
Function Description
GetSecurityDescriptorControl_win32_GetSecurityDescriptorControl Retrieves revision and control information.
GetSecurityDescriptorDacl_win32_GetSecurityDescriptorDacl Retrieves the discretionary ACL.
GetSecurityDescriptorGroup_win32_GetSecurityDescriptorGroup Retrieves primary group information.
GetSecurityDescriptorLength_win32_GetSecurityDescriptorLength Returns the length of a security descriptor.
GetSecurityDescriptorOwner_win32_GetSecurityDescriptorOwner Retrieves the owner of a security descriptor.
GetSecurityDescriptorSacl_win32_GetSecurityDescriptorSacl Retrieves the system ACL.
SetSecurityDescriptorDacl_win32_SetSecurityDescriptorDacl Sets discretionary ACL information.
SetSecurityDescriptorGroup_win32_SetSecurityDescriptorGroup Sets primary group information.
SetSecurityDescriptorOwner_win32_SetSecurityDescriptorOwner Sets the owner of the security descriptor.
SetSecurityDescriptorSacl_win32_SetSecurityDescriptorSacl Sets system ACL information.

To check the revision level and structural integrity of a security descriptor, an application can call the IsValidSecurityDescriptor_win32_IsValidSecurityDescriptor function.
A security descriptor can be in either an absolute or self-relative format. In absolute format, a security descriptor contains pointers to its information, not the information itself. This format is useful when default settings for the owner, group, and discretionary ACL are available, because an application can simply call the InitializeSecurityDescriptor_win32_InitializeSecurityDescriptor function to initialize a SECURITY_DESCRIPTOR_win32_SECURITY_DESCRIPTOR_str structure and then point to preexisting components, such as SIDs and ACLs.
A security descriptor in self-relative format contains information in a contiguous block of memory. In this format, a SECURITY_DESCRIPTOR structure always starts the information, but the other components of the security descriptor can follow the structure in any order. Instead of using memory addresses, the security descriptor's components are identified by offsets from the beginning of the descriptor. This format is useful when a security descriptor must be stored on disk, transmitted by means of a communications protocol, or copied in memory.
All security descriptors provided by the operating system as the result of calls to Win32 API functions are in self-relative format. Security descriptors passed back to the operating system can be in either form, depending on the situation.
A server that copies secured objects to various media can use the MakeSelfRelativeSD_win32_MakeSelfRelativeSD function to create a self-relative security descriptor from an absolute security descriptor. The MakeAbsoluteSD_win32_MakeAbsoluteSD function can create an absolute security descriptor from a self-relative security descriptor.
+$#A Access-control Lists (ACLs)
Part of the security descriptor for each securable object is a list specifying users and groups, and the access to the object each is granted or denied. This list is an access-control list (ACL). An ACL consists of two parts. The first is a header in the form of an ACL_win32_ACL_str structure. This structure contains information pertaining to the entire ACL, such as its revision level and size. The second part is an ordered list of access-control entries (ACEs)_win32_Access_control_Entries_ACEs_. Each ACE identifies a user, group, or local group, and the access each of them is allowed.
There are two types of ACLs: discretionary and system.
A discretionary ACL is controlled by the owner of an object and specifies the access particular users or groups can have to an object. For example, the owner of a file can use a discretionary ACL to control who can and cannot have access to the file.
When an object's discretionary ACL contains no access-control entries, no access rights_win32_Access_Masks_and_Access_Rights have been explicitly granted. Therefore, access to the object is implicitly denied. However, when an object has no discretionary ACL, no protection is assigned to the object and any access request is granted. For information about setting no ACL for an object, see Allowing Access_win32_Allowing_Access.
An object may also have system-level security information associated with it, in the form of a system ACL. System ACLs are controlled by system administrators. A system ACL can enable the system administrator to audit any attempts to gain access to an object. In future releases, a system ACL will also be able to raise an alarm when an unauthorized user attempts to gain access to an object.
An application can use Win32 functions to retrieve, initialize, query, and modify ACLs.
Function Description
GetAclInformation_win32_GetAclInformation Retrieves revision or size information about an ACL.
GetSecurityDescriptorDacl_win32_GetSecurityDescriptorDacl Retrieves a discretionary ACL from a specified security descriptor.
GetSecurityDescriptorSacl_win32_GetSecurityDescriptorSacl Retrieves a system ACL from a specified security descriptor.
InitializeAcl_win32_InitializeAcl Initializes an ACL by creating an empty ACL structure.
IsValidAcl_win32_IsValidAcl Determines whether an ACL's revision level and structure are valid.
SetAclInformation_win32_SetAclInformation Changes the revision information in an ACL.
SetSecurityDescriptorDacl_win32_SetSecurityDescriptorDacl Puts a discretionary ACL into a specified security descriptor, superseding any preexisting discretionary ACL.
SetSecurityDescriptorSacl_win32_SetSecurityDescriptorSacl Puts a system access-control list into a specified security descriptor, superseding any preexisting system ACL.

To change a security descriptor's ACL, an application generally initializes a new ACL, adds ACEs to it, then uses either the SetSecurityDescriptorDacl or SetSecurityDescriptorSacl function to add the new ACL to the security descriptor. GetSecurityDescriptorDacl and GetSecurityDescriptorSacl retrieve a pointer to a preexisting ACL in a security descriptor so an application can copy information from the previous ACL as it creates a new one.
+$#A Access-control Entries (ACEs)
Each of the entries in an access-control list (ACL)_win32_Access_control_Lists_ACLs_ is an access-control entry (ACE). Each ACE consists of three parts:
· A header in the form of an ACE_HEADER_win32_ACE_HEADER_str structure structure
· An access mask for the specified ACE type
· A security identifier (SID)

The ACE_HEADER structure gives information such as the type of the ACE and its size. The access mask_win32_Access_Masks_and_Access_Rights is a 32-bit value that specifies the rights being granted, denied, or monitored. The security identifier is in the form of an SID_win32_SID_str structure and it specifies the user or group to which the ACE applies.
Discretionary ACLs have two types of ACE defined in the AceType member of the ACE_HEADER structure. They are shown in the following table.
Type Description
ACCESS_ALLOWED_ACE_TYPE This ACE grants the specified rights to the user or group identified by the security identifier. It is defined by an ACCESS_ALLOWED_ACE_win32_ACCESS_ALLOWED_ACE_str structure.
ACCESS_DENIED_ACE_TYPE This ACE denies the specified rights to the user or group identified by the security identifier. It is defined by an ACCESS_DENIED_ACE_win32_ACCESS_DENIED_ACE_str structure.

Only one type of ACE, SYSTEM-AUDIT-ACE-TYPE, is currently supported for system ACLs. It specifies what types of access cause system-level audits. This ACE is defined by a SYSTEM_AUDIT_ACE_win32_SYSTEM_AUDIT_ACE_str structure.
The SYSTEM_ALARM_ACE_TYPE is not supported in the current version of Windows NT.
Security auditing is used to keep a log of security-significant events, such as which users access a secured file. The SYSTEM_AUDIT_ACE_TYPE ACE is used to generate security audit messages and causes them to be entered into a system audit log for later processing. An administrator can view this log by using Event Viewer (EVENTVWR.EXE) and can manipulate the log by using the event-logging_win32_Event_Logging functions.
The order of ACEs in an ACL is important. An access token is checked against the ACEs in an ACL until it either accumulates the access rights_win32_Access_Masks_and_Access_Rights it needs, it is explicitly denied access in an access-denied ACE, or it reaches the end of the ACL and is implicitly denied access. If a user is a member of several groups represented by ACEs in the ACL, the rights granted to each group apply to the user. For example, a user may request read and write access to an object protected by an ACE that grants one group read access only. Another ACE may provide protection that grants write access. If the user belongs to both groups, the request will succeed. An ACL typically denies access to specific users or groups of users and allows access to more general categories of users. Although the ACEs in an ACL can be in any order, the security menu items in File Manager and Registry Editor (REGEDT32.EXE) only work with ACLs in which access-denied ACEs precede access-allowed ACEs.
To change a security descriptor's ACL, an application usually initializes a new ACL, adds ACEs to it, and then adds the ACL to the security descriptor. The AddAccessAllowedAce_win32_AddAccessAllowedAce and AddAccessDeniedAce_win32_AddAccessDeniedAce functions add an ACE at the end of a discretionary ACL. The AddAuditAccessAce_win32_AddAuditAccessAce function adds an ACE at the end of a system ACL. The AddAce_win32_AddAce function allows an application to specify the position of a new ACE in an ACL, and the DeleteAce_win32_DeleteAce function allows an application to remove an ACE from a specified position in one. The GetAce_win32_GetAce and FindFirstFreeAce_win32_FindFirstFreeAce functions are especially useful when copying information from a preexisting ACL to a new one.
+$#A Access Masks and Access Rights
An access right defines a particular set of abilities that can be granted or denied to a process when it attempts to use an object. For example, if an application attempts to create a subkey in the registry but does not have the KEY_CREATE_SUB_KEY access right, the system does not carry out the operation. An application usually requests a set of access rights when it opens an object, and in the case of registry keys, it makes that request when it calls the RegOpenKeyEx_win32_RegOpenKeyEx function.
An access mask is the component of an access-control entry (ACE)LRXVEZ that contains the specific rights, standard rights, and generic rights defining the access a user or group has to an object. Access masks are also used to request access rights when an object is opened. For example, an application that needs to set values, create subkeys, and query values in the registry can open a key using an access mask that combines the KEY_WRITE and KEY_QUERY_VALUE access rights.
An access mask is a single 32-bit value. The first 16 bits are the specific rights and apply only to the object type associated with the access mask. Bits 16 through 23 are the standard rights applying to all objects. Bits 28 through 31 are the generic rights mapped to specific and standard rights when access to an object is requested.
Generic access rights are broad types of access whose exact implementation is determined by the application defining an object. These rights are used when protecting an object. For example, an application that defines a voice-annotation object might define specific access rights by using VOICE_PLAY and VOICE_EDIT for playing and editing the object. It might set up a GENERIC_MAPPING_win32_GENERIC_MAPPING_str structure in which GENERIC_EXECUTE maps to VOICE_PLAY and GENERIC_WRITE maps to both VOICE_PLAY and VOICE_EDIT.
The following are the generic rights that have been defined.
Constant Meaning
GENERIC_ALL Read, write, and execute access
GENERIC_EXECUTE Execute access
GENERIC_READ Read access
GENERIC_WRITE Write access

If an application receives GENERIC_WRITE access to a file, for example, it has specific rights allowing it to write and append data to the file, write file attributes, and write extended attributes. In addition, the application has the STANDARD_RIGHTS_WRITE and SYNCHRONIZE standard access rights.
The following constants are masks for the standard access rights.
Constant Meaning
DELETE Delete access.
READ_CONTROL Read access to the security descriptor not including the system ACL.
STANDARD_RIGHTS_ALL Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE access.
STANDARD_RIGHTS_EXECUTE Currently defined to equal READ_CONTROL.
STANDARD_RIGHTS_READ Currently defined to equal READ_CONTROL.
STANDARD_RIGHTS_REQUIRED Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.
STANDARD_RIGHTS_WRITE Currently defined to equal READ_CONTROL.
SYNCHRONIZE Synchronize access. Allows a thread to wait for the object. This access type is not supported by all object types.
WRITE_DAC Write access to the discretionary ACL.
WRITE_OWNER Write access to the owner.

The SPECIFIC_RIGHTS_ALL constant denotes all of the specific rights, even those not be defined for an object.
The system validates each requested access against the process's access token, so requests for broad access can require a great deal of validation time. Therefore, it is generally better for applications to avoid requesting the broadest possible access to an object except when such access is actually required. Certain rights are commonly used in an ACL to deny access to an object, but they are rarely used in requests to open an object. In particular, requesting any of the access rights in the following list has a negative impact on system performance:
EVENT_ALL_ACCESS SECTION_ALL_ACCESS
FILE_MAP_ALL_ACCESS SEMAPHORE_ALL_ACCESS
GENERIC_ALL SERVICE_ALL_ACCESS
KEY_ALL_ACCESS SPECIFIC_RIGHTS_ALL
MUTEX_ALL_ACCESS STANDARD_RIGHTS_ALL
PROCESS_ALL_ACCESS THREAD_ALL_ACCESS
SC_MANAGER_ALL_ACCESS TOKEN_ALL_ACCESS

The MAXIMUM_ALLOWED constant specifies that an object is to be opened by using all the access rights that are valid for the given user. An additional access type, ACCESS_SYSTEM_SECURITY, is needed to manipulate an object's system ACL. Neither MAXIMUM_ALLOWED nor ACCESS_SYSTEM_SECURITY can be used in an DACL. However, you can use ACCESS_SYSTEM_SECURITY in a SACL to audit use of that bit in an access attempt.
The MapGenericMask_win32_MapGenericMask function maps a series of generic rights specified in a GENERIC_MAPPING_win32_GENERIC_MAPPING_str structure to specific and standard rights in an access mask. An application can use the GENERIC_MAPPING structure to specify the access rights for an object when it is created. When a client process requests access to the object, the server can use MapGenericMask to translate the client's requested access into the standard and specific rights for that object.
To discover whether a client process has the proper access rights to an object, a server can call the AccessCheck_win32_AccessCheck or AccessCheckAndAuditAlarm_win32_AccessCheckAndAuditAlarm function. The AreAllAccessesGranted_win32_AreAllAccessesGranted and AreAnyAccessesGranted_win32_AreAnyAccessesGranted functions compare a requested access mask with a granted access mask.
+$#A Security Identifiers (SIDs)
A security identifier (SID) is a unique value of variable length used to identify a user or group. The SID assigned to a user when he or she logs on becomes part of the access token that accompanies any process begun by that user. Except for the logon SID, an SID is always unique. When it has been used to identify a user or group, it cannot be used again, at any time, to identify another user or group.
SIDs identify several separate elements. These include the owner and group in security descriptors, the recipient of the access being granted by access-control entries (ACEs), and the user, as well as groups of which the user is a member in access tokens.
An SID also contains the following information.
· A 48-bit identifier authority value
· A revision level
· A variable number of subauthority values (relative identifiers)

The identifier authority value actually contains two values and is the most important piece of information in an SID. It contains a value identifying the agency that issued the SID, usually representing a Microsoft® Server™ domain, and a 32-bit relative identifier (RID) value, to uniquely identify the user or group in that agency. Joining these values ensures that no two SIDs will be the same, even if two different SID-issuing authorities issue the same RID. Each SID-issuing authority issues a given RID only once.
A standardized shorthand notation for SIDs makes it simpler to visualize their components:
S-R-I-S-S...
In the notation shown above, S identifies the series of digits as an SID, R is the revision level, I is the identifier-authority value, and S is the subauthority value. An SID could be written in this notation as follows:
S-1-4138-86
In this example, the SID has a revision level of 1, an identifier-authority value of 4138, and one subauthority value of 86.
An application is never required to manipulate an SID directly. The following Win32 functions provide all the functionality required to work with SIDs.
Function Description
AllocateAndInitializeSid_win32_AllocateAndInitializeSid Allocates and initializes an SID with the specified number of subauthorities.
CopySid_win32_CopySid Copies a source SID to a buffer.
EqualPrefixSid_win32_EqualPrefixSid Tests two SID prefix values for equality. An SID prefix is the entire SID except for the last subauthority value.
EqualSid_win32_EqualSid Tests two SIDs for equality. They must match exactly to be considered equal.
FreeSid_win32_FreeSid Frees an SID previously allocated by using the AllocateAndInitializeSid_win32_AllocateAndInitializeSid function.
GetLengthSid_win32_GetLengthSid Retrieves the length of an SID.
GetSidIdentifierAuthority_win32_GetSidIdentifierAuthority Retrieves a pointer to an SID's identifier authority.
GetSidLengthRequired_win32_GetSidLengthRequired Retrieves the size of the buffer required to store an SID with a specified number of subauthorities.
GetSidSubAuthority_win32_GetSidSubAuthority Retrieves a pointer to a specified subauthority in an SID.
GetSidSubAuthorityCount_win32_GetSidSubAuthorityCount Retrieves the number of subauthorities in an SID.
InitializeSid_win32_InitializeSid Initializes an SID_win32_SID_str structure.
IsValidSid_win32_IsValidSid Tests the validity of an SID by verifying that the revision number is within a known range and that the number of subauthorities is less than the maximum.
LookupAccountName_win32_LookupAccountName Retrieves the SID corresponding to a specified account name. Use the GetUserName_win32_GetUserName function to retrieve the user name for the current thread.
LookupAccountSid_win32_LookupAccountSid Retrieves the account name corresponding to a specified SID.

Some identifier authorities are predefined, as shown in the following table.
Identifier authority SID Value
SECURITY_NULL_SID_AUTHORITY 0
SECURITY_WORLD_SID_AUTHORITY 1
SECURITY_LOCAL_SID_AUTHORITY 2
SECURITY_CREATOR_SID_AUTHORITY 3
SECURITY_NT_AUTHORITY 5

The following RID values are also defined.
Relative identifier authority Value Superior authority
SECURITY_NULL_RID 0 S-1-0
SECURITY_WORLD_RID 0 S-1-1
SECURITY_LOCAL_RID 0 S-1-2
SECURITY_CREATOR_OWNER_RID 0 S-1-3
SECURITY_CREATOR_GROUP_RID 1 S-1-3

An application can combine an identifier authority and one of these RID values to create an SID that is meaningful on all installations. For example, S-1-1-0 (SECURITY_WORLD_SID_AUTHORITY and SECURITY_WORLD_RID) always identifies the special group representing all users. These security identifiers are universal well-known SIDs. A universal well-known SID is meaningful on all secure systems using this security model, including systems not running Windows NT. The AllocateAndInitializeSid_win32_AllocateAndInitializeSid function provides a simple method of combining an identifier authority and an RID.
The following are some universal well-known SIDs.
Universal well-known SID Value Identifies
Null SID (S-1-0-0) A group with no members. This is often used when an SID value is not known.
World (S-1-1-0) A group that includes all users.
Local (S-1-2-0) Users who log on to terminals locally (physically) connected to the system.
Creator Owner ID (S-1-3-0) A security identifier to be replaced by the security identifier of the user who created a new object. This SID is used in inheritable ACLs.
Creator Group ID (S-1-3-1) Identifies a security identifier to be replaced by the primary-group SID of the user who created a new object. Use this SID in inheritable ACLs.

Another predefined identifier authority is SECURITY_NT_AUTHORITY (S-1-5). This identifier authority produces SIDs that are not universal but are meaningful only on Windows NT installations. An application can use the following RID values with SECURITY_NT_AUTHORITY to create well-known SIDs.
Constant Identifies
SECURITY_DIALUP_RID (S-1-5-1)
Users who log on to terminals using a dialup modem. This is a group identifier.
SECURITY_NETWORK_RID (S-1-5-2)
Users who log on across a network. This is a group identifier.
SECURITY_BATCH_RID (S-1-5-3)
Users who log on using a batch queue facility. This is a group identifier.
SECURITY_INTERACTIVE_RID (S-1-5-4)
Users who log on for interactive operation. This is a group identifier.
SECURITY_LOGON_IDS_RID (S-1-5-5-X-Y)
A logon session. This is used to ensure that only processes in a given logon session can gain access to the window-station objects for that session. The X and Y values for these SIDs are different for each logon session. The value SECURITY_LOGON_IDS_RID_COUNT is the number of RIDs in this identifier (5-X-Y).
SECURITY_SERVICE_RID (S-1-5-6)
An account authorized to perform security services.
SECURITY_LOCAL_SYSTEM_RID (S-1-5-0x12)
The user account used by the operating system.
SECURITY_BUILTIN_DOMAIN_RID (S-1-5-0x20)
The built-in system domain.

The following RIDs are relative to each domain.
RID Identifies
DOMAIN_USER_RID_ADMIN The administrative user account in a domain.
DOMAIN_USER_RID_GUEST The guest-user account in a domain. This account can be logged onto automatically by users who do not have an account.
DOMAIN_GROUP_RID_ADMINS The domain administrator's group. This account exists only on systems running Microsoft® LAN Manager for Windows NT™, not Windows NT systems.
DOMAIN_GROUP_RID_USERS A group containing all user accounts in a domain. All users are automatically added to this group.

An alias is a local group and is similar to a global group, but it can contain members from other domains. Its members can be users or other groups. Some domain-relative RIDs, such as the following, can be used to form well-known SIDs used as local groups.
RID Identifies
DOMAIN_ALIAS_RID_ADMINS A local group used for administration of the domain.
DOMAIN_ALIAS_RID_USERS A local group representing all users in the domain.
DOMAIN_ALIAS_RID_GUESTS A local group representing guests of the domain.
DOMAIN_ALIAS_RID_POWER_USERS A local group used to represent a user or set of users that expect to treat a system as if it were their personal computer rather than a multiuser workstation.
DOMAIN_ALIAS_RID_ACCOUNT_OPS A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group permits control over non-administrator accounts.
DOMAIN_ALIAS_RID_SYSTEM_OPS A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group performs system administrative functions, not including security functions. It establishes network shares, controls printers, unlocks workstations, and performs other operations.
DOMAIN_ALIAS_RID_PRINT_OPS A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group controls printers and print queues.
DOMAIN_ALIAS_RID_BACKUP_OPS A local group used for controlling assignment of file backup-and-restore privileges.
DOMAIN_ALIAS_RID_REPLICATOR A local group responsible for copying security databases from the primary domain controller to the backup domain controllers.

+$#A Privileges
A privilege is used to control access to an object or service more strictly than is normal with discretionary access control. A system manager uses privileges to control which users are able to manipulate system resources. An application uses privileges when it changes a system resource, such as when it changes the system time or shuts down the system.
A privilege is a locally unique identifier (LUID) identified by a character string. This 64-bit value is guaranteed to be unique on the operating system that generated it until the system is restarted. For example, SE_SYSTEMTIME_NAME is a string identifying an LUID. A privilege has three representations, as shown below.
· A string name, meaningful across systems, called a global program name (for example, SE_SYSTEMTIME_NAME).
· A readable name that can be displayed to the user when necessary. For example, "Change the system time."
· A local representation that differs from computer to computer.

Privileges provide access to services rarely needed by most users. An account usually has privileges that are disabled, and they must be enabled to be used. For example, to set the time on the local computer, an application has to set the SE_PRIVILEGE_ENABLED attribute for the SE_SYSTEMTIME_NAME privilege. For security reasons, disable an enabled privilege when it is no longer needed.
The following privileges are defined by Windows NT.
Privilege Description
SE_ASSIGNPRIMARYTOKEN_NAME Required to assign the primary token of a process.
SE_AUDIT_NAME Required to generate audit-log entries. Give this privilege to secure servers.
SE_BACKUP_NAME Required to perform backup operations.
SE_CHANGE_NOTIFY_NAME Required to receive notifications of changes to files or directories. This privilege also causes the system to skip all traversal access checks. It is enabled by default for all users.
SE_CREATE_PAGEFILE_NAME Required to create a paging file.
SE_CREATE_PERMANENT_NAME Required to create a permanent object.
SE_CREATE_TOKEN_NAME Required to create a primary token.
SE_DEBUG_NAME Required to debug a process.
SE_INC_BASE_PRIORITY_NAME Required to increase the base priority of a process.
SE_INCREASE_QUOTA_NAME Required to increase the quota assigned to a process.
SE_LOAD_DRIVER_NAME Required to load or unload a device driver.
SE_LOCK_MEMORY_NAME Required to lock physical pages in memory.
SE_PROF_SINGLE_PROCESS_NAME Required to gather profiling information for a single process.
SE_REMOTE_SHUTDOWN_NAME Required to shut down a system using a network request.
SE_RESTORE_NAME Required to perform restore operations.
SE_SECURITY_NAME Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege identifies its holder as a security operator.
SE_SHUTDOWN_NAME Required to shut down a local system.
SE_SYSTEM_ENVIRONMENT_NAME Required to modify the non-volatile RAM of systems that use this type of memory to store configuration information.
SE_SYSTEM_PROFILE_NAME Required to gather profiling information for the entire system.
SE_SYSTEMTIME_NAME Required to modify the system time.
SE_TAKE_OWNERSHIP_NAME Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be set only to those values that the holder may legitimately assign as the owner of an object.
SE_TCB_NAME This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this privilege.
SE_UNSOLICITED_INPUT_NAME Required to read unsolicited input from a terminal device.

The following functions are provided for working with privileges.
Function Description
LookupPrivilegeValue_win32_LookupPrivilegeValue Allows an application to retrieve the LUID corresponding to a privilege on the local system. This is the local representation of that privilege and an application can set it in the TOKEN_PRIVILEGES_win32_TOKEN_PRIVILEGES_str and LUID_AND_ATTRIBUTES_win32_LUID_AND_ATTRIBUTES_str structures.
LookupPrivilegeDisplayName_win32_LookupPrivilegeDisplayName Retrieves a displayable privilege name. For example, "Force shutdown from a remote system."
LookupPrivilegeName_win32_LookupPrivilegeName Retrieves a programmatic privilege name.
PrivilegeCheck_win32_PrivilegeCheck Determines whether a client application has the privileges required to gain access to an object, such as, SE_REMOTE_SHUTDOWN_NAME.

+$#A Impersonation
Impersonation is the ability of a process to take on the security attributes of another process. Typically, a server thread impersonates a client thread to act on behalf of that client to access objects or validate access to its own objects. For example, when a client in a DDE conversation requests information from a DDE server, the server can impersonate the client so the system can verify that the client is allowed access to the information.
Currently, there are three impersonation levels:
Impersonation Level Meaning
SecurityAnonymous Indicates the client does not want the server to obtain identification information about the client.
SecurityIdentification Allows the server to obtain information about the client, such as security identifiers and privileges, without being able to impersonate the client. This is useful for servers who export their own objects, such as tables and views exported by a database product. Using the retrieved client security information, the server is able to make access-validation decisions for itself even though it is unable to use other services as the client.
SecurityImpersonation Allows the server to impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.

A fourth level, SecurityDelegation, is not supported in this release of Windows NT.
A DDE server application can impersonate a client by calling the DdeImpersonateClient_win32_DdeImpersonateClient function. When it has finished the task requiring the impersonation, the DDE server application can revert to its own security information by calling the RevertToSelf_win32_RevertToSelf function. Similarly, a named-pipe server can use the ImpersonateNamedPipeClient_win32_ImpersonateNamedPipeClient and RevertToSelf functions. The DuplicateToken_win32_DuplicateToken function creates an impersonation token that duplicates an existing token.
The ImpersonateSelf_win32_ImpersonateSelf function enables a thread to generate a copy of its own access token. This is useful when an application needs to change the security attributes of a single thread. For example, sometimes only one thread of a process requires a special privilege.
+$#A Security in New Objects
When an application creates an object, any values explicitly provided in a security descriptor are assigned to the object. If a security descriptor is not explicitly provided, the system searches for security information in different places, depending on the information required.
This guide describes the algorithms for security attribute assignments to newly created objects.
+$#A Assignment of Owner
To assign an owner to a new object when a security descriptor is not provided, the system checks the creator's access token for a default value. If no default value is found, the creator's user identifier (from the access token) is assigned as the object's owner. Even when an owner is provided in a security descriptor, the system checks these values in the access token to make sure the specified owner can be assigned ownership of the object.
+$#A Assignment of Group
To assign a primary group to a new object when a security descriptor is not provided, the system uses the primary group in the creator's access token.
+$#A Assignment of Discretionary ACL
To assign a discretionary access-control list (ACL) to a new object when a security descriptor is not provided, the system checks the parent's discretionary ACL for inheritable access-control entries (ACEs) and creates an ACL from any it finds. If there are no inheritable ACEs, the system checks the creator's security descriptor for a default discretionary ACL. If none is found in the security descriptor, the system looks in the creator's access token. If none of these sources provides a discretionary ACL, the object is created without one, and universal unconditional access to the object is granted.
+$#A Assignment of System ACL
To assign a system ACL to a new object when a security descriptor is not provided, the system checks the parent's system ACL for inheritable ACEs and creates an ACL from any it finds. If there are no inheritable ACEs, the system checks the creator's security descriptor for a default system ACL. When a system ACL is provided explicitly or by default in a security descriptor, the creator of the object must have the SE_SECURITY_NAME privilege, although this privilege is not required if the system ACL is acquired by inheritance. If none of these sources provides a system ACL, the object is created without one.
+$#A Security Attributes Inheritance
The security attributes of an object can be passed from process to process through the mechanism of handle inheritance_win32_Handle_Inheritance. In addition, individual ACEs can be inherited_win32_ACE_Inheritance in an ACL by new objects.
+$#A Handle Inheritance
If a new process inherits a handle, it also inherits the security attributes associated with that handle. A pointer to a SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure is used as a parameter in most Win32 kernel and window-management functions that return object handles. The bInheritHandle member of this structure specifies whether the returned handle is inherited when a new process is created. The security attributes of the inherited handle are identical to those of the original handle.
+$#A ACE Inheritance
Each ACE in an ACL begins with an ACE_HEADER_win32_ACE_HEADER_str structure structure specifying how that ACE is inherited. Among other things, the flags in an ACE_HEADER structure specify whether the ACE is inherited by container objects (directories or registry keys) or by non-container objects, such as files.
ACE Inheritance in Container Objects
This section outlines the rules governing the inheritance of ACEs by container objects.
If the CONTAINER_INHERIT_ACE flag is set in the ACE_HEADER_win32_ACE_HEADER_str structure of the parent ACE, a copy of the ACE is added to the end of the inherited ACL and the following actions are performed on the inherited ACE:
· All inheritance flags in the inherited ACE are cleared.
· If the ACE contains an access mask_win32_Access_Masks_and_Access_Rights and any generic access flags are set, the generic rights are mapped to the standard and specific rights for the child object type.

If neither the CONTAINER_INHERIT_ACE flag nor the NO_PROPAGATE_INHERIT_ACE flag is set, the OBJECT_INHERIT_ACE and INHERIT_ONLY_ACE flags are set. If both the CONTAINER_INHERIT_ACE and the NO_PROPAGATE_INHERIT_ACE are set, the ACE is added to the end of the inherited ACL.
If neither the NO_PROPAGATE_INHERIT_ACE nor the CONTAINER_INHERIT_ACE flag is set in the ACE_HEADER structure of the parent ACE, a copy of the ACE is added to the end of the inherited ACL and its INHERIT_ONLY_ACE flag is set.
Otherwise, the ACE is not inherited.
ACE Inheritance in Non-container Objects
The following rules govern the inheritance of ACEs in objects other than containers.
If the OBJECT_INHERIT_ACE flag is set in the ACE_HEADER_win32_ACE_HEADER_str structure of the parent ACE, the ACE is copied to the end of the inherited ACL and:
· The INHERIT_ONLY_ACE flag in the inherited ACE is cleared.
· The NO_PROPAGATE_INHERIT_ACE, OBJECT_INHERIT_ACE, and CONTAINER_INHERIT_ACE flags in the inherited ACE are cleared.
· If the ACE contains an access mask_win32_Access_Masks_and_Access_Rights and, if any generic access flags are set, the generic rights are mapped to the standard and specific rights for the child object type.

Otherwise, the ACE is not inherited.
+$#A Duplicating Handles
Among cooperating processes, any one process is only as restricted as the least restricted process. For example, a process can call the DuplicateHandle_win32_DuplicateHandle function to duplicate an object handle belonging to a second (source) process into a third (target) process. If the access rights_win32_Access_Masks_and_Access_Rights requested for the target process are greater than the rights held by the source process, the system checks the requested access rights against the security context of the process calling DuplicateHandle. If the calling process has the required access rights, the handle is duplicated, and the target process has greater access to the object than the source process.
+$#A Audit Generation
C2-level security requirements specify that system administrators must be able to audit security-related events and that access to this audit data must be limited to authorized administrators. The Win32 API provides functions enabling an administrator to monitor security-related events.
The system access-control list (ACL) contains audit access-control entries (ACEs) for an object. An application can use the GetSecurityDescriptorSacl_win32_GetSecurityDescriptorSacl and SetSecurityDescriptorSacl_win32_SetSecurityDescriptorSacl functions to retrieve an existing system ACL or to set a new one. The AddAuditAccessAce_win32_AddAuditAccessAce function adds an ACE to a system ACL that causes the system to record specified access attempts in a security log. This security log can be read by using the Microsoft Windows Event Viewer (EVENTVWR.EXE), and can be manipulated by using the event-logging functions discussed in Event Logging_win32_Event_Logging. For more information about system ACLs, see Access-control Lists (ACLs)_win32_Access_control_Lists_ACLs_.
An application can use the ObjectPrivilegeAuditAlarm_win32_ObjectPrivilegeAuditAlarm function to generate audit and alarm messages whenever a process attempts to complete a privileged operation. The PrivilegedServiceAuditAlarm_win32_PrivilegedServiceAuditAlarm generates audit and alarm messages whenever a process attempts to perform a privileged system-service operation.
The ObjectCloseAuditAlarm_win32_ObjectCloseAuditAlarm generates audit messages when an object is deleted. The ObjectOpenAuditAlarm_win32_ObjectOpenAuditAlarm function generates audit messages when a process attempts to open or gain access to an object.
+$#A Securable Objects
The kernel and window-management systems contain securable objects. This term refers to an object that has a security descriptor.
All named objects are securable. Some unnamed objects, such as unnamed process and thread objects, have security descriptors also.
You can use Win32 functions to query and set the security attributes of securable objects. The following are types of securable objects and the functions an application can use to manipulate their security attributes.
Object type Functions
Files and directories GetFileSecurity_win32_GetFileSecurity, SetFileSecurity_win32_SetFileSecurity
Mailslots GetFileSecurity, SetFileSecurity
Pipes GetFileSecurity, SetFileSecurity
Kernel objects (processes, threads, access tokens, and file-mapping objects) SetKernelObjectSecurity_win32_SetKernelObjectSecurity, GetKernelObjectSecurity_win32_GetKernelObjectSecurity
Window-management objects (window stations and desktops) GetUserObjectSecurity_win32_GetUserObjectSecurity, SetUserObjectSecurity_win32_SetUserObjectSecurity
Registry objects RegGetKeySecurity_win32_RegGetKeySecurity, RegSetKeySecurity_win32_RegSetKeySecurity
Service objects QueryServiceObjectSecurity_win32_QueryServiceObjectSecurity, SetServiceObjectSecurity_win32_SetServiceObjectSecurity
Synchronization objects SetKernelObjectSecurity_win32_SetKernelObjectSecurity, GetKernelObjectSecurity_win32_GetKernelObjectSecurity
Private objects (objects private to the creating application) CreatePrivateObjectSecurity_win32_CreatePrivateObjectSecurity, DestroyPrivateObjectSecurity_win32_DestroyPrivateObjectSecurity, GetPrivateObjectSecurity_win32_GetPrivateObjectSecurity, SetPrivateObjectSecurity_win32_SetPrivateObjectSecurity

+$#A File and Directory Objects
File and directory objects can be secured only when the New Technology file system (NTFS) is in use.
The access rights_win32_Access_Masks_and_Access_Rights for files and directories must be manipulated by using the generic access rights.
For file objects, the handle returned by the CreateFile_win32_CreateFile function has SYNCHRONIZE access and the right to read file attributes, in addition to the access rights specified in the call (GENERIC_READ, GENERIC_WRITE, or both).
GENERIC_READ access for file objects combines STANDARD_RIGHTS_READ and SYNCHRONIZE with rights that allow the process to read data from the file, read file attributes, and read extended attributes.
GENERIC_WRITE access for file objects combines STANDARD_RIGHTS_WRITE and SYNCHRONIZE with rights that allow the process to write data to the file, append data to it, write file attributes, and write extended attributes.
An application cannot use an access-denied ACE to deny only GENERIC_READ or GENERIC_WRITE access to a file. If an application denies GENERIC_WRITE access to a file, SYNCHRONIZE access is implicitly denied. When the CreateFile function is used in an attempt to open the file for read access, the function requests SYNCHRONIZE access and the operation fails. Instead of denying read or write access to a file, an application can explicitly allow the permitted access rights.
For directory objects, the handle returned by the CreateDirectory_win32_CreateDirectory function has SYNCHRONIZE access and the right to list the contents of the directory.
For more information about files and directories, see Files_win32_Files.
+$#A Mailslot Objects
The handle returned by the CreateMailslot_win32_CreateMailslot function has GENERIC_READ, SYNCHRONIZE, and WRITE_DAC access to the mailslot object.
GENERIC_READ access for a mailslot combines STANDARD_RIGHTS_READ with rights allowing the process to read data from the mailslot, read mailslot attributes, and read extended attributes.
For more information about mailslots, see Mailslots_win32_Mailslots.
+$#A Pipe Objects
The security attributes of named pipes_win32_Named_Pipes differ slightly from those of anonymous pipes_win32_Anonymous_Pipes_Overview.
+$#A Access Rights to Named Pipes
The handle returned by the CreateNamedPipe_win32_CreateNamedPipe function always has SYNCHRONIZE access. It also has GENERIC_READ, GENERIC_WRITE, or both, depending on the open mode of the pipe. The following are the access rights_win32_Access_Masks_and_Access_Rights for each open mode.
Open mode Access rights
PIPE_ACCESS_DUPLEX GENERIC_READ, GENERIC_WRITE, and SYNCHRONIZE
PIPE_ACCESS_INBOUND GENERIC_READ and SYNCHRONIZE
PIPE_ACCESS_OUTBOUND GENERIC_WRITE and SYNCHRONIZE

GENERIC_READ access for a named pipe combines STANDARD_RIGHTS_READ access with rights that allow the process to read data from the named pipe and to read named-pipe attributes.
GENERIC_WRITE access for a named pipe combines STANDARD_RIGHTS_WRITE with rights that allow the process to write data to the named pipe and to write named-pipe attributes.
+$#A CreatePipe Function and Anonymous Pipes
The handles returned by the CreatePipe_win32_CreatePipe function have GENERIC_READ, GENERIC_WRITE, and SYNCHRONIZE access to the anonymous pipe, in addition to the right to write the attributes of the pipe. GENERIC_READ and GENERIC_WRITE access map to the same access rights_win32_Access_Masks_and_Access_Rights as for named pipes.
For more information about pipes, see Pipes_win32_Pipes.
+$#A Special Access Kernel Objects
The following are the kernel objects for which the system defines special access rights_win32_Access_Masks_and_Access_Rights:
· Processes_win32_Process_Objects
· Threads_win32_Thread_Objects
· Access tokens_win32_Access_token_Objects
· File mapping_win32_File_Mapping_Objects

+$#A Process Objects
The valid access rights_win32_Access_Masks_and_Access_Rights for process objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
PROCESS_ALL_ACCESS Specifies all possible access rights for a process object.
PROCESS_CREATE_PROCESS Required to create a process.
PROCESS_CREATE_THREAD Required to create a thread.
PROCESS_DUP_HANDLE Required to duplicate a handle.
PROCESS_QUERY_INFORMATION Required to retrieve certain information about a process, such as its priority class.
PROCESS_SET_INFORMATION Required to set certain information about a process, such as its priority class.
PROCESS_TERMINATE Required to terminate a process.
PROCESS_VM_OPERATION Required to perform an operation on the address space of a process.
PROCESS_VM_READ Required to read memory in a process.
PROCESS_VM_WRITE Required to write to memory in a process.
SYNCHRONIZE Required to wait for the process to terminate.

The handle returned by the CreateProcess_win32_CreateProcess function has PROCESS_ALL_ACCESS access to the process object.
For more information about processes, see Processes and Threads_win32_Processes_and_Threads.
+$#A Thread Objects
The valid access rights_win32_Access_Masks_and_Access_Rights for thread objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
SYNCHRONIZE Required to wait for the thread to exit.
THREAD_ALL_ACCESS Specifies all possible access rights for a thread object. A thread created with THREAD_ALL_ACCESS access is created with its execution suspended.
THREAD_DIRECT_IMPERSONATION Required for a server thread that impersonates a client.
THREAD_GET_CONTEXT Required to read the context of a thread using GetThreadContext_win32_GetThreadContext.
THREAD_IMPERSONATE Required to use a thread's security information directly without calling it by using a communication mechanism that provides impersonation services.
THREAD_QUERY_INFORMATION Required to read certain information from the thread object.
THREAD_SET_CONTEXT Required to write the context of a thread.
THREAD_SET_INFORMATION Required to set certain information in the thread object.
THREAD_SET_THREAD_TOKEN Required to set the impersonation token for a thread.
THREAD_SUSPEND_RESUME Required to suspend or resume a thread.
THREAD_TERMINATE Required to terminate a thread.

The handle returned by the CreateThread_win32_CreateThread function has THREAD_ALL_ACCESS access to the thread object.
For more information about threads, see Processes and Threads_win32_Processes_and_Threads.
+$#A Access-token Objects
A process or thread cannot wait on an access-token object because the access-token object type does not support the SYNCHRONIZE standard access type.
The access rights_win32_Access_Masks_and_Access_Rights defined for access-token objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
TOKEN_ADJUST_DEFAULT Required to adjust default information, such as the owner, primary group, and default ACL in an access token.
TOKEN_ADJUST_GROUPS Required to adjust the groups in an access token.
TOKEN_ADJUST_PRIVILEGES Required to adjust the privileges in an access token.
TOKEN_ASSIGN_PRIMARY Required to attach a primary token to a process. The SE_CREATE_TOKEN_NAME privilege is also required to accomplish this task.
TOKEN_DUPLICATE Required to duplicate an access token.
TOKEN_EXECUTE Combines STANDARD_RIGHTS_EXECUTE and TOKEN_IMPERSONATE.
TOKEN_IMPERSONATE Required to attach an impersonation access token to a process.
TOKEN_QUERY Required to query an access token.
TOKEN_QUERY_SOURCE Required to query the source of an access token.
TOKEN_READ Combines STANDARD_RIGHTS_READ and TOKEN_QUERY.
TOKEN_WRITE Combines STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, and TOKEN_ADJUST_DEFAULT.

The TOKEN_ALL_ACCESS access right combines all possible access rights for a token.
The access rights for tokens are typically specified in calls to the OpenProcessToken_win32_OpenProcessToken and OpenThreadToken_win32_OpenThreadToken functions.
+$#A File Mapping Objects
A process or thread cannot wait on an access-token object because the access-token object type does not support the SYNCHRONIZE standard access type.
The access rights_win32_Access_Masks_and_Access_Rights defined for file mapping objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
FILE_MAP_READ Read access to the file-mapping object is requested. This allows a readable view of the file to be mapped.
FILE_MAP_WRITE Write access to the file mapping object is requested. This allows a writable view of the file to be mapped. If the file mapping access rights do not include PAGE_READWRITE, this access type does not allow writing the mapped file.
SECTION_EXTEND_SIZE Required to extend the size of a file mapping object.
SECTION_MAP_EXECUTE Required to complete an operation in a file mapping object.
SECTION_MAP_READ Equivalent to FILE_MAP_READ.
SECTION_MAP_WRITE Equivalent to FILE_MAP_WRITE.
SECTION_QUERY Required to query a file mapping object.

If the call to the CreateFileMapping_win32_CreateFileMapping function specifies the PAGE_READWRITE protection flag, the handle to the file mapping object has SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE access. If the call to CreateFileMapping specifies PAGE_READONLY, the handle of the file mapping object has SECTION_QUERY | SECTION_MAP_READ access.
The FILE_MAP_ALL_ACCESS access right specifies all possible access rights for the file mapping object. The SECTION_ALL_ACCESS access right is equivalent to it.
For more information about file-mapping objects, see File Mapping_win32_File_Mapping.
+$#A Window-management Objects
The following are the securable window-management objects.
Securable Object Description
Window station An object that contains a clipboard, a set of global atoms, and a group of desktop objects. The interactive window station assigned to the logon session of the interactive user also contains the display device, keyboard, and mouse. Window stations provide the first level of security for a work station and are the source of inheritable security for desktop objects.
Desktop An object that has a display surface and contains windows, menus, and hooks. A desktop object is a subobject of a window-station object, from which it inherits security.

+$#A Window Station Objects
The following are the window station access rights_win32_Access_Masks_and_Access_Rights and their meanings.
Constant Description
WINSTA_ACCESSCLIPBOARD Required to use the clipboard.
WINSTA_ACCESSGLOBALATOMS Required to manipulate global atoms.
WINSTA_CREATEDESKTOP Required to create new desktop objects on the window station.
WINSTA_ENUMDESKTOPS Required to enumerate existing desktop objects.
WINSTA_ENUMERATE Required for the window station to be enumerated.
WINSTA_EXITWINDOWS Required to successfully call the ExitWindows_win32_ExitWindows or ExitWindowsEx_win32_ExitWindowsEx function. Window stations can be shared by users and this access type can prevent other users of a window station from logging off the window station owner.
WINSTA_READATTRIBUTES Required to read the attributes of a window station object. This attribute includes color settings and other global window station properties.
WINSTA_READSCREEN Required to access screen contents.
WINSTA_WRITEATTRIBUTES Required to modify the attributes of a window station object. The attributes include color settings and other global window-station properties.

The following are the generic mappings for the interactive window-station object, which is the window station assigned to the logon session of the interactive user.
Value Maps to
GENERIC_READ STANDARD_RIGHTS_READ, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_READATTRIBUTES, and WINSTA_READSCREEN
GENERIC_WRITE STANDARD_RIGHTS_WRITE, WINSTA_ACCESSCLIPBOARD, WINSTA_CREATEDESKTOP, and WINSTA_WRITEATTRIBUTES
GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, WINSTA_ACCESSGLOBALATOMS, and WINSTA_EXITWINDOWS
GENERIC_ALL STANDARD_RIGHTS_REQUIRED, WINSTA_ACCESSCLIPBOARD, WINSTA_ACCESSGLOBALATOMS, WINSTA_CREATEDESKTOP, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_EXITWINDOWS, WINSTA_READATTRIBUTES, WINSTA_READSCREEN, and WINSTA_WRITEATTRIBUTES

The following are the generic mappings for a noninteractive window-station object. The system assigns noninteractive window stations to all logon sessions other than that of the interactive user.
Value Maps to
GENERIC_READ STANDARD_RIGHTS_READ, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, and WINSTA_READATTRIBUTES
GENERIC_WRITE STANDARD_RIGHTS_WRITE, WINSTA_ACCESSCLIPBOARD, and WINSTA_CREATEDESKTOP
GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, WINSTA_ACCESSGLOBALATOMS, and WINSTA_EXITWINDOWS
GENERIC_ALL STANDARD_RIGHTS_REQUIRED, WINSTA_ACCESSCLIPBOARD, WINSTA_ACCESSGLOBALATOMS, WINSTA_CREATEDESKTOP, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_EXITWINDOWS, and WINSTA_READATTRIBUTES

An application can call the GetProcessWindowStation_win32_GetProcessWindowStation function to retrieve a handle of its assigned window station. The security attributes of the window station can be retrieved and set by using this handle in calls to the GetUserObjectSecurity_win32_GetUserObjectSecurity and SetUserObjectSecurity_win32_SetUserObjectSecurity functions. An application can use the GetUserObjectInformation_win32_GetUserObjectInformation and SetUserObjectInformation_win32_SetUserObjectInformation functions to get and set information about a window-station object. For more information on window-station objects, see Window Stations and Desktops_win32_Window_Stations_and_Desktops.
+$#A Desktop Objects
A new desktop inherits its security descriptor from its parent window station.
The following are the desktop access rights_win32_Access_Masks_and_Access_Rights and their meanings.
Constant Description
DESKTOP_CREATEMENU Required to create a menu on the the desktop.
DESKTOP_CREATEWINDOW Required to create a window on the desktop.
DESKTOP_ENUMERATE Required for the desktop to be enumerated.
DESKTOP_HOOKCONTROL Required to establish any of the window hooks.
DESKTOP_JOURNALPLAYBACK Required to perform journal playback on a desktop.
DESKTOP_JOURNALRECORD Required to perform journal recording on a desktop.
DESKTOP_READOBJECTS Required to read objects on the desktop.
DESKTOP_SWITCHDESKTOP Required to activate the desktop using the SwitchDesktop_win32_SwitchDesktop function.
DESKTOP_WRITEOBJECTS Required to write objects on the desktop.

The following are the generic mappings for a desktop object contained in the interactive window station of the user's logon session.
Value Maps to
GENERIC_READ DESKTOP_ENUMERATE, DESKTOP_READOBJECTS, and STANDARD_RIGHTS_READ
GENERIC_WRITE DESKTOP_CREATEMENU, DESKTOP_CREATEWINDOW, DESKTOP_HOOKCONTROL, DESKTOP_JOURNALPLAYBACK, DESKTOP_JOURNALRECORD, DESKTOP_WRITEOBJECTS, and STANDARD_RIGHTS_WRITE
GENERIC_EXECUTE DESKTOP_SWITCHDESKTOP and STANDARD_RIGHTS_EXECUTE
GENERIC_ALL DESKTOP_CREATEMENU, DESKTOP_CREATEWINDOW, DESKTOP_ENUMERATE, DESKTOP_HOOKCONTROL, DESKTOP_JOURNALPLAYBACK, DESKTOP_JOURNALRECORD, DESKTOP_READOBJECTS, DESKTOP_SWITCHDESKTOP, DESKTOP_WRITEOBJECTS, and STANDARD_RIGHTS_REQUIRED

An application can call the GetThreadDesktop_win32_GetThreadDesktop function to retrieve a handle of the desktop for a given thread. The desktop's security attributes can be retrieved and set by using this handle in calls to GetUserObjectSecurity_win32_GetUserObjectSecurity and SetUserObjectSecurity_win32_SetUserObjectSecurity. An application can use the GetUserObjectInformation_win32_GetUserObjectInformation and SetUserObjectInformation_win32_SetUserObjectInformation functions to get and set information about a desktop object. For more information on desktop objects, see Window Stations and Desktops_win32_Window_Stations_and_Desktops.
+$#A Registry Key Objects
The following are the registry-key access rights_win32_Access_Masks_and_Access_Rights and their meanings.
Value Meaning
KEY_ALL_ACCESS Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK values.
KEY_CREATE_LINK Required to create a link to a registry-key object.
KEY_CREATE_SUB_KEY Required to create a subkey of a registry-key object.
KEY_ENUMERATE_SUB_KEYS Required to enumerate the subkeys of a registry-key object.
KEY_EXECUTE Equivalent to KEY_READ.
KEY_NOTIFY Required to request change notifications for a registry key or for subkeys of a registry key.
KEY_QUERY_VALUE Required to query a value of a registry-key object.
KEY_READ Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
KEY_SET_VALUE Required to create or set a value of a registry-key object.
KEY_WRITE Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY values.

For more information about registry objects, see Registry_win32_Registry.
+$#A Service Objects
The access rights_win32_Access_Masks_and_Access_Rights and generic mappings for service-control manager objects and service objects are given in the reference materials for the OpenService_win32_OpenService and OpenSCManager_win32_OpenSCManager functions. For information about service objects, see Services_win32_Services.
+$#A Synchronization Objects
There are three kinds of securable synchronization objects: semaphores_win32_Access_Rights_for_Semaphore_Objects, events_win32_Event_Objects, and mutexes_win32_Access_Rights_for_Mutex_Objects. The fourth type of synchronization object is critical sections. Because they are per-process objects and not kernel objects, they are not securable.
+$#A Semaphore Objects
The valid access rights_win32_Access_Masks_and_Access_Rights for semaphore objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
SEMAPHORE_ALL_ACCESS Specifies all possible access rights for a semaphore object.
SEMAPHORE_MODIFY_STATE Specifies modify state (release) access.
SYNCHRONIZE Specifies synchronization (wait) access.

The handle returned by the CreateSemaphore_win32_CreateSemaphore function has SEMAPHORE_ALL_ACCESS access to the semaphore object.
+$#A Event Objects
The valid access rights_win32_Access_Masks_and_Access_Rights for event objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
EVENT_ALL_ACCESS Specifies all possible access rights for an event object.
EVENT_MODIFY_STATE Specifies modify state (release) access.
SYNCHRONIZE Specifies synchronization (wait or release) access.

The handle returned by the CreateEvent_win32_CreateEvent function has EVENT_ALL_ACCESS access to the semaphore object.
+$#A Mutex Objects
The valid access rights_win32_Access_Masks_and_Access_Rights for mutex objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.
Value Meaning
MUTEX_ALL_ACCESS Specifies all possible access rights for a mutex object.
MUTEX_MODIFY_STATE Specifies modify state (release) access.
SYNCHRONIZE Specifies synchronization (wait or release) access.

The handle returned by the CreateMutex_win32_CreateMutex function has MUTEX_ALL_ACCESS access to the mutex object.
+$#A Using Security
The following topics demonstrate some practical applications of the Win32 API security functions and structures. As noted in File and Directory Objects_win32_File_and_Directory_Objects, file objects can be secured only when the NTFS file system is in use.
· Denying all access_win32_Denying_All_Access
· Allowing access_win32_Allowing_Access
· Setting privileges_win32_Setting_Privileges
· Security editor_win32_Security_Editor
· Designing a protected server_win32_Designing_a_Protected_Server

+$#A Denying All Access
The following example demonstrates adding an empty discretionary access-control list (ACL) to a file's security descriptor. An empty ACL implicitly denies all access to the file.
PSECURITY_DESCRIPTOR pSD;
PACL pACL;
DWORD cbACL = 1024;

/* Initialize a security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */
if (pSD == NULL) {
ErrorHandler("LocalAlloc");
return FALSE;
}

if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION)) { /* defined in WINNT.H */
ErrorHandler("InitializeSecurityDescriptor");
goto Cleanup;
}

/* Initialize a DACL. */

pACL = (PACL) LocalAlloc(LPTR, cbACL);
if (pACL == NULL) {
ErrorHandler("LocalAlloc");
goto Cleanup;
}

if (!InitializeAcl(pACL, cbACL, ACL_REVISION2)) {
ErrorHandler("InitializeAcl");
goto Cleanup;
}

/* Add an empty ACL to the SD to deny access. */

if (!SetSecurityDescriptorDacl(pSD,
TRUE, /* fDaclPresent flag */
pACL,
FALSE)) { /* not a default DACL */
ErrorHandler("SetSecurityDescriptorDacl");
goto Cleanup;
}

/* Use the new SD as the file's security info. */

if (!SetFileSecurity(lpszTestFile,
DACL_SECURITY_INFORMATION,
pSD)) {
ErrorHandler("SetFileSecurity");
goto Cleanup;
}

Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pACL != NULL)
LocalFree((HLOCAL) pACL);

+$#A Allowing Access
The following example demonstrates adding a NULL discretionary ACL to a file's security descriptor. A NULL ACL implicitly allows all access to the file.
Note: It is fine to write code like this that builds security descriptors from scratch. It is, however, a good practice for people who write code that builds or manipulates security descriptors to first write code that explores the default security descriptors that Windows NT places on objects. For example, if Windows NT by default includes in a DACL an ACE granting the Local Logon SID certain access, it's good to know that, so that a decision not to grant any access to the Local Logon SID would be a conscious decision.
PSECURITY_DESCRIPTOR pSD;

/* Initialize a security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */
if (pSD == NULL) {
ErrorHandler("LocalAlloc");
return FALSE;
}

if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION)) { /* defined in WINNT.H */
ErrorHandler("InitializeSecurityDescriptor");
goto Cleanup;
}

/* Add a NULL disc. ACL to the security descriptor. */

if (!SetSecurityDescriptorDacl(pSD,
TRUE, /* specifying a disc. ACL */
(PACL) NULL,
FALSE)) { /* not a default disc. ACL */
ErrorHandler("SetSecurityDescriptorDacl");
goto Cleanup;
}

/* Add the security descriptor to the file. */

if (!SetFileSecurity(lpszTestFile,
DACL_SECURITY_INFORMATION,
pSD)) {
ErrorHandler("SetFileSecurity");
goto Cleanup;
}

Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);

Rather than allowing all access by using a NULL ACL, applications generally allow access to an object by adding access-control entries (ACEs) to an ACL. The following example demonstrates finding a security identifier (SID) for a particular account and adding an access-allowed ACE for that account.
PSECURITY_DESCRIPTOR pSD;
PACL pACLNew;
DWORD cbACL = 1024;
PSID pSID;
DWORD cbSID = 1024;
LPSTR lpszAccount = "UserABC";
LPSTR lpszDomain;
DWORD cchDomainName = 80;
PSID_NAME_USE psnuType;

/* Initialize a new security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */
if (pSD == NULL) {
ErrorHandler("LocalAlloc");
return FALSE;
}

if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION)) {
ErrorHandler("InitializeSecurityDescriptor");
goto Cleanup;
}

/* Initialize a new ACL. */

pACLNew = (PACL) LocalAlloc(LPTR, cbACL);
if (pACLNew == NULL) {
ErrorHandler("LocalAlloc");
goto Cleanup;
}

if (!InitializeAcl(pACLNew, cbACL, ACL_REVISION2)) {
ErrorHandler("InitializeAcl");
goto Cleanup;
}

/* Retrieve the SID for UserABC. */

pSID = (PSID) LocalAlloc(LPTR, cbSID);
psnuType = (PSID_NAME_USE) LocalAlloc(LPTR, 1024);
lpszDomain = (LPSTR) LocalAlloc(LPTR, cchDomainName);
if (pSID == NULL || psnuType == NULL ||
lpszDomain == NULL) {
ErrorHandler("LocalAlloc");
goto Cleanup;
}

if (!LookupAccountName((LPSTR) NULL, /* local name */
lpszAccount,
pSID,
&cbSID,
lpszDomain,
&cchDomainName,
psnuType)) {
ErrorHandler("LookupAccountName");
goto Cleanup;
}
if (!IsValidSid(pSID))
TextOut(hdc, 10, yIncrement += 18,
"SID is not valid.", 17);
else
TextOut(hdc, 10, yIncrement += 18,
"SID is valid.", 13);

/* Allow read but not write access to the file. */

if (!AddAccessAllowedAce(pACLNew,
ACL_REVISION2,
GENERIC_READ,
pSID)) {
ErrorHandler("AddAccessAllowedAce");
goto Cleanup;
}

/* Add a new ACL to the security descriptor. */

if (!SetSecurityDescriptorDacl(pSD,
TRUE, /* fDaclPresent flag */
pACLNew,
FALSE)) { /* not a default disc. ACL */
ErrorHandler("SetSecurityDescriptorDacl");
goto Cleanup;
}

/* Apply the new security descriptor to the file. */

if (!SetFileSecurity("d:\\ntfs_sample_file",
DACL_SECURITY_INFORMATION,
pSD)) {
ErrorHandler("SetFileSecurity");
goto Cleanup;
}
TextOut(hdc, 10, yIncrement += 18,
"Successfully added access-allowed ACE to file's DACL.", 53);

Cleanup:
FreeSid(pSID);
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pACLNew != NULL)
LocalFree((HLOCAL) pACLNew);
if(psnuType != NULL)
LocalFree((HLOCAL) psnuType);
if(lpszDomain != NULL)
LocalFree((HLOCAL) lpszDomain);

+$#A Setting Privileges
The following example removes the discretionary ACL from a file. If necessary, ownership of the file is given to the Administrator account. If required, the SE_TAKE_OWNERSHIP_NAME privilege is enabled for the account.
LPSTR lpszOwnFile = "d:\\ntfs_sample_file";
PSID pSIDAliasAdmins = NULL;

static SID_IDENTIFIER_AUTHORITY
siaNTAuthority = SECURITY_NT_AUTHORITY;

BOOL FAR PASCAL TakeOwnership()
{
SECURITY_DESCRIPTOR sd;

/*
* Initialize a security descriptor and assign it a NULL
* discretionary ACL to allow unrestricted access.
* Assign the security descriptor to a file.
*/

if (!InitializeSecurityDescriptor(&sd,
SECURITY_DESCRIPTOR_REVISION)) {
ErrorHandler("InitializeSecurityDescriptor");
return FALSE;
}

if (!SetSecurityDescriptorDacl(&sd,
TRUE,
(PACL) NULL,
FALSE)) {
ErrorHandler("SetSecurityDescriptorDacl");
return FALSE;
}

if (SetFileSecurity(lpszOwnFile,
DACL_SECURITY_INFORMATION,
&sd))
return TRUE;
else
ErrorHandler("SetFileSecurity1");

/*
* If the preceding call to SetFileSecurity fails, create
* an Administrator SID and use it to set the owner of
* the security descriptor.
*/

if (!AllocateAndInitializeSid(&siaNTAuthority,
2, /* subauthority count */
/* first subauthority */
SECURITY_BUILTIN_DOMAIN_RID,
/* second subauthority */
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSIDAliasAdmins)) {
ErrorHandler("AllocateAndInitializeSid");
return FALSE;
}

if (!SetSecurityDescriptorOwner(&sd,
pSIDAliasAdmins,
FALSE)) {
ErrorHandler("SetSecurityDescriptorOwner");
FreeSid(pSIDAliasAdmins);
return FALSE;
}

/*
* If the following call to SetFileSecurity fails,
* enable SE_TAKE_OWNERSHIP_NAME in the access token for
* the current process and try again.
*/

if (!SetFileSecurity(lpszOwnFile,
OWNER_SECURITY_INFORMATION,
&sd)) {
ErrorHandler("SetFileSecurity2");
if (!AssertTakeOwnership(TRUE)) { /* local */
MessageBox(NULL, "Must be logged on as Administrator",
"AssertTakeOwnership", MB_OK);
FreeSid(pSIDAliasAdmins);
return FALSE;
}
if (!SetFileSecurity(lpszOwnFile,
OWNER_SECURITY_INFORMATION,
&sd)) {
MessageBox(NULL, "Must be logged on as Administrator",
"SetFileSecurity", MB_OK);
FreeSid(pSIDAliasAdmins);
return FALSE;
}
}

/*
* The Administrator is now the owner of the file.
* Try again to assign a NULL ACL.
*/

if (SetFileSecurity(lpszOwnFile,
DACL_SECURITY_INFORMATION,
&sd)) {
MessageBox(NULL, "Added NULL DACL; protection removed",
"SetFileSecurity", MB_OK);
AssertTakeOwnership(FALSE);
return TRUE;
}

else {
AssertTakeOwnership(FALSE);
ErrorHandler("SetFileSecurity3");
return FALSE;
}
}

BOOL FAR PASCAL AssertTakeOwnership(BOOL fEnable)
{
HANDLE hToken;
LUID TakeOwnershipValue;
TOKEN_PRIVILEGES tkp;

/* Retrieve a handle of the access token. */

if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken)) {
ErrorHandler("OpenProcessToken");
return FALSE;
}

/*
* Enable the SE_TAKE_OWNERSHIP_NAME privilege or
* disable all privileges, depending on the fEnable
* flag.
*/

if(fEnable) {
if (!LookupPrivilegeValue((LPSTR) NULL,
SE_TAKE_OWNERSHIP_NAME,
&TakeOwnershipValue)) {
ErrorHandler("LookupPrivilegeValue");
return FALSE;
}

tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = TakeOwnershipValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

AdjustTokenPrivileges(hToken,
FALSE,
&tkp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL);

/*
* The return value of AdjustTokenPrivileges cannot
* be tested.
*/

if (GetLastError() != ERROR_SUCCESS) {
ErrorHandler("AdjustTokenPrivileges");
return FALSE;
}
}
else {
AdjustTokenPrivileges(hToken,
TRUE, /* disable all privileges */
(PTOKEN_PRIVILEGES) NULL,
(DWORD) 0,
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL);

/*
* The return value of AdjustTokenPrivileges cannot
* be tested.
*/

if (GetLastError() != ERROR_SUCCESS) {
ErrorHandler("AdjustTokenPrivileges");
return FALSE;
}
}

return TRUE;
}

+$#A Security Editor
Both File Manager and the Microsoft Windows NT Registry Editor (REGEDT32.EXE) include a security editor that allows a user with the appropriate privileges_win32_Privileges and access rights_win32_Access_Masks_and_Access_Rights to change the security attributes of files and registry keys. This security editor imposes guidelines on the form of the security information. Applications that modify the security of files, directories, or registry keys can follow these guidelines so the security editor will function correctly after the modifications.
The security editor accepts discretionary ACLs having either zero or more ACCESS_DENIED_ACE access-control entries (ACEs) or zero or more ACCESS_ALLOWED_ACE ACEs. The two ACE types cannot be mixed in a discretionary ACL. Neither does the security editor accept ACCESS_DENIED_ACE ACEs that deny only partial access. For example, it would reject an ACE that denies only read access to a file.
The security editor also imposes a requirement on inheritance flags. These flags are ignored for noncontainer objects, such as files, but are important for container objects, such as directories and registry keys. For container objects that support permissions on objects in the container, make sure that each security identifier (SID) has an ACE inherited by objects and an ACE inherited by containers. Accordingly, the discretionary ACL should contain ACEs in which the AceFlags member of the ACE_HEADER_win32_ACE_HEADER_str structure structure contains the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags. Sometimes these flags can be combined in a single ACE. For example, a discretionary ACL that grants read access to a directory can contain a CONTAINER_INHERIT_ACE ACE that grants GENERIC_READ access to the user or group as well as an ACE with the OBJECT_INHERIT_ACE and INHERIT_ONLY_ACE flags that also grants GENERIC_READ access. In this case, the required flags could be combined into an ACE that grants GENERIC_READ access and combines the CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE flags.
The same rules for inheritance flags apply to system ACLs. The security editor does not support the SYSTEM_ALARM_ACE type and will not edit a system ACL containing one. As noted earlier, this ACE type is not supported by the current version of Windows NT.
+$#A Designing a Protected Server
The designer of a server application that assigns security to private object types must make many decisions about the design of the security interface, such as those in the following list:
· Define object.
· Determine the types of objects and the access types required, including whether to support SYNCHRONIZE.
· Determine whether an object is a container or noncontainer, as well as the appropriate inheritance flags. For more information about container objects, see Networks_win32_Networks.
· Define appropriate access types and generic mappings.
· Determine impersonation characteristics.
· Develop security descriptor for the initial object and for new objects assembled from the parent's security descriptor and the client's access token.
· Consider string- versus handle-based object-manipulation functions.
· Resolve potential conflicts in access rights_win32_Access_Masks_and_Access_Rights. For example, decide what is to be done if an administrator and a user both have delete access to an object.

For example, the designer of a protected print server might choose to define a server object, a printer object, and a document object. The three access rights in the following table are likely to be required:
Access right Description
Administrate Allows the holder to add objects to and delete them from another object. This right applies to the server, the printers, and the documents.
Enumerate Allows the holder to list a container object's contents. This right applies to the server and to the printers.
Use Allow the holder to add items to a print queue. This right applies to the printers.

+$#A Security Functions
Following are the functions used to control security.
AccessCheck_win32_AccessCheckAccessCheckAndAuditAlarm_win32_AccessCheckAndAuditAlarmAddAccessAllowedAce_win32_AddAccessAllowedAceAddAccessDeniedAce_win32_AddAccessDeniedAceAddAce_win32_AddAceAddAuditAccessAce_win32_AddAuditAccessAceAdjustTokenGroups_win32_AdjustTokenGroupsAdjustTokenPrivileges_win32_AdjustTokenPrivilegesAllocateAndInitializeSid_win32_AllocateAndInitializeSidAllocateLocallyUniqueId_win32_AllocateLocallyUniqueIdAreAllAccessesGranted_win32_AreAllAccessesGrantedAreAnyAccessesGranted_win32_AreAnyAccessesGrantedCopySid_win32_CopySidCreatePrivateObjectSecurity_win32_CreatePrivateObjectSecurityDdeImpersonateClient_win32_DdeImpersonateClientDeleteAce_win32_DeleteAceDestroyPrivateObjectSecurity_win32_DestroyPrivateObjectSecurityDuplicateToken_win32_DuplicateTokenEqualPrefixSid_win32_EqualPrefixSidEqualSid_win32_EqualSidFindFirstFreeAce_win32_FindFirstFreeAceFreeSid_win32_FreeSidGetAce_win32_GetAceGetAclInformation_win32_GetAclInformationGetFileSecurity_win32_GetFileSecurityGetKernelObjectSecurity_win32_GetKernelObjectSecurityGetLengthSid_win32_GetLengthSidGetPrivateObjectSecurity_win32_GetPrivateObjectSecurityGetProcessWindowStation_win32_GetProcessWindowStationGetSecurityDescriptorControl_win32_GetSecurityDescriptorControlGetSecurityDescriptorDacl_win32_GetSecurityDescriptorDaclGetSecurityDescriptorGroup_win32_GetSecurityDescriptorGroupGetSecurityDescriptorLength_win32_GetSecurityDescriptorLengthGetSecurityDescriptorOwner_win32_GetSecurityDescriptorOwnerGetSecurityDescriptorSacl_win32_GetSecurityDescriptorSaclGetSidIdentifierAuthority_win32_GetSidIdentifierAuthorityGetSidLengthRequired_win32_GetSidLengthRequiredGetSidSubAuthority_win32_GetSidSubAuthorityGetSidSubAuthorityCount_win32_GetSidSubAuthorityCountGetTokenInformation_win32_GetTokenInformationGetUserObjectSecurity_win32_GetUserObjectSecurityImpersonateNamedPipeClient_win32_ImpersonateNamedPipeClientImpersonateSelf_win32_ImpersonateSelfInitializeAcl_win32_InitializeAclInitializeSecurityDescriptor_win32_InitializeSecurityDescriptorInitializeSid_win32_InitializeSidIsValidAcl_win32_IsValidAclIsValidSecurityDescriptor_win32_IsValidSecurityDescriptorIsValidSid_win32_IsValidSidLookupAccountName_win32_LookupAccountNameLookupAccountSid_win32_LookupAccountSidLookupPrivilegeDisplayName_win32_LookupPrivilegeDisplayNameLookupPrivilegeName_win32_LookupPrivilegeNameLookupPrivilegeValue_win32_LookupPrivilegeValueMakeAbsoluteSD_win32_MakeAbsoluteSDMakeSelfRelativeSD_win32_MakeSelfRelativeSDMapGenericMask_win32_MapGenericMaskObjectCloseAuditAlarm_win32_ObjectCloseAuditAlarmObjectOpenAuditAlarm_win32_ObjectOpenAuditAlarmObjectPrivilegeAuditAlarm_win32_ObjectPrivilegeAuditAlarmOpenProcessToken_win32_OpenProcessTokenOpenThreadToken_win32_OpenThreadTokenPrivilegeCheck_win32_PrivilegeCheckPrivilegedServiceAuditAlarm_win32_PrivilegedServiceAuditAlarmRevertToSelf_win32_RevertToSelfSetAclInformation_win32_SetAclInformationSetFileSecurity_win32_SetFileSecuritySetKernelObjectSecurity_win32_SetKernelObjectSecuritySetPrivateObjectSecurity_win32_SetPrivateObjectSecuritySetSecurityDescriptorDacl_win32_SetSecurityDescriptorDaclSetSecurityDescriptorGroup_win32_SetSecurityDescriptorGroupSetSecurityDescriptorOwner_win32_SetSecurityDescriptorOwnerSetSecurityDescriptorSacl_win32_SetSecurityDescriptorSaclSetThreadToken_win32_SetThreadTokenSetTokenInformation_win32_SetTokenInformationSetUserObjectSecurity_win32_SetUserObjectSecurity

+$#AK Services
In the Microsoft® Win32® application programming interface (API), a service is an executable object that is installed in a registry database maintained by the service control manager.
+$#A About Services
The services database includes information that determines whether each installed service is started on demand or is started automatically when the system starts. The database can also contain logon and security information for a service so that a service can run even though no user is logged on. It also enables system administrators to customize security requirements for each service and thereby control access to the service. No more than one instance of a service can be running at a time.
Services can be divided into these two groups: Win32 services that conform to the interface rules of the service control manager, and driver services that conform to the device driver protocols for Microsoft Windows NT™.
+$#A Service Control Manager
A service control manager provides a unified and secure means of controlling services. Each machine that is running Windows NT has a service control manager process started by the system at system startup. The service control manager is a remote procedure call (RPC) server. The functions in the Win32 API that access a service control manager are provided through an RPC server so that service configuration and service control processes can manipulate services on remote machines.
The Win32 API includes a set of functions that provide a programmer's interface for the following tasks performed by the service control manager:
· Using the registry to maintain a database of installed service objects.
· Starting services either when the system is starting up or upon demand.
· Maintaining status information on running services.
· Transmitting control requests to running services.

Three types of programs use these functions:
Type Description
Win32 service process
A process that provides executable code for one or more services. Win32 services use functions that connect to the service control manager and send status information to it. Driver services do not use these functions.
Service configuration program
A program that queries or modifies a service control manager database. Service configuration programs use functions that open a database, install or delete services in a database, and query or modify the configuration and security parameters of installed services. The functions are used to manipulate Win32 services and driver services.
Service control program
A program that starts a service and controls a running service. Service control programs use functions that send requests to the service control manager, which starts the requested service or sends the control request to the specified service.

+$#A Database of Installed Services
The service control manager maintains a ServicesActive database in the registry. This is the currently active database that was used to start the system. The following is the registry path to this database.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
In this directory, there is a ServiceName key for each installed Win32 service or driver service. The ServiceName key is the name of the service specified by the CreateService_win32_CreateService function when the service was installed.
The database includes the following information about each installed service:
· The type of service. For Win32 services, the service type indicates whether the service executes in its own process or shares a process with other Win32 services. For driver services, it indicates whether the service is a kernel driver or a file system driver.
· The start type of the service. The start type indicates whether the service is started automatically at system startup or whether the service control manager starts it when requested by a service control process. The start type can also indicate whether the service is disabled, in which case it cannot be started.
· A fully qualified path of the service's executable file. The filename extension is .EXE for Win32 services and .SYS for driver services.
· Optional information used by the service control manager to determine the proper order for starting services. This information can include a list of services that must be running before the service can start, the name of a load ordering group that the service is part of, and a tag identifier that indicates the start order of the service in its load ordering group.
· For Win32 services, an optional logon account name in which the service process runs and an optional password for this account. If no account is specified, the service runs in the LocalSystem account.
· For driver services, an optional driver object name (for example, \FileSystem\Rdr or \Driver\Xns), used by the I/O system to load the device driver. If no name is specified, the I/O system creates a default name based on the service name.

+$#A Win32 Service Processes
A Win32 service process contains executable code for one or more services. To conform to the interface requirements of the service control manager, it must include:
· A main function that immediately calls the StartServiceCtrlDispatcher_win32_StartServiceCtrlDispatcher function to connect the main thread of the process to the service control manager.
· An entry point function, ServiceMain_win32_ServiceMain, for each service that can run in the process.
· A control handler function, Handler_win32_Handler, for each service that can run in the process.

For simplicity, the discussions in this section use the term service to mean Win32 service. The information in this section does not apply to driver services.
+$#A Main Function
When the service control manager starts a service process, it waits for the process to call the StartServiceCtrlDispatcher_win32_StartServiceCtrlDispatcher function. If a service does not share a process with other services (SERVICE_WIN32_OWN_PROCESS services), the main thread of a service process should make this call immediately, leaving all initialization tasks to be performed by the ServiceMain_win32_ServiceMain function after the service starts. For services where multiple services share a process (SERVICE_WIN32_SHARE_PROCESS services) and where some common process-wide initialization needs to be done before any ServiceMain function is called, the main thread can do the work before calling StartServiceCtrlDispatcher, as long as it takes less than 30 seconds. Otherwise, another thread must be created to do the process-wide initialization, while the main thread calls StartServiceCtrlDispatcher. Any service specific initialization should still be done by the individual ServiceMain functions.
If StartServiceCtrlDispatcher succeeds, it connects the calling thread to the service control manager and does not return until all running services in the process have terminated. The service control manager uses this connection to send control and service start requests to the main thread of the service process. The main thread acts as a dispatcher by calling the appropriate Handler_win32_Handler function to handle control requests or by creating a new thread to execute the appropriate ServiceMain function when a new service is started.
The StartServiceCtrlDispatcher function specifies a dispatch table containing an entry for each service that can run in the calling process. Each entry specifies the ServiceMain function for that service. For SERVICE_WIN32_SHARE_PROCESS services, each entry must contain the name of a service. This name is the service name that was specified by the CreateService_win32_CreateService function when the service was installed. For SERVICE_WIN32_OWN_PROCESS services, the service name in the table entry is ignored.
+$#A ServiceMain Function
The ServiceMain_win32_ServiceMain function is the entry point for a service. When a service control program calls the StartService_win32_StartService function and the service process for this service is not yet running, the service control manager starts the process and sends a start request to the dispatcher in the service's main thread. The dispatcher creates a new thread to execute the ServiceMain function for the service.
A ServiceMain function should perform the following tasks:
1. Call the RegisterServiceCtrlHandler_win32_RegisterServiceCtrlHandler function immediately to register a Handler_win32_Handler function to handle control requests for the service. The return value of RegisterServiceCtrlHandler is a SERVICE_STATUS_HANDLE type that identifies the service in calls to the SetServiceStatus_win32_SetServiceStatus function. A service calls SetServiceStatus to inform the service control manager of its status.
2. Perform initialization. If the execution time of the initialization code is expected to be very short (less than one second), initialization can be performed directly in ServiceMain.
If the initialization time is expected to be longer than one second, call SetServiceStatus, specifying SERVICE_START_PENDING as the current service state. As initialization continues, the service should make additional calls to SetServiceStatus to report progress. Sending multiple SetServiceStatus calls is useful for both service debugging and product support.
The following members of the SERVICE_STATUS structure passed in SetServiceStatus calls should change as the initialization progresses:
Member Value
dwCurrentState Set this value to SERVICE_START_PENDING during initialization. Set it to SERVICE_RUNNING when initialization is complete.
dwControlsAccepted You can disable control codes during initialization and enable them when initialization is complete.
dwCheckPoint Increment this value each time SetServiceStatus_win32_SetServiceStatus. is called. That informs the service control manager of the progress of a lengthy initialization.
dwWaitHint This value is an estimate of how long it will take to complete the next initialization step. It should reflect a worst-case estimate of the amount of time, in milliseconds, that will elapse before the next pending call to SetServiceStatus. If this interval elapses and dwCheckPoint has not been incremented, the service control manager or service control program can assume that an error has occurred.

3. When initialization is complete, call SetServiceStatus_win32_SetServiceStatus to specify the current state as SERVICE_RUNNING. The ServiceMain_win32_ServiceMain function can then go on to perform the service tasks, or, if there are no pending tasks, it can return. Any change in the state of the service warrants a call to SetServiceStatus to report new status information. If the service stops due to an error, the service should use the dwServiceSpecificExitCode and dwWin32ExitCode members of the SERVICE_STATUS_win32_SERVICE_STATUS_str structure to identify the error.

+$#A The Control Handler Function
Each Win32 service has a control handler function, the Handler_win32_Handler function, that is invoked by the main thread's dispatcher when the service process receives a control request. Control requests are generated by application programs that call the ControlService_win32_ControlService function. To register a Handler function, the service's ServiceMain_win32_ServiceMain function calls RegisterServiceCtrlHandler_win32_RegisterServiceCtrlHandler.
Whenever a service's Handler function is invoked, the service must call the SetServiceStatus_win32_SetServiceStatus function to report its status to the service control manager. This call must be done regardless of whether the status changed.
There is a set of standard control codes that the ControlService function can specify. All services must accept and process the SERVICE_CONTROL_INTERROGATE control code. Any of the other standard control codes can be enabled or disabled by calling SetServiceStatus. Services can also handle additional user-defined control codes, which must be in the range 128 to 255.
The standard control codes are listed below:
Control Meaning
SERVICE_CONTROL_STOP
Requests the service to stop.
SERVICE_CONTROL_PAUSE
Requests the service to pause.
SERVICE_CONTROL_CONTINUE
Requests the paused service to resume.
SERVICE_CONTROL_INTERROGATE
Requests the service to immediately update its current status information to the service control manager.
SERVICE_CONTROL_SHUTDOWN
Requests the service to perform cleanup tasks, because the system is shutting down. Due to the extremely limited time available for shutdown, this control code should be used only by services that must perform a specific cleanup operation that takes a minimal amount of time before the system shuts down. The Event Log and Server services are examples of services that handle SERVICE_CONTROL_SHUTDOWN. The Event Log service needs to clear a dirty bit in the files that it maintains. The Server service needs to shutdown to prevent future network connections to a system that is shut down.
To respond quickly to the SERVICE_CONTROL_SHUTDOWN code, the service may send a SERVICE_STOP_PENDING message and allow another thread to complete the shutdown operation. Service shutdown is complete when the SERVICE_STOPPED message is sent. The system gives the service control manager a limited amount of time (about 20 seconds) to complete service shutdown, after which time system shutdown proceeds regardless of whether service shutdown is complete. .

+$#A Service Configuration Programs
Service configuration programs are used by programmers or system administrators to query or modify a service control manager database. These databases are maintained in the registry, so they can also be accessed by using the registry functions or a registry editor. It is, however, safer to use the service control manager's configuration functions, which ensure that the service is installed and configured properly.
Service control manager databases and services are implemented as objects that should only be accessed through the service control manager's configuration functions. A database of installed services is represented by an SCManager object, and an installed service is represented by a service object. An SCManager object is a container object that holds service objects.
The handle type of both SCManager objects and service objects is SC_HANDLE. The OpenSCManager_win32_OpenSCManager function returns a handle to an SCManager object on a specified machine. The CreateService_win32_CreateService and OpenService_win32_OpenService functions return a handle to a service object installed in a specified SCManager object. The CloseServiceHandle_win32_CloseServiceHandle function closes SC_HANDLE to either an SCManager object or a service object.
The functions that return SC_HANDLE can request different types of access to the SCManager or service object. The requested access is granted or denied depending on the access token of the calling process and the security descriptor associated with the SCManager or service object.
A configuration program uses the CreateService function to install a new service in an service control manager database. This function specifies the name of the service and provides the service's configuration information that is stored in the database. For a description of the information stored in the database for each service, see Database of Installed Services_win32_Database_of_Installed_Services.The DeleteService_win32_DeleteService function removes an installed service from its database.
The following functions are available to determine the current configuration of a service control manager database:
Function Description
QueryServiceConfig_win32_QueryServiceConfig
Fills in a buffer with a QUERY_SERVICE_CONFIG_win32_QUERY_SERVICE_CONFIG_str structure, containing the current configuration information for a specified service object.
QueryServiceObjectSecurity_win32_QueryServiceObjectSecurity
Fills in a buffer with the SECURITY_DESCRIPTOR_win32_SECURITY_DESCRIPTOR_str structure of a specified SCManager or service object.
EnumDependentServices_win32_EnumDependentServices
Fills in a buffer with an array of ENUM_SERVICE_STATUS_win32_ENUM_SERVICE_STATUS_str structures, containing the name and current status of all services that are dependent on a specified service object.
EnumServicesStatus_win32_EnumServicesStatus
Fills in a buffer with an array of ENUM_SERVICE_STATUS_win32_ENUM_SERVICE_STATUS_str structures, containing the name and current status of all services in a specified SCManager object.

To modify the configuration information for a service object, a configuration program uses the ChangeServiceConfig_win32_ChangeServiceConfig function. To modify the security descriptor for either an SCManager object or a service object, a configuration program uses the SetServiceObjectSecurity_win32_SetServiceObjectSecurity function.
Before using either of these functions to reconfigure a service object, a program should use the LockServiceDatabase_win32_LockServiceDatabase function. This function tries to acquire a lock on the specified service control manager database and, if successful, prevents the service control manager from starting a service while the database is being reconfigured. A lock should also be acquired before using the registry functions to reconfigure a service object. Note, however, that failure to acquire a lock does not prevent a configuration program from successfully reconfiguring a service object. The only effect of a lock is to prevent the service control manager from starting a service in the locked database. To release the lock on the database when the reconfiguration is complete, use the UnlockServiceDatabase_win32_UnlockServiceDatabase function. To determine whether the database is locked, use the QueryServiceLockStatus_win32_QueryServiceLockStatus function.
+$#A Service Control Programs
A service control program performs the following actions:
· Starts a Win32 service or a driver service, if the service's start type is SERVICE_DEMAND_START.
· Sends control requests to a running service.
· Queries the current status of a running service.

The program must have an open handle to any service object that it wants to start or control. To do this, it must perform these steps:
1. Use the OpenSCManager_win32_OpenSCManager function to open a handle to a service control manager database on a specified machine.
2. Use the OpenService_win32_OpenService or CreateService_win32_CreateService function to open a handle to the desired service object.

+$#A Service Startup
To start a service, use the StartService_win32_StartService function. For Win32 services, the calling process can use the parameters of this function to specify an array of arguments that are passed to the ServiceMain_win32_ServiceMain function of the service. This function fails if the service control manager database is locked. If this occurs, the service control program can wait a few seconds and call StartService again. It can check the current lock status of the database by calling the QueryServiceLockStatus_win32_QueryServiceLockStatus function.
When starting a driver service, StartService returns after the device driver has completed its initialization.
When starting a Win32 service, StartService returns after the ServiceMain function thread for the new service has been created. During its initialization, a service specifies a SERVICE_STATUS_win32_SERVICE_STATUS_str structure in calls to the SetServiceStatus_win32_SetServiceStatus function to report its progress to the service control manager. The service control program can retrieve this status information by calling the QueryServiceStatus_win32_QueryServiceStatus function. During initialization, the dwCurrentState member of the SERVICE_STATUS structure should be SERVICE_START_PENDING. The service should increment the dwCheckPoint member at regular intervals during initialization. The dwWaitHint member is a time interval, in milliseconds, that indicates how long the service control program should wait before calling QueryServiceStatus again. When the initialization is complete, the service changes the dwCurrentState member to SERVICE_RUNNING.
+$#A Service Control Requests
To send control requests to a running service, use the ControlService_win32_ControlService function. This function specifies a control value that is passed to the Handler_win32_Handler function of the specified service. This control value can be a user-defined code, or it can be one of the standard codes that enable the calling program to perform the following actions:
· Stops or pauses a service.
· Resumes a paused service.
· Gets updated status information from a service.

Each service specifies the control values that it will accept and process. To determine which of the standard control values are accepted by a service, use the QueryServiceStatus_win32_QueryServiceStatus function or specify the SERVICE_CONTROL_INTERROGATE control value in a call to the ControlService_win32_ControlService function. The dwControlsAccepted member of the SERVICE_STATUS_win32_SERVICE_STATUS_str structure returned by these functions indicates whether the service can be stopped and whether it can be paused and resumed. All services accept the SERVICE_CONTROL_INTERROGATE control value, which requests the service to immediately report its current status.
Note, however, that the QueryServiceStatus function reports the service control manager's most recent status for a specified service, but does not get an updated status from the service itself. Using the SERVICE_CONTROL_INTERROGATE control value in a call to ControlService ensures that the status information returned is current.
+$#A Service Security
When a process uses the OpenSCManager_win32_OpenSCManager function to open a handle to a service control manager database, the system performs a security check before granting the requested access. All processes are permitted SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS access to all service control manager databases. This access enables any process to open a service control manager database handle that it can use for the OpenService_win32_OpenService, EnumServicesStatus_win32_EnumServicesStatus, and QueryServiceLockStatus_win32_QueryServiceLockStatus functions. Only processes with Administrator privileges are able to open a database handle that can be used by the CreateService_win32_CreateService and LockServiceDatabase_win32_LockServiceDatabase functions.
Security for service objects is provided by an access check performed when a process uses the OpenService function. The access permitted to different users depends on the SECURITY_DESCRIPTOR_win32_SECURITY_DESCRIPTOR_str structure associated with the service object. The service control manager creates a service object's security descriptor when the service is installed by the CreateService function. You can use the QueryServiceObjectSecurity_win32_QueryServiceObjectSecurity and SetServiceObjectSecurity_win32_SetServiceObjectSecurity functions to query and set the security descriptor of a service object. The default security descriptor of a service object permits the following access:
· All users have SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_ENUMERATE_DEPENDENTS, SERVICE_INTERROGATE, and SERVICE_USER_DEFINED_CONTROL access.
· Local users have SERVICE_START access in addition to those listed above.
· LocalSystem users and PowerUsers have SERVICE_START, SERVICE_PAUSE_CONTINUE, and SERVICE_STOP access in addition to those listed above.
· Administrators have SERVICE_ALL_ACCESS access.

+$#A Win32 Service User Accounts
Each Win32 service runs in a user account. The user name and password of an account are specified by the CreateService_win32_CreateService function at the time the service is installed. The user name and password can be changed by using the ChangeServiceConfig_win32_ChangeServiceConfig function. You can use the QueryServiceConfig_win32_QueryServiceConfig function to get the user name (but not the password) of a service object.
When starting a service process, the service control manager logs on to the account associated with the service. If the log on is successful, the system produces an access token and attaches it to the new service process. This token identifies the service process in all subsequent interactions with securable objects (objects that have a security descriptor associated with them). For example, if the service tries to open a handle to a pipe, the system compares the service's access token to the pipe's security descriptor before granting access.
A SERVICE_WIN32_OWN_PROCESS service that runs in a process by itself can have an account from either the built-in (local), primary, or trusted domain. A SERVICE_WIN32_SHARE_PROCESS service that can share a process with multiple services must be installed to run in the LocalSystem account. This restriction exists because different services in the same process must run under the same security context.
The service control manager does not try to maintain the passwords of service user accounts. If a password is expired, the logon fails and the service fails to start. The system administrator who assigns accounts to services can create accounts with passwords that never expire. The administrator can also manage accounts with passwords that expire by using a service configuration program to periodically change the passwords.
If a service needs to recognize another service before sharing its information, the second service can either use the same account as the first service, or it can run in an account belonging to an alias that is recognized by the first service. Services that need to run in a distributed manner across the network should run in domain-wide accounts.
+$#A Interactive Services
An interactive service is a Win32 service that allows the user's desktop to interact with the service. Interactive services run in the LocalSystem account. Services are configured to be interactive when the dwServiceType parameter in a CreateService_win32_CreateService call is set to include the SERVICE_INTERACTIVE_PROCESS bit flag. SERVICE_INTERACTIVE_PROCESS may be used only with the SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS flags. This bit flag affects only the service process when it is created (that is, when the first service in the process is started).
The following key in the registry contains a value called NoInteractiveServices that controls the effect of the SERVICE_INTERACTIVE_PROCESS flag:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows
The NoInteractiveServices value defaults to 0, which means that services marked with the SERVICE_INTERACTIVE_PROCESS flag will be allowed to be interactive. When the NoInteractiveServices value is set to nonzero, any service started thereafter, regardless of whether it has been configured with SERVICE_INTERACTIVE_PROCESS, will not be allowed to run interactively. Setting NoInteractiveServices to nonzero completely guarantees a secure system.
+$#A Using Services
· Writing a Win32 service process's main function_win32_Writing_a_Win32_Service_Process_s_main_Function
· Writing a ServiceMain function_win32_Writing_a_ServiceMain_Function
· Writing a control handler function_win32_Writing_a_Control_Handler_Function
· Opening an SCManager database_win32_Opening_an_SCManager_Database
· Installing a service_win32_Installing_a_Service
· Deleting a service_win32_Deleting_a_Service
· Changing a service configuration_win32_Changing_a_Service_Configuration
· Querying a service's configuration_win32_Querying_a_Service_s_Configuration
· Starting a service_win32_Starting_a_Service
· Sending control requests to a service_win32_Sending_Control_Requests_to_a_Service

+$#A Writing a Win32 Service Process's main Function
The main function of a Win32 service process calls the StartServiceCtrlDispatcher_win32_StartServiceCtrlDispatcher function to connect to the service control manager and start the control dispatcher thread. The dispatcher thread loops, waiting for incoming control requests for the services specified in the dispatch table. This thread does not return unless there is an error or all of the services in the process have terminated. When all services in a process have terminated, the service control manager sends a control request to the dispatcher thread telling it to shut down. The thread can then return from the StartServiceCtrlDispatcher call and the process can terminate.
The example provided in this section demonstrates a service process that supports only one service. Service processes with a single service call StartServiceCtrlDispatcher immediately because its entry-point function, ServiceMain_win32_ServiceMain, can perform all necessary initialization.
If your service process supports multiple services, the implementation of the main function may differ slightly. The names of the additional services are added to the dispatch table so they can be monitored by the dispatcher thread. Some multiple service processes may need to perform process-wide initialization tasks before starting any of the services with the StartServiceCtrlDispatcher call. If this is the case and the initialization is brief (less than 30 seconds), the main thread can take care of it before calling StartServiceCtrlDispatcher. If this initialization typically lasts longer than 30 seconds, one of the following strategies can be used:
· Call CreateThread_win32_CreateThread to create a thread to perform the process-wide initialization.
· Use a global flag that each service checks during its initialization so that the first service to start up performs the tasks that are process-wide.

The following example calls a local function, SvcDebugOut, to print informational messages and errors to the debugger, and a Win32 function, GetLastError_win32_GetLastError, to retrieve a detailed error code when RegisterServiceCtrlHandler_win32_RegisterServiceCtrlHandler fails. SvcDebugOut takes two parameters: a single string that can contain one formatted output character and a numeric value to be used as the formatted character.
/* Globals */
SERVICE_STATUS MyServiceStatus;
SERVICE_STATUS_HANDLE MyServiceStatusHandle;

/* Function Prototypes */
VOID MyServiceStart (DWORD argc, LPTSTR *argv);
VOID MyServiceCtrlHandler (IN DWORD opcode);
DWORD MyServiceInitialization(DWORD argc, LPTSTR *argv, DWORD *specificError);

VOID _CRTAPI1 main(void)
{
SERVICE_TABLE_ENTRY DispatchTable[] = {
{ TEXT("MyService"), MyServiceStart },
{ NULL, NULL }
};

if (!StartServiceCtrlDispatcher( DispatchTable)) {
SvcDebugOut(" [MY_SERVICE] StartServiceCtrlDispatcher error = %d\n", GetLastError());
}
}

VOID SvcDebugOut(LPSTR String, DWORD Status)
{
CHAR Buffer[1024];
if (strlen(String) < 1000) {
sprintf(Buffer, String, Status);
OutputDebugStringA(Buffer);
}
}

+$#A Writing a ServiceMain Function
The MyServiceStart function in the following example is the service's entry point. When the control dispatcher is told to start a service, it creates a thread that will begin executing at this point. MyServiceStart has access to command-line arguments in the same manner as does the typical main function. The first parameter contains the number of arguments being passed to the service. There will always be at least one argument. The second parameter is a pointer to an array of string pointers. The first item in the array always points to the service name.
MyServiceStart first fills in the SERVICE_STATUS_win32_SERVICE_STATUS_str structure including the control codes that it will accept. Note that although this service accepts PAUSE_CONTINUE, it does nothing significant when told to pause. PAUSE_CONTINUE was included for illustration purposes only; if pausing does not add value to your service, do not support it. The dwCheckPoint, dwWaitHint, and the exit code fields are all initialized to zero.
MyServiceStart then calls the RegisterServiceCtrlHandler_win32_RegisterServiceCtrlHandler function to register MyService as the service's Handler_win32_Handler function and begin initialization. The following sample initialization function, MyServiceInitialization, is included for illustration purposes; it does not perform any initialization tasks such as creating additional threads. If your service's initialization performs tasks that are expected to take longer than one second, your code must call SetServiceStatus_win32_SetServiceStatus periodically to send out wait hints and check points indicating that progress is being made.
When initialization has completed successfully, a call to SetServiceStatus with a status of SERVICE_RUNNING is made and the service can continue with its work. If an error has occurred in initialization, MyServiceStart reports SERVICE_STOPPED with the SetServiceStatus call and returns.
Since this sample service does not do anything, MyServiceStart simply returns control to the caller. However, a real service should use this thread to do whatever work it was designed to do. The ServiceMain_win32_ServiceMain function for a service that does not need a thread to do its work, such as a service that processes only RPC requests, should return control to the caller. It is important to return, rather than call the ExitThread_win32_ExitThread function, because returning allows for cleanup of the memory allocated for the arguments.
To output debugging information, MyServiceStart calls SvcDebugOut. The source code for SvcDebugOut is listed in Writing a Win32 Service Process's main Function_win32_Writing_a_Win32_Service_Process_s_main_Function.
void MyServiceStart (DWORD argc, LPTSTR *argv)
{
DWORD status;
DWORD specificError;

MyServiceStatus.dwServiceType = SERVICE_WIN32;
MyServiceStatus.dwCurrentState = SERVICE_START_PENDING;
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE;
MyServiceStatus.dwWin32ExitCode = 0;
MyServiceStatus.dwServiceSpecificExitCode = 0;
MyServiceStatus.dwCheckPoint = 0;
MyServiceStatus.dwWaitHint = 0;

MyServiceStatusHandle = RegisterServiceCtrlHandler(
TEXT("MyService"),
MyServiceCtrlHandler);

if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
SvcDebugOut(" [MY_SERVICE] RegisterServiceCtrlHandler failed %d\n", GetLastError());
return;
}

// Initialization code goes here.
status = MyServiceInitialization(argc,argv, &specificError);

// Handle error condition
if (status != NO_ERROR) {
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
MyServiceStatus.dwCheckPoint = 0;
MyServiceStatus.dwWaitHint = 0;
MyServiceStatus.dwWin32ExitCode = status;
MyServiceStatus.dwServiceSpecificExitCode = specificError;

SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
return;
}

// Initialization complete - report running status
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
MyServiceStatus.dwCheckPoint = 0;
MyServiceStatus.dwWaitHint = 0;

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
}

// This is where the service does its work. //
SvcDebugOut(" [MY_SERVICE] Returning the Main Thread \n",0);

return;
}

/* stub initialization function */
DWORD MyServiceInitialization(DWORD argc, LPTSTR *argv, DWORD *specificError)
{
argv;
argc;
specificError;
return(0);
}

+$#A Writing a Control Handler Function
The MyServiceCtrlHandler function in the following example is the service's Handler_win32_Handler function. When this function is called by the dispatcher thread, it handles the control code passed in the parameter Opcode and then calls the SetServiceStatus_win32_SetServiceStatus function to update the service's status. Every time a Handler function receives a control code, it is appropriate to return status with a call to SetServiceStatus regardless of whether the service operates on the control.
The Handler function executes in the context of the dispatcher thread. Therefore, this function should not perform time-consuming operations. If an operation such as a stop request is going to take a long time, the Handler function should send the SERVICE_STOP_PENDING message in a call to SetServiceStatus and then signal the other service thread(s), if necessary, that a stop operation is in progress. Then the function should return so that the dispatcher can service more requests. One of the other service threads becomes responsible for sending further wait hints and the final SERVICE_STOPPED message.
Note that when the pause control is received, MyServiceCtrlHandler simply sets the dwCurrentState field in the SERVICE_STATUS structure to SERVICE_PAUSED. Likewise, when the continue control is received, the state is set to SERVICE_RUNNING. Note that MyServiceCtrlHandler is not presented as an accurate example of how to handle the pause and continue controls. Since MyServiceCtrlHandler is a template for a Handler function, code for the pause and continue controls is included for completeness. A real service that supports either the pause or continue control should handle these controls in whatever way makes sense for that service. Many services support neither the pause or continue control. If the service indicates that it does not support pause or continue with the dwControlsAccepted parameter, then the service control manager will not send pause or continue controls to the service's Handler function.
When handling the stop request for a service that will take a long time, the current thread should create another thread or ask an existing one to handle the stop process. The current thread should simply call SetServiceStatus with the SERVICE_STOP_PENDING message and return to the caller.
To output debugging information, MyServiceCtrlHandler calls SvcDebugOut. The source code SvcDebugOut is listed in Writing a Win32 Service Process's main Function_win32_Writing_a_Win32_Service_Process_s_main_Function.
VOID MyServiceCtrlHandler ( IN DWORD Opcode)
{

DWORD status;

switch(Opcode) {
case SERVICE_CONTROL_PAUSE:
/* Do whatever it takes to pause here.
MyServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;

case SERVICE_CONTROL_CONTINUE:
/* Do whatever it takes to continue here.
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;

case SERVICE_CONTROL_STOP:
/* Do whatever it takes to stop here.
MyServiceStatus.dwWin32ExitCode = 0;
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
MyServiceStatus.dwCheckPoint = 0;
MyServiceStatus.dwWaitHint = 0;

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
}

SvcDebugOut(" [MY_SERVICE] Leaving MyService \n",0);
return;

case SERVICE_CONTROL_INTERROGATE:
/* fall through to send current status
break;

default:
SvcDebugOut(" [MY_SERVICE] Unrecognized opcode %ld\n", Opcode);
}

/* Send current status.
if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
}
return;
}

+$#A Opening an SCManager Database
The examples in the following sections require an open handle to an SCManager database object. Different operations on a service control manager database require different levels of access, and you should only request the minimum access required. If SC_MANAGER_ALL_ACCESS is requested, the OpenSCManager_win32_OpenSCManager function fails if the calling process does not have administrator privileges. The following example shows how to request full access to the ServicesActive database on the local machine.
/* Open a handle to the SC Manager database. */

schSCManager = OpenSCManager(
NULL, /* local machine */
NULL, /* ServicesActive database */
SC_MANAGER_ALL_ACCESS); /* full access rights */

if (schSCManager == NULL)
MyErrorExit("OpenSCManager");

+$#A Installing a Service
A service configuration program uses the CreateService_win32_CreateService function to install a service in a service control manager database. The application-defined schSCManager handle must have SC_MANAGER_CREATE_SERVICE access to the SCManager object. The following example shows how to install a service.
VOID CreateSampleService() {
LPCTSTR lpszBinaryPathName =
TEXT("%SystemRoot%\\system\\testserv.exe");

schService = CreateService(
schSCManager, /* SCManager database */
TEXT("Sample_Srv"), /* name of service */
lpszDisplayName, /* service name to display */
SERVICE_ALL_ACCESS, /* desired access */
SERVICE_WIN32_OWN_PROCESS, /* service type */
SERVICE_DEMAND_START, /* start type */
SERVICE_ERROR_NORMAL, /* error control type */
lpszBinaryPathName, /* service's binary */
NULL, /* no load ordering group */
NULL, /* no tag identifier */
NULL, /* no dependencies */
NULL, /* LocalSystem account */
NULL); /* no password */

if (schService == NULL)
MyErrorExit("CreateService");
else
printf("CreateService SUCCESS\n");

CloseServiceHandle(schService);

}

+$#A Deleting a Service
In the following example, a service configuration program uses the OpenService_win32_OpenService function to get a handle with DELETE access to an installed service object. The program then uses the service object handle in the DeleteService_win32_DeleteService function to remove the service from the service control manager database.
VOID DeleteSampleService() {

schService = OpenService(
schSCManager, /* SCManager database */
TEXT("Sample_Srv"), /* name of service */
DELETE); /* only need DELETE access */

if (schService == NULL)
MyErrorExit("OpenService");

if (! DeleteService(schService) )
MyErrorExit("DeleteService");
else
printf("DeleteService SUCCESS\n");

CloseServiceHandle(schService);

}

+$#A Changing a Service Configuration
In the following example, a service configuration program uses the ChangeServiceConfig_win32_ChangeServiceConfig function to change the configuration parameters of an installed service. The program first tries to lock the database, to prevent the service control manager from starting a service while it is being reconfigured. If it successfully locks the database, the program opens a handle to the service object, modifies its configuration, unlocks the database, and then closes the service object handle. If the program does not successfully in lock the database, it uses the QueryServiceLockStatus_win32_QueryServiceLockStatus function to retrieve information about the lock.
VOID ReconfigureSampleService(BOOL fDisable) {

SC_LOCK sclLock;
LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
DWORD dwBytesNeeded, dwStartType;

/* Need to acquire database lock before reconfiguring. */

sclLock = LockServiceDatabase(schSCManager);

/* If the database cannot be locked, report the details. */

if (sclLock == NULL) {

/* Exit if the database is not locked by another process. */

if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED)
MyErrorExit("LockServiceDatabase");

/* Allocate a buffer to get details about the lock. */

lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(
LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
if (lpqslsBuf == NULL)
MyErrorExit("LocalAlloc");

/* Get and print the lock status information. */

if (! QueryServiceLockStatus(
schSCManager,
lpqslsBuf,
sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
&dwBytesNeeded) )
MyErrorExit("QueryServiceLockStatus");

if (lpqslsBuf->fIsLocked)
printf("Locked by: %s, duration: %d seconds\n",
lpqslsBuf->lpLockOwner,
lpqslsBuf->dwLockDuration);
else
printf("No longer locked\n");

LocalFree(lpqslsBuf);
MyErrorExit("Could not lock database");
}

/* The database is locked, so it is safe to make changes. */

/* Open a handle to the service. */

schService = OpenService(
schSCManager, /* SCManager database */
TEXT("Sample_Srv"), /* name of service */
SERVICE_CHANGE_CONFIG); /* need CHANGE access */
if (schService == NULL)
MyErrorExit("OpenService");

dwStartType = (fDisable) ? SERVICE_DISABLED :
SERVICE_DEMAND_START;

if (! ChangeServiceConfig(
schService, /* handle of service */
SERVICE_NO_CHANGE, /* service type: no change */
dwStartType, /* *change* service start type */
SERVICE_NO_CHANGE, /* error control: no change */
NULL, /* binary path: no change */
NULL, /* load order group: no change */
NULL, /* tag ID: no change */
NULL, /* dependencies: no change */
NULL, /* account name: no change */
NULL) ) /* password: no change */
MyErrorExit("ChangeServiceConfig");
else
printf("ChangeServiceConfig SUCCESS\n");

/* Release the database lock. */

UnlockServiceDatabase(sclLock);

/* Close the handle to the service. */

CloseServiceHandle(schService);

}

+$#A Querying a Service's Configuration
In the following example, a service configuration program uses the OpenService_win32_OpenService function to get a handle with SERVICE_QUERY_CONFIG access to an installed service object. Then the program uses the service object handle in the QueryServiceConfig_win32_QueryServiceConfig function to retrieve the current configuration of the service.
VOID GetSampleServiceConfig() {

LPQUERY_SERVICE_CONFIG lpqscBuf;
DWORD dwBytesNeeded;

/* Open a handle to the service. */

schService = OpenService(
schSCManager, /* SCManager database */
TEXT("Sample_Srv"), /* name of service */
SERVICE_QUERY_CONFIG); /* need QUERY access */
if (schService == NULL)
MyErrorExit("OpenService");

/* Allocate a buffer for the information configuration. */

lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(
LPTR, 4096);
if (lpqscBuf == NULL)
MyErrorExit("LocalAlloc");

/* Get and print the information configuration. */

if (! QueryServiceConfig(
schService,
lpqscBuf,
4096,
&dwBytesNeeded) )
MyErrorExit("QueryServiceConfig");

printf("\nSample_Srv configuration: \n");
printf(" Type: 0x%x\n", lpqscBuf->dwServiceType);
printf(" Start Type: 0x%x\n", lpqscBuf->dwStartType);
printf(" Err Control: 0x%x\n", lpqscBuf->dwErrorControl);
printf(" Binary path: %s\n", lpqscBuf->lpBinaryPathName);
if (lpqscBuf->lpLoadOrderGroup != NULL)
printf(" Load order group: %s\n",
lpqscBuf->lpLoadOrderGroup);
if (lpqscBuf->dwTagId != 0)
printf(" Tag ID: %d\n", lpqscBuf->dwTagId);
if (lpqscBuf->lpDependencies != NULL)
printf(" Dependencies: %s\n", lpqscBuf->lpDependencies);
if (lpqscBuf->lpServiceStartName != NULL)
printf(" Start Name: %s\n",
lpqscBuf->lpServiceStartName);

LocalFree(lpqscBuf);

}

+$#A Starting a Service
To start a service, the following example opens a handle to an installed database and then specifies the handle in a call to the StartService_win32_StartService function. StartService can be used to start either a Win32 service or a driver service, but this example assumes that a Win32 service is being started. After starting the service, the program uses the members of the SERVICE_STATUS_win32_SERVICE_STATUS_str structure returned by the QueryServiceStatus_win32_QueryServiceStatus function to track the progress of the Win32 service.
VOID StartSampleService() {
SERVICE_STATUS ssStatus;
DWORD dwOldCheckPoint;

schService = OpenService(
schSCManager, /* S. C. Manager database */
TEXT("Sample_Srv"), /* name of service */
SERVICE_ALL_ACCESS);

// SERVICE_START | /* need START access */
// SERVICE_QUERY_CONFIG); /* and QUERY access */

if (schService == NULL)
MyErrorExit("OpenService");

if (! StartService(schService, /* handle of service */
0, /* number of arguments */
NULL) ) /* no arguments */
MyErrorExit("StartService");
else
printf("Service start pending\n");

/* Check the status until the service is running. */

if (! QueryServiceStatus(
schService, /* handle of service */
&ssStatus) ) /* address of status info */
MyErrorExit("QueryServiceStatus");

while (ssStatus.dwCurrentState != SERVICE_RUNNING) {

/* Save the current checkpoint. */

dwOldCheckPoint = ssStatus.dwCheckPoint;

/* Wait for the specified interval. */

Sleep(ssStatus.dwWaitHint);

/* Check the status again. */

if (! QueryServiceStatus(
schService, /* handle of service */
&ssStatus) ) /* address of status info */
break;

/* Break if the checkpoint has not been incremented. */

if (dwOldCheckPoint >= ssStatus.dwCheckPoint)
break;

}

if (ssStatus.dwCurrentState == SERVICE_RUNNING)
printf("StartService SUCCESS\n");
else {
printf("\nService not started: \n");
printf(" Current State: %d\n",
ssStatus.dwCurrentState);
printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf(" Service Specific Exit Code: %d\n",
ssStatus.dwServiceSpecificExitCode);
printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);
}

CloseServiceHandle(schService);

}

+$#A Sending Control Requests to a Service
The following example uses the ControlService_win32_ControlService function to send a control value to a running service. Different control values require different levels of access to the service object. For example, a service object handle must have SERVICE_STOP access to send the SERVICE_CONTROL_STOP code. When ControlService returns, a SERVICE_STATUS_win32_SERVICE_STATUS_str structure contains the latest status information for the service.
VOID ControlSampleService(DWORD fdwControl) {

SERVICE_STATUS ssStatus;
DWORD fdwAccess;

/* The required service object access depends on the control. */

switch (fdwControl) {
case SERVICE_CONTROL_STOP:
fdwAccess = SERVICE_STOP;
break;

case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
fdwAccess = SERVICE_PAUSE_CONTINUE;
break;

case SERVICE_CONTROL_INTERROGATE:
fdwAccess = SERVICE_INTERROGATE;
break;

default:
fdwAccess = SERVICE_INTERROGATE;
}

/* Open a handle to the service. */

schService = OpenService(
schSCManager, /* SCManager database */
TEXT("Sample_Srv"), /* name of service */
fdwAccess); /* specify access */
if (schService == NULL)
MyErrorExit("OpenService");

/* Send a control value to the service. */

if (! ControlService(
schService, /* handle of service */
fdwControl, /* control value to send */
&ssStatus) ) /* address of status info */
MyErrorExit("ControlService");

/* Print the service status. */

printf("\nStatus of Sample_Srv: \n");
printf(" Service Type: 0x%x\n", ssStatus.dwServiceType);
printf(" Current State: 0x%x\n", ssStatus.dwCurrentState);
printf(" Controls Accepted: 0x%x\n",
ssStatus.dwControlsAccepted);
printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf(" Service Specific Exit Code: %d\n",
ssStatus.dwServiceSpecificExitCode);
printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);

return;
}

+$#A Service Functions
Following are the Win32 functions used by services and by programs that control or configure services:
ChangeServiceConfig_win32_ChangeServiceConfigCloseServiceHandle_win32_CloseServiceHandleControlService_win32_ControlServiceCreateService_win32_CreateServiceDeleteService_win32_DeleteServiceEnumDependentServices_win32_EnumDependentServicesEnumServicesStatus_win32_EnumServicesStatusGetServiceDisplayName_win32_GetServiceDisplayNameGetServiceKeyName_win32_GetServiceKeyNameHandler_win32_HandlerLockServiceDatabase_win32_LockServiceDatabaseNotifyBootConfigStatus_win32_NotifyBootConfigStatusOpenSCManager_win32_OpenSCManagerOpenService_win32_OpenServiceQueryServiceConfig_win32_QueryServiceConfigQueryServiceLockStatus_win32_QueryServiceLockStatusQueryServiceObjectSecurity_win32_QueryServiceObjectSecurityQueryServiceStatus_win32_QueryServiceStatusRegisterServiceCtrlHandler_win32_RegisterServiceCtrlHandlerServiceMain_win32_ServiceMainSetServiceBits_win32_SetServiceBitsSetServiceObjectSecurity_win32_SetServiceObjectSecuritySetServiceStatus_win32_SetServiceStatusStartService_win32_StartServiceStartServiceCtrlDispatcher_win32_StartServiceCtrlDispatcherUnlockServiceDatabase_win32_UnlockServiceDatabase

+$#AK String Manipulation
The overview describes the string manipulation functions and explains how to use them in your Win32-based applications.
+$#A About String Manipulation
The string manipulation functions give applications developed for the Win32 application programming interface (API) the means to copy, compare, sort, format, and convert character strings as well as the means to determine the character type of each character in a string. All the string manipulation functions support the single-byte, double-byte, and Unicode character sets if these character sets are supported by the operating system on which the application is run.
+$#A String Manipulation in Windows
Many Windows functions duplicate or enhance familiar string-manipulation functions from the standard C library. Many of the enhancements enable Windows functions to work with Unicode or extended character sets. For example, the Windows functions in the following table are identical to standard C functions except that the Windows functions are enhanced for use with Unicode.
Windows function Standard C function
lstrcat_win32_lstrcat strcat
lstrcmp_win32_lstrcmp strcmp
lstrcmpi_win32_lstrcmpi strcmpi
lstrcpy_win32_lstrcpy strcpy
lstrlen_win32_lstrlen strlen

The strlen standard C function, for example, always returns the number of bytes in a string, but the lstrlen function returns the number of characters, regardless of whether the characters are 1 or 2 bytes wide.
The following Windows functions differ from standard C functions such as tolower and toupper in that they operate on any character in a character set. By using the CharLower function, for example, a Windows-based application can convert an uppercase U with an umlaut (Ü) to lowercase (ü). For more information about character sets, see Single-byte Character Sets_win32_Single_byte_Character_Sets.
Function Description
CharLower_win32_CharLower Converts a character or string to lowercase.
CharLowerBuff_win32_CharLowerBuff Converts a character string to lowercase.
CharNext_win32_CharNext Moves to the next character in a string.
CharPrev_win32_CharPrev Moves to the preceding character in a string.
CharUpper_win32_CharUpper Converts a character or string to uppercase.
CharUpperBuff_win32_CharUpperBuff Converts a string to uppercase.

The following Windows functions make determinations about a character based on the semantics of the language selected by the user. These functions are Unicode enabled.
Function Description
IsCharAlpha_win32_IsCharAlpha Determines whether a character is alphabetic.
IsCharAlphaNumeric_win32_IsCharAlphaNumeric Determines whether a character is alphanumeric.
IsCharLower_win32_IsCharLower Determines whether a character is lowercase.
IsCharUpper_win32_IsCharUpper Determines whether a character is uppercase.

The wsprintf_win32_wsprintf and wvsprintf_win32_wvsprintf functions are extensions to the standard C functions sprintf and vsprintf. The Windows versions support format specifications unique to Unicode_win32_Unicode.
+$#A String Resources
An application that maintains character strings in resources can be translated into new languages with minimum effort. Instead of searching for strings in the source modules, a developer can simply translate the strings in the resource file and relink the application. In addition, using string resources simplifies creation of Unicode and non-Unicode versions of the application from the same source files.
The LoadString_win32_LoadString function loads a string resource from an application's executable file. The FormatMessage_win32_FormatMessage function loads a string resource and interprets formatting options that may be embedded in the string.
Windows 32-bit resources in binary form are stored in Unicode format. When loading resources, applications can use the Unicode version of the resource functions (LoadStringW, for example) to obtain resources as Unicode data.
+$#A Obsolete Functions
The AnsiLower_win32_AnsiLower function is obsolete. Win32-based applications should use the CharLower_win32_CharLower function.
The AnsiLowerBuff_win32_AnsiLowerBuff function is obsolete. Win32-based applications should use the CharLowerBuff_win32_CharLowerBuff function.
The AnsiNext_win32_AnsiNext function is obsolete. Win32-based applications should use the CharNext_win32_CharNext function.
The AnsiPrev_win32_AnsiPrev function is obsolete. Win32-based applications should use the CharPrev_win32_CharPrev function.
The AnsiUpper_win32_AnsiUpper function is obsolete. Win32-based applications should use the CharUpper_win32_CharUpper function.
The AnsiUpperBuff_win32_AnsiUpperBuff function is obsolete. Win32-based applications should use the CharUpperBuff_win32_CharUpperBuff function.
OemToAnsi_win32_OemToAnsiOemToAnsiBuff_win32_OemToAnsiBuff

+$#A String Manipulation Reference
This section provides detailed reference information for the string manipulation functions, structures, and messages.
+$#A String Manipulation Functions
Following are the functions used with string manipulation.
CharLower_win32_CharLowerCharLowerBuff_win32_CharLowerBuffCharNext_win32_CharNextCharPrev_win32_CharPrevCharToOem_win32_CharToOemCharToOemBuff_win32_CharToOemBuffCharUpper_win32_CharUpperCharUpperBuff_win32_CharUpperBuffCompareString_win32_CompareStringConvertDefaultLocale_win32_ConvertDefaultLocaleEnumCalendarInfo_win32_EnumCalendarInfoEnumCalendarInfoProc_win32_EnumCalendarInfoProcEnumCodePagesProc_win32_EnumCodePagesProcEnumDateFormats_win32_EnumDateFormatsEnumDateFormatsProc_win32_EnumDateFormatsProcEnumLocalesProc_win32_EnumLocalesProcEnumSystemCodePages_win32_EnumSystemCodePagesEnumSystemLocales_win32_EnumSystemLocalesEnumTimeFormats_win32_EnumTimeFormatsEnumTimeFormatsProc_win32_EnumTimeFormatsProcFoldString_win32_FoldStringFormatMessage_win32_FormatMessageGetACP_win32_GetACPGetCPInfo_win32_GetCPInfoGetCurrencyFormat_win32_GetCurrencyFormatGetDateFormat_win32_GetDateFormatGetNumberFormat_win32_GetNumberFormatGetLocaleInfo_win32_GetLocaleInfoGetOEMCP_win32_GetOEMCPGetStringTypeA_win32_GetStringTypeAGetStringTypeEx_win32_GetStringTypeExGetStringTypeW_win32_GetStringTypeWGetSystemDefaultLangID_win32_GetSystemDefaultLangIDGetSystemDefaultLCID_win32_GetSystemDefaultLCIDGetThreadLocale_win32_GetThreadLocaleGetTimeFormat_win32_GetTimeFormatGetUserDefaultLangID_win32_GetUserDefaultLangIDGetUserDefaultLCID_win32_GetUserDefaultLCIDIsCharAlpha_win32_IsCharAlphaIsCharAlphaNumeric_win32_IsCharAlphaNumericIsCharLower_win32_IsCharLowerIsCharUpper_win32_IsCharUpperIsDBCSLeadByte_win32_IsDBCSLeadByteIsTextUnicode_win32_IsTextUnicodeIsValidCodePage_win32_IsValidCodePageIsValidLocale_win32_IsValidLocaleLCMapString_win32_LCMapStringLoadString_win32_LoadStringlstrcat_win32_lstrcatlstrcmp_win32_lstrcmplstrcmpi_win32_lstrcmpilstrcpy_win32_lstrcpylstrcpyn_win32_lstrcpynlstrlen_win32_lstrlenMultiByteToWideChar_win32_MultiByteToWideCharOemToChar_win32_OemToCharOemToCharBuff_win32_OemToCharBuffSetLocaleInfo_win32_SetLocaleInfoSetThreadLocale_win32_SetThreadLocaleWideCharToMultiByte_win32_WideCharToMultiBytewsprintf_win32_wsprintfwvsprintf_win32_wvsprintf

+$#A String Manipulation Constants
This section describes the constants used with the string manipulation functions.
+$#A Character Type Information Values
The character-type bits are divided into several levels. The information for one level can be retrieved by a single call to this function. Each level is limited to 16 bits of information so that the other mapping routines limited to 16 bits of representation per character can return character-type information.
The character types supported by this function include the following.
Ctype 1: These types support ANSI C and POSIX (LC_CTYPE) character-typing functions. A combination of these values is returned in the array pointed to by the lpCharType parameter when the fdwInfoType parameter is set to CT_CTYPE1.
Name Value Meaning
C1_UPPER 0x0001 Uppercase
C1_LOWER 0x0002 Lowercase
C1_DIGIT 0x0004 Decimal digits
C1_SPACE 0x0008 Space characters
C1_PUNCT 0x0010 Punctuation
C1_CNTRL 0x0020 Control characters
C1_BLANK 0x0040 Blank characters
C1_XDIGIT 0x0080 Hexadecimal digits
C1_ALPHA 0x0100 Any letter

The following character types are either constant or computable from basic types and do not need to be supported by this function.
Type Description
Alphanumeric Alphabetic characters and digits
Printable Graphic characters and blank

Ctype 2: These types support proper layout of Unicode™ text. The direction attributes are assigned so that the bidirectional layout algorithm standardized by Unicode produces accurate results. These types are mutually exclusive. For more information about the use of these attributes, see The Unicode Standard: Worldwide Character Encoding, Volumes 1 and 2, Addison Wesley Publishing Company: 1991, 1992, ISBN 0201567881.
Name Value Meaning
Strong:
C2_LEFTTORIGHT 0x1 Left to right
C2_RIGHTTOLEFT 0x2 Right to left
Weak:
C2_EUROPENUMBER 0x3 European number, European digit
C2_EUROPESEPARATOR 0x4 European numeric separator
C2_EUROPETERMINATOR 0x5 European numeric terminator
C2_ARABICNUMBER 0x6 Arabic number
C2_COMMONSEPARATOR 0x7 Common numeric separator
Neutral:
C2_BLOCKSEPARATOR 0x8 Block separator
C2_SEGMENTSEPARATOR 0x9 Segment separator
C2_WHITESPACE 0xA White space
C2_OTHERNEUTRAL 0xB Other neutrals
Not applicable:
C2_NOTAPPLICABLE 0x0 No implicit directionality (for example, control codes)

Ctype 3: These types are intended to be placeholders for extensions to the POSIX types required for general text processing or for the standard C library functions. These types are supported in the current version of Microsoft® Windows NT™. A combination of these values is returned when fdwInfoType is set to CT_CTYPE3.
Name Value Meaning
C3_NONSPACING 0x1 Nonspacing mark
C3_DIACRITIC 0x2 Diacritic nonspacing mark
C3_VOWELMARK 0x4 Vowel nonspacing mark
C3_SYMBOL 0x8 Symbol
C3_KATAKANA 0x10 Katakana character
C3_HIRAGANA 0x20 Hiragana character
C3_HALFWIDTH 0x40 Half Width character
C3_FULLWIDTH 0x80 Full Width character
C3_IDEOGRAPH 0x100 Ideographic character
C3_KASHIDA 0x200 Arabic Kashida character
C3_ALPHA 0x8000 All linguistic characters (alphabetic, syllabary and ideographic)
Not applicable:
C3_NOTAPPLICABLE 0x0 Not applicable

+$#A Format Specifications
%[-][#][0][width][.precision]type
Format specifications always begin with a percent sign (%). Each field is a single character or a number signifying a particular format option. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting.
%
Required. Marks the beginning of the format specification. If the percent sign is followed by a character that has no meaning as a format field, the character is not formatted (for example, %% produces a single percent-sign character).
-
Optional. Pads the output with blanks or zeros to the right to fill the field width, justifying output to the left. If this field is omitted, the output is padded to the left, justifying it to the right.
#
Optional. Prefix hexadecimal values with 0x (lowercase) or 0X (uppercase).
0
Optional. Pad the output value with zeros to fill the field width. If this field is omitted, the output value is padded with blank spaces.
width
Optional. Copy the specified minimum number of characters to the output buffer. The width field is a nonnegative integer. The width specification never causes a value to be truncated; if the number of characters in the output value is greater than the specified width, or if the width field is not present, all characters of the value are printed, subject to the precision specification.
.precision
Optional. For numbers, copy the specified minimum number of digits to the output buffer. If the number of digits in the argument is less than the specified precision, the output value is padded on the left with zeros. The value is not truncated when the number of digits exceeds the specified precision. If the specified precision is 0 or omitted entirely, or if the period (.) appears without a number following it, the precision is set to 1.
For strings, copy the specified maximum number of characters to the output buffer.
type
Required. Determines whether the associated argument is interpreted as a character, a string, or a number. Can be one of these character sequences:
c A single character. The functions ignore character arguments with a numeric value of zero. This sequence is interpreted as type WCHAR when the calling application uses the #define UNICODE compile flag and as type CHAR otherwise.
C A single character. This sequence is interpreted as type CHAR when the calling application uses the #define UNICODE compile flag and as type WCHAR otherwise.
d A signed decimal integer argument. This sequence is equivalent to the i sequence.
hc, hC A single character. The functions ignore character arguments with a numeric value of zero. This sequence is always interpreted as type CHAR, even when the calling application uses the #define UNICODE compile flag.
hs, hS A string. This sequence is always interpreted as type LPSTR, even when the calling application uses the #define UNICODE compile flag.
i A signed decimal integer. This sequence is equivalent to the d sequence.
lc, lC A single character. The functions ignore character arguments with a numeric value of zero. This sequence is always interpreted as type WCHAR, even when the calling application does not use the #define UNICODE compile flag.
ld A long signed decimal integer. This sequence is equivalent to the li sequence.
li A long signed decimal integer. This sequence is equivalent to the ld sequence.
ls, lS A string. This sequence is always interpreted as type LPWSTR, even when the calling application does not use the #define UNICODE compile flag. This sequence is equivalent to the ws sequence.
lu A long unsigned integer.
lx, lX A long unsigned hexadecimal integer in lowercase or uppercase.
s A string. This sequence is interpreted as type LPWSTR when the calling application uses the #define UNICODE compile flag and as type LPSTR otherwise.
S A string. This sequence is interpreted as type LPSTR when the calling application uses the #define UNICODE compile flag and as type LPWSTR otherwise.
u An unsigned integer argument.
x, X An unsigned hexadecimal integer in lowercase or uppercase.

+$#AK Structured Exception Handling
An exception is an event that occurs during the execution of a program, and that requires the execution of software outside the normal flow of control. Hardware exceptions can result from the execution of certain instruction sequences, such as division by zero or an attempt to access an invalid memory address. A software routine can also initiate an exception explicitly.
The Microsoft® Win32® application programming interface (API) supports structured exception handling, a mechanism for handling hardware- and software-generated exceptions.
+$#A About Structured Exception Handling
Structured exception handling gives developers complete control over the handling of exceptions, provides support for debuggers, and is usable across all programming languages and machines.
The Win32 API also supports termination handling, which enables developers to ensure that whenever a guarded body of code is executed, a specifie d block of termination code is also executed. The termination code is executed regardless of how the flow of control leaves the guarded body. For example, a termination handler can guarantee that clean-up tasks are performed even if an exception or some other error occurs while the guarded body of code is being executed.
Structured exception and termination handling is an integral part of the Win32 API; it enables a robust implementation of the system software. Developers can use these mechanisms to create consistently robust and reliable applications.
Structured exception handling is made available to developers primarily through compiler support. For example, the Microsoft compilers provided with the Win32 software development kit support the try keyword that identifies a guarded body of code, and the except and finally keywords that identify an exception handler and a termination handler, respectively. Although this topic uses examples from the support available in Microsoft compilers, other compiler vendors can provide this support as well.
+$#A Exception Handling
Exceptions can be initiated by hardware or software, and can occur in kernel-mode as well as user-mode code. Win32-based structured exception handling provides a single mechanism for the handling of kernel-mode and user-mode exceptions, both hardware- and software-generated.
The execution of ce rtain instruction sequences can result in exceptions that are initiated by hardware. For example, an access violation is generated by the hardware when a process attempts to read from or write to a virtual address to which it does not have the appropriate access.
Events that require exception handling may also occur during execution of a software routine (for example, when an invalid parameter value is specified). When this happens, a thread can initiate an exception explicitly by calling the RaiseException_win32_RaiseException function. This function enables the calling thread to specify information that describes the exception.
+$#A Exception Dispatching
When a hardware or software exception occurs, the processor stops execution at the point at which the exception occurred and transfers control to the system. First, the system saves both the machine state of the current thread and information that describes the exception. The system then attempts to find an exception handler to handle the exception.
The machine state of the thread in which the exception occurred is saved in a CONTEXT_win32_CONTEXT_str structure. This information (called the context record) enables the system to continue execution at the point of the exception if the exception is successfully handled. The description of the exception (called the exception record) is saved in an EXCEPTION_RECORD_win32_EXCEPTION_RECORD_str structure. Because it stores the machine-dependent information of the context record separately from the machine-independent information of the exception record, the exception-handling mechanism is portable to different platforms. The information in both the context and exception records is available by means of the GetExceptionInformation_win32_GetExceptionInformation function, and can be made available to any exception handlers that are executed as a result of the exception. The exception record includes the following information.
· An exception code that identifies the type of exception.
· Flags indicating whether the exception is continuable. Any attempt to continue execution after a noncontinuable exception generates another exception.
· A pointer to another exception record. This facilitates creation of a linked list of exceptions if nested exceptions occur.
· The address at which the exception occurred.
· An array of 32-bit arguments that provide additional information about the exception.

When an exception occurs in user-mode code, the system goes through the following search for an exception handler:
1. The system first attempts to notify the process's debugger, if any.
2. If the process is not being debugged, or if the associated debugger does not handle the exception, the system attempts to locate a frame-based exception handler by searching the stack frames of the thread in which the exception occurred. The system searches the current stack frame first, then proceeds backward through preceding stack frames.
3. If no frame-based handler can be found, or no frame-based handler handles the exception, the system makes a second attempt to notify the process's debugger.
4. If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. For most exceptions, the default action is to call the ExitProcess_win32_ExitProcess function.

When an exception occurs in kernel-mode code, the system searches the stack frames of the kernel stack in an attempt to locate an exception handler. If a handler cannot be located or no handler handles the exception, the system is shut down as if the ExitWindows_win32_ExitWindows function had been called.
+$#A Debugger Support
The system's handling of user-mode exceptions provides support for sophisticated debuggers. If the process in which an exception occurs is being debugged, the system generates a debug event. If the debugger is using the WaitForDebugEvent_win32_WaitForDebugEvent function, the debug event causes that function to return with a pointer to a DEBUG_EVENT_win32_DEBUG_EVENT_str structure. This structure contains the process and thread identifiers that the debugger can use to access the thread's context record. The structure also contains an EXCEPTION_DEBUG_INFO_win32_EXCEPTION_DEBUG_INFO_str structure that includes a copy of the exception record.
The system's search for an exception handler includes two attempts to notify a process's debugger. The first notification attempt provides the debugger with an opportunity to handle breakpoint or single-step exceptions. The user can then issue debugger commands to manipulate the process's environment before any exception handlers are executed. The second attempt to notify the debugger occurs only if the system is unable to find a frame-based exception handler that handles the exception.
At each notification attempt, the debugger uses the ContinueDebugEvent_win32_ContinueDebugEvent function to return control to the system. Before returning control, the debugger can handle the exception and modify the thread state as appropriate, or it can choose not to handle the exception. Using ContinueDebugEvent, the debugger can indicate that it has handled the exception, in which case the machine state is restored and thread execution is continued at the point at which the exception occurred. The debugger can also indicate that it did not handle the exception, which causes the system to continue its search for an exception handler.
+$#A Frame-based Exception Handling
A frame-based exception handler is a mechanism by which a developer deals with the possibility that an exception may occur in a certain sequence of code. A frame-based exception handler consists of the following elements.
· A guarded body of code
· A filter expression
· An exception-handler block

Frame-based exception handlers are declared in language-specific syntax. For example, in the Microsoft® C Optimizing Compiler, they are implemented by the try-except statement. For more information about the Microsoft C exception-handling syntax, see Syntax_win32_Syntax.
The guarded body of code is a set of one or more statements for which the filter expression and the exception-handler block provide exception-handling protection. The guarded body can be a block of code, a set of nested blocks, or an entire procedure or function. In Microsoft C, a guarded body is enclosed by braces ({}) following the try keyword.
The filter expression of a frame-based exception handler is an expression that is evaluated by the system when an exception occurs within the guarded body. This evaluation results in one of the following actions by the system.
· The system stops its search for an exception handler, restores the machine state, and continues thread execution at the point at which the exception occurred.
· The system continues its search for an exception handler.
· The system transfers control to the exception handler, and thread execution continues sequentially in the stack frame in which the exception handler is found. If the handler is not in the stack frame in which the exception occurred, the system unwinds the stack, leaving the current stack frame and any other stack frames until it is back to the exception handler's stack frame. Before an exception handler is executed, termination handlers are executed for any guarded bodies of code that terminated as a result of the transfer of control to the exception handler. For more information about termination handlers, refer to Termination Handling_win32_Termination_Handling.

The filter expression can be a simple expression, or it can invoke a filter function that attempts to handle the exception. You can call the GetExceptionCode_win32_GetExceptionCode and GetExceptionInformation_win32_GetExceptionInformation functions from within a filter expression to get information about the exception being filtered. GetExceptionCode returns a code that identifies the type of exception, and GetExceptionInformation returns a pointer to an EXCEPTION_POINTERS_win32_EXCEPTION_POINTERS_str structure that contains pointers to CONTEXT_win32_CONTEXT_str and EXCEPTION_RECORD_win32_EXCEPTION_RECORD_str structures.
These functions cannot be called from within a filter function, but their return values can be passed as parameters to a filter function. GetExceptionCode can be used within the exception-handler block, but GetExceptionInformation cannot because the information it points to is typically on the stack and is destroyed when control is transferred to an exception handler. However, an application can copy the information to safe storage to make it available to the exception handler.
The advantage of a filter function is that it can handle an exception and return a value that causes the system to continue execution from the point at which the exception occurred. With an exception-handler block, in contrast, execution continues sequentially from the exception handler rather than from the point of the exception.
Handling an exception may be as simple as noting an error and setting a flag that will be examined later, printing a warning or error message, or taking some other limited action. If execution can be continued, it may also be necessary to change the machine state by modifying the context record. For an example of a filter function that handles a page fault exception, see Memory Management_win32_Memory_Management.
The UnhandledExceptionFilter_win32_UnhandledExceptionFilter function can be used as a filter function in a filter expression. It returns EXCEPTION_EXECUTE_HANDLER unless the process is being debugged, in which case it returns EXCEPTION_CONTINUE_SEARCH.
+$#A Termination Handling
A termination handler is a mechanism by which a developer ensures that a specific block of code is executed whenever flow of control leaves a particular guarded body of code. A termination handler consists of the following elements.
· A guarded body of code
· A block of termination code to be executed when the flow of control leaves the guarded body

Termination handlers are declared in language-specific syntax. In Microsoft C, they are implemented by the try-finally statement. For more information about the Microsoft C exception-handling syntax, see Syntax_win32_Syntax.
The guarded body of code can be a block of code, a set of nested blocks, or an entire procedure or function. Whenever the guarded body is executed, the block of termination code will be executed. The only exception to this is when the thread terminates during execution of the guarded body (for example, if the ExitThread_win32_ExitThread or ExitProcess_win32_ExitProcess function is called from within the guarded body).
The termination block is executed when the flow of control leaves the guarded body, regardless of whether the guarded body terminated normally or abnormally. The guarded body is considered to have terminated normally when the last statement in the block is executed and control proceeds sequentially into the termination block. Abnormal termination occurs when the flow of control leaves the guarded body due to an exception, or due to a control statement such as return, goto, break, or continue. The AbnormalTermination_win32_AbnormalTermination function can be called from within the termination block to determine whether the guarded body terminated normally.
+$#A Syntax
This section describes the syntax and usage of structured exception handling as implemented in Microsoft C. The following keywords are interpreted by the Microsoft C compiler as part of the structured exception-handling mechanism.
Keyword Description
try Begins a guarded body of code. Used with the except keyword to construct an exception handler, or with the finally keyword to construct a termination handler.
except Begins a block of code that is executed only when an exception occurs within its associated try block.
finally Begins a block of code that is executed whenever the flow of control leaves its associated try block.

The compiler also interprets the GetExceptionCode_win32_GetExceptionCode, GetExceptionInformation_win32_GetExceptionInformation, and AbnormalTermination_win32_AbnormalTermination functions as keywords, and their use outside the appropriate exception-handling syntax generates a compiler error. Following are brief descriptions of these functions.
Function Description
GetExceptionCode_win32_GetExceptionCode Returns a code that identifies the type of exception. This function can be called only from within the filter expression or exception-handler block of a try-except exception handler.
GetExceptionInformation_win32_GetExceptionInformation Returns a pointer to an EXCEPTION_POINTERS_win32_EXCEPTION_POINTERS_str structure containing pointers to the context record and the exception record. This function can be called only from within the filter expression of a try-except exception handler.
AbnormalTermination_win32_AbnormalTermination Indicates whether the flow of control left the associated try block sequentially after executing the last statement in the block. This function can be called only from within the finally block of a try-finally termination handler.

+$#A Exception-Handler Syntax
The try and except keywords are used to construct a frame-based exception handler. The following example shows the structure of a try-except exception handler.
try {

// guarded body of code

}
except (filter-expression) {

// exception-handler block

}

Note that the try block and the exception-handler block require braces ({}). Using a goto statement to jump into the body of a try block or into an exception-handler block is not permitted. This rule applies to both try-except and try-finally statements.
The try block contains the guarded body of code that the exception handler protects. A function can have any number of try-except statements, and these exception-handling statements can be nested within the same function or in different functions. If an exception occurs within the try block, the system takes control and begins the search for an exception handler. For a detailed description of this search, see Exception Handling_win32_Exception_Handling.
The exception handler receives only exceptions that occur within a single thread. This means that if a try block contains a call to the CreateProcess_win32_CreateProcess or CreateThread_win32_CreateThread function, exceptions that occur within the new process or thread are not dispatched to this handler.
The system evaluates the filter expression of each exception handler guarding the code in which the exception occurred until either the exception is handled or there are no more handlers. A filter expression must be evaluated as one of the three following values.
Value Meaning
EXCEPTION_EXECUTE_HANDLER
The system transfers control to the exception handler, and execution continues in the stack frame in which the handler is found.
EXCEPTION_CONTINUE_SEARCH
The system continues to search for a handler.
EXCEPTION_CONTINUE_EXECUTION
The system stops its search for a handler and returns control to the point at which the exception occurred. If the exception is noncontinuable, this results in a EXCEPTION_NONCONTINUABLE_EXCEPTION exception.

The filter expression is evaluated in the context of the function in which the try-except statement is located, even though the exception may have occurred in a different function. This means that the filter expression can access the function's local variables. Similarly, the exception-handler block can access the local variables of the function in which it is located.
For more information about filter expressions and filter functions, see Frame-based Exception Handling_win32_Frame.2d.based_Exception_Handling.
+$#A Termination-Handler Syntax
The try and finally keywords are used to construct a termination handler. The following example shows the structure of a try-finally termination handler.
try {

// guarded body of code

}
finally {

// finally block

}

As with the try-except statement, both the try block and the finally block require braces ({}), and using a goto statement to jump into either block is not permitted.
The try block contains the guarded body of code that is protected by the termination handler. A function can have any number of try-finally statements, and these termination handling structures can be nested within the same function or in different functions.
The finally block is executed whenever the flow of control leaves the try block. However, the finally block is not executed if either the ExitProcess_win32_ExitProcess or ExitThread_win32_ExitThread function is called from within the try block.
If execution of the try block terminates because of an exception that invokes the exception-handling block of a frame-based exception handler, the finally block is executed before the exception-handling block is executed. Similarly, a call to the standard C longjmp function from the try block causes execution of the finally block before execution resumes at the target of the longjmp operation. If try block execution terminates due to a normal control statement (return, break, continue, or goto), the finally block is executed.
The AbnormalTermination_win32_AbnormalTermination function can be used within the finally block to determine whether the try block terminated sequentially ¾ that is, whether it reached the closing brace (}). Leaving the try block because of a call to longjmp, a jump to an exception handler, or a return, break, continue, or goto statement, is considered an abnormal termination. Note that failure to terminate sequentially causes the system to search backward through all stack frames to determine whether any termination handlers must be called. This can result in performance degradation due to the execution of hundreds of instructions.
Execution of the finally block can terminate by any of the following means.
· Execution of the last statement in the block and continuation to the next instruction
· Use of a normal control statement (return, break, continue, or goto)
· Use of longjmp or a jump to an exception handler

The finally block is executed in the context of the function in which the try-finally statement is located. This means that the finally block can access that function's local variables.
+$#A Using Structured Exception Handling
· Using an Exception Handler_win32_Using_an_Exception_Handler
· Using a Termination Handler_win32_Using_a_Termination_Handler

+$#A Using an Exception Handler
The following example shows a version of the standard C strcpy function that can handle an invalid pointer and return NULL if one is encountered.
Note that this exception handler is intended to handle an access-violation exception, but is inappropriate if some other type of exception occurs. Therefore, the filter expression in the example uses the GetExceptionCode_win32_GetExceptionCode function to check the exception type before executing the handler. This enables the system to continue its search for an appropriate handler if some other type of exception occurs.
Note also that there is no problem with using a return statement in the try block of a try-except statement. This differs from the use of return in the try block of a try-finally statement, which causes an abnormal termination of the try block.
LPTSTR SafeStrcpy(LPTSTR lpszString1, LPTSTR lpszString2) {
try {
return strcpy(string1, string2);
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH ) {
return NULL;
}
}

The following example shows the interaction of nested try-finally and try-except statements. The RaiseException_win32_RaiseException function causes an exception in the guarded body of a try-finally termination handler that is inside the guarded body of a try-except exception handler. The exception causes the system to evaluate the FilterFunction function, whose return value in turn causes the exception handler to be invoked. However, before the exception-handler block is executed, the finally block of the termination handler is executed because the flow of control has left the try block of the termination handler.

DWORD FilterFunction() {
printf("1 "); // printed first
return EXCEPTION_EXECUTE_HANDLER;
}

VOID main(VOID) {
try {
try {
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments
}
finally {
printf("2 ") // this is printed second
}
}
except ( FilterFunction() ) {
printf("3\n"); // this is printed last
}
}

+$#A Using a Termination Handler
The following example shows how a try-finally statement is used to ensure that resources are released when execution of a guarded body of code terminates. In this case, a thread uses the EnterCriticalSection_win32_EnterCriticalSection function to wait for ownership of a critical section object. When the thread is finished executing the code that is protected by the critical section, it must call the LeaveCriticalSection_win32_LeaveCriticalSection function to make the critical section object available to other threads. Using a try-finally statement guarantees that this will happen.
For more information about critical section objects, see Synchronization_win32_Synchronization.
LPTSTR lpBuffer = NULL;
CRITICAL_SECTION csCriticalSection;

try {

// EnterCriticalSection synchronizes code
// with other threads.

EnterCriticalSection(&CriticalSection);

// Perform a task that may cause an exception.

lpBuffer = (LPTSTR) LocalAlloc(LPTR, 10);
strcpy(lpBuffer,"Hello"); // possible access violation
printf("%s\n",lpBuffer);
LocalFree(lpBuffer);
}

// LeaveCriticalSection is called even if
// an exception occurred.

finally {
LeaveCriticalSection(&CriticalSection);
}

+$#A Structured Exception Handling Functions
Following are the functions used in structured exception handling.
AbnormalTermination_win32_AbnormalTerminationGetExceptionCode_win32_GetExceptionCodeGetExceptionInformation_win32_GetExceptionInformationRaiseException_win32_RaiseExceptionSetUnhandledExceptionFilter_win32_SetUnhandledExceptionFilterUnhandledExceptionFilter_win32_UnhandledExceptionFilter
Obsolete or Deleted Functions
Catch_win32_CatchThrow_win32_Throw

+$#AK Synchronization
The Microsoft® Win32® application programming interface (API) provides a variety of ways to coordinate multiple threads of execution.
+$#A About Synchronization
The functions described in this overview provide synchronization mechanisms, such as wait functions and synchronization objects, and overlapped input and output (I/O) operations. The synchronization mechanisms also include critical section objects and interlocked variable access
+$#A Wait Functions
The Win32 API provides a set of wait functions that enable a thread to block its own execution. These functions do not return until a set of conditions specified by the wait function's parameters have been satisfied. These parameters include a time-out interval and handles of one or more synchronization objects. For more information about synchronization objects, see Synchronization Objects Overview_win32_Synchronization_Objects_Overview.
When a wait function is called, it checks the state of the specified synchronization object (or objects) and any other conditions that could cause it to return. If this initial state does not satisfy the specified conditions and the time-out interval has not elapsed, the calling thread enters an efficient wait state, consuming very little processor time while waiting for the conditions to be satisfied. The time-out interval can be set to INFINITE to cause an indefinite wait.
The WaitForSingleObject_win32_WaitForSingleObject and WaitForSingleObjectEx_win32_WaitForSingleObjectEx functions require a handle of one synchronization object. These functions return when the state of the object is signaled, or when the time-out interval elapses. (The state of a synchronization object is either signaled or nonsignaled.)
The WaitForMultipleObjects_win32_WaitForMultipleObjects, WaitForMultipleObjectsEx_win32_WaitForMultipleObjectsEx, and MsgWaitForMultipleObjects_win32_MsgWaitForMultipleObjects functions enable the calling thread to specify an array containing one or more synchronization object handles. These functions can be set to return when the state of any one of the specified objects is signaled, in which case the return value indicates the array index of the object that caused the function to return. Alternatively, the functions can be set to return only when the state of all objects is signaled at the same time, in which case the return value simply indicates that the specified conditions were satisfied.
When waiting for all objects to be signaled, these multiple-object functions do not modify the state of the specified objects until all objects are signaled simultaneously. For example, a mutex can be signaled, but the calling thread does not get ownership of it until the other objects are also signaled. In the meantime, some other thread may get ownership of the mutex, thereby delaying satisfaction of all specified conditions.
The WaitForSingleObjectEx and WaitForMultipleObjectsEx functions differ from the other wait functions in that they can optionally perform an alertable wait. In an alertable wait, the function can return when the specified conditions are satisfied, but it can also return if the system queues a completion routine for execution by the waiting thread. For more information about alertable waits and I/O completion routines, see Synchronization and Overlapped Input and Output_win32_Synchronization_and_Overlapped_Input_and_Output.
The MsgWaitForMultipleObjects function is identical to WaitForMultipleObjects, except that it can also return when a specified type of input is available in the calling thread's input queue. For example, a thread could use MsgWaitForMultipleObjects to block its execution until either the state of a specified object is signaled, or there is mouse input available in the thread's input queue. When input is available, the thread can use the GetMessage_win32_GetMessage or PeekMessage_win32_PeekMessage function to retrieve it.
Before returning, a wait function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. A wait function can modify the state of synchronization objects as follows:
· The count of a semaphore object decreases by one, with the semaphore's state changing to nonsignaled if its count is zero.
· The state of mutex, auto-reset event, and change notification objects changes to nonsignaled.
· The state of manual-reset event, process, thread, and console input objects is not affected by a wait function.

+$#A Synchronization Objects Overview
A synchronization object is an object whose handle can be specified in one of the wait functions to coordinate the execution of multiple threads. The state of a synchronization object is either signaled, which can allow the wait function to return, or nonsignaled, which can prevent the function from returning. More than one process can have a handle of the same synchronization object, making interprocess synchronization possible.
The Win32 API provides the following three object types intended exclusively for synchronization.
Type Description
Event Notifies one or more waiting threads that an event has occurred.
Mutex Can be owned by only one thread at a time, enabling threads to coordinate mutually exclusive access to a shared resource.
Semaphore Maintains a count between zero and some maximum value, limiting the number of threads that are simultaneously accessing a shared resource.

Though available for other uses, the following objects can also be used for synchronization.
Object Description
Change notification Created by the FindFirstChangeNotification_win32_FindFirstChangeNotification function, its state is signaled when a specified type of change occurs within a specified directory or directory tree. For more information, see Files_win32_Files.
Console input Created when a console is created, when a handle to console input is returned by the CreateFile_win32_CreateFile function when CONIN$ is specified, or by the GetStdHandle_win32_GetStdHandle function. Its state is signaled when there is unread input in the console's input buffer, and nonsignaled when the input buffer is empty. For more information about consoles, see Consoles and Character-Mode Support_win32_Consoles_and_Character_Mode_Support.
Process Created when a new process is created by calling the CreateProcess_win32_CreateProcess function, its state is nonsignaled while the process is running, and signaled when the process terminates. For more information about processes, see Processes and Threads_win32_Processes_and_Threads.
Thread Created when a new thread is created by calling the CreateProcess, CreateThread_win32_CreateThread, or CreateRemoteThread_win32_CreateRemoteThread function, its state is nonsignaled while the thread is running, and signaled when the thread terminates. For more information about threads, see Processes and Threads_win32_Processes_and_Threads.

In some circumstances, you can also use a file, named pipe, or communications device as a synchronization object; however, their use for this purpose is discouraged. For additional information about I/O operations on files, named pipes, or communications, see Synchronization and Overlapped Input and Output_win32_Synchronization_and_Overlapped_Input_and_Output.
+$#A Mutex Objects Overview
A mutex object is a synchronization object whose state is signaled when it is not owned by any thread, and nonsignaled when it is owned. Only one thread at a time can own a mutex, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex.
A thread uses the CreateMutex_win32_CreateMutex function to create a mutex object. The creating thread can request immediate ownership of the mutex and can also specify a name for the mutex object. Threads in other processes can open a handle of an existing mutex object by specifying its name in a call to the OpenMutex_win32_OpenMutex function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization_win32_Interprocess_Synchronization.
Any thread with a handle of a mutex object can use one of the wait functions to request ownership of the mutex. If the mutex is owned by another thread, the wait function blocks the execution of the requesting thread until the owning thread releases the mutex using the ReleaseMutex_win32_ReleaseMutex function. The return value of the wait function indicates whether the function returned for some reason other than the state of the mutex being signaled.
Once a thread owns a mutex, it can specify the same mutex in repeated wait-function calls without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.
If a thread terminates without releasing its ownership of a mutex object, the mutex is considered to be abandoned. A waiting thread can acquire ownership of an abandoned mutex, but the wait function's return value indicates that the mutex is abandoned. It is best to assume that an abandoned mutex indicates that an error has occurred and that any shared resource being protected by the mutex is in an undefined state. If the thread proceeds normally, as though the mutex had not been abandoned, the mutex's abandoned flag is cleared when the thread releases its ownership. This restores normal mutex behavior if the mutex is subsequently specified in a wait function.
+$#A Semaphore Objects Overview
A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. A semaphore's state is signaled when its count is greater than zero, and nonsignaled when its count is zero.
The semaphore object is useful in controlling a shared resource that can support a limited number of users. It acts like a gate that counts the threads as they enter and exit a controlled area and limits the number of threads sharing the resource to a specified maximum number. For example, an application might want to place a limit on the number of windows that it creates. It uses a semaphore with a maximum count equal to the window limit, decrementing the count whenever a window is created and incrementing it whenever a window is closed. The application specifies the semaphore object in a wait function before each window is created. When the count is zero ¾ indicating that the window limit has been reached ¾ the wait function blocks execution of the window-creation code.
A thread uses the CreateSemaphore_win32_CreateSemaphore function to create a semaphore object. The creating thread specifies the initial count and the maximum count of the object. The initial count must be neither less than zero nor greater than the maximum count. The creating thread can also specify a name for the semaphore object. Threads in other processes can open a handle of an existing semaphore object by specifying its name in a call to the OpenSemaphore_win32_OpenSemaphore function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization_win32_Interprocess_Synchronization.
Each time that a wait function returns because of a semaphore's signaled state, the count of the semaphore is decreased by one. The ReleaseSemaphore_win32_ReleaseSemaphore function increases a semaphore's count by a specified amount. The count can never be less than zero or greater than the maximum count.
The initial count of a semaphore is typically set to the maximum value. The count is then decremented from that level as the protected resource is consumed. Alternatively, a semaphore can be created with an initial count of zero to block access to the protected resource while the application is being initialized. After initialization, you would use ReleaseSemaphore to increment the count to the maximum.
Threads do not acquire ownership of semaphore objects as they do with mutex objects. A thread that owns a mutex can wait repeatedly for the same mutex object without its execution becoming blocked. A thread that waits repeatedly for the same semaphore object, however, decrements the semaphore's count each time a wait is satisfied; its execution is blocked when the count gets to zero. Similarly, only the thread that owns a mutex can successfully call the ReleaseMutex_win32_ReleaseMutex function, though any thread can use ReleaseSemaphore to increase the count of a semaphore object, even if the thread never used a wait function to decrement the count.
A thread can decrement a semaphore's count more than once by repeatedly specifying the same semaphore object in calls to any of the wait functions. However, calling the WaitForMultipleObjects_win32_WaitForMultipleObjects function with an array that contains multiple handles of the same semaphore does not perform multiple decrements.
+$#A Event Objects Overview
An event object is a synchronization object whose state can be explicitly set to signaled by use of the SetEvent_win32_SetEvent or PulseEvent_win32_PulseEvent function. Following are the two types of event object.
Object Description
Manual-reset event An event object whose state remains signaled until it is explicitly reset to nonsignaled by the ResetEvent_win32_ResetEvent function. While it is signaled, any number of waiting threads, or threads that subsequently specify the same event object in one of the wait functions, can be released.
Auto-reset event An event object whose state remains signaled until a single waiting thread is released, at which time the system automatically sets the state to nonsignaled. If no threads are waiting, the event object's state remains signaled.

The event object is useful in signaling a thread that a particular event has occurred. For example, in overlapped input and output, the system sets a specified event object to the signaled state when the overlapped operation has been completed. A single thread can specify different event objects in several simultaneous overlapped operations, then use the WaitForMultipleObjects_win32_WaitForMultipleObjects function to wait for the state of any one of the event objects to be signaled.
A thread uses the CreateEvent_win32_CreateEvent function to create an event object. The creating thread specifies the initial state of the object and whether it is a manual-reset or auto-reset event object. The creating thread can also specify a name for the event object. Threads in other processes can open a handle of an existing event object by specifying its name in a call to the OpenEvent_win32_OpenEvent function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization_win32_Interprocess_Synchronization.
A thread can use the PulseEvent_win32_PulseEvent function to set an event object's state to signaled and then reset it to nonsignaled after releasing the appropriate number of waiting threads. For a manual-reset event object, all waiting threads that can be released immediately are released. For an auto-reset event object, the function releases only a single waiting thread, even if multiple threads are waiting. If no threads are waiting, or if no thread can be released immediately, PulseEvent simply sets the event object's state to nonsignaled and returns.
+$#A Interprocess Synchronization
Multiple processes can have handles of the same mutex, semaphore, or event object, so these objects can be used to accomplish interprocess synchronization. The process that creates an object can use the handle returned by the creation function (CreateEvent_win32_CreateEvent, CreateMutex_win32_CreateMutex, or CreateSemaphore_win32_CreateSemaphore). Other processes can open a handle of the object by using its name, or through inheritance or duplication.
+$#A Object Names
Named objects provide an easy way for processes to share object handles. The name specified by the creating process is limited to MAX_PATH characters, and can include any character except the backslash path-separator character (\). Once a process has created a named event, mutex, or semaphore object, other processes can use the name to call the appropriate function (OpenEvent_win32_OpenEvent, OpenMutex_win32_OpenMutex, or OpenSemaphore_win32_OpenSemaphore) to open a handle of the object. Name comparison is case sensitive. The names of event, semaphore, mutex, and file-mapping objects share the same name space, so an error occurs if a specified name matches the name of an existing object of a different type. When creating named objects, therefore, use unique names and be sure to check function return values for duplicate-name errors.
If the name specified in a call to the CreateMutex_win32_CreateMutex function matches the name of an existing mutex object, the function returns a handle of the existing object. In this case, the call to CreateMutex is equivalent to a call to the OpenMutex function. (The CreateEvent_win32_CreateEvent and CreateSemaphore_win32_CreateSemaphore functions work the same way with existing named event and semaphore objects, respectively.) Having multiple processes use CreateMutex for the same mutex is therefore equivalent to having one process that calls CreateMutex while the other processes call OpenMutex, except that it eliminates the need to ensure that the creating process is started first. When using this technique for mutex objects, however, none of the calling processes should request immediate ownership of the mutex. If multiple processes do request immediate ownership, it can be difficult to predict which process actually gets the initial ownership.
+$#A Object Inheritance
A child process created by the CreateProcess_win32_CreateProcess function can inherit a handle of a mutex, event, or semaphore if the SECURITY_ATTRIBUTES_win32_SECURITY_ATTRIBUTES_str structure ¾ specified when the object was created ¾ enabled inheritance. The handle inherited by the child process has the same access to the object as the parent's handle. The inherited handle appears in the handle table of the child process, but for the child process to be able to use the handle, the parent process must communicate the handle's value to the child process. The parent can do this by specifying the value as a command-line argument when it calls the CreateProcess function. The child process can then use the GetCommandLine_win32_GetCommandLine function to retrieve the command-line string and convert the handle argument into a usable handle. For more information about object inheritance, see Processes and Threads_win32_Processes_and_Threads.
+$#A Object Duplication
The DuplicateHandle_win32_DuplicateHandle function creates a duplicate handle that can be used by another specified process. This method of sharing object handles is more complex than using named objects or inheritance. It requires communication between the creating process and the process into which the handle is duplicated. The necessary information (the handle value and process identifier) can be communicated by any of the interprocess communication methods (for example, named pipe, shared file, shared memory). For more information about object duplication, see Processes and Threads_win32_Processes_and_Threads.
+$#A Synchronization and Overlapped Input and Output
The Win32 API supports both synchronous and asynchronous (or overlapped) I/O operations on files, named pipes, and serial communications devices. The WriteFile_win32_WriteFile, ReadFile_win32_ReadFile, DeviceIoControl_win32_DeviceIoControl, WaitCommEvent_win32_WaitCommEvent, ConnectNamedPipe_win32_ConnectNamedPipe, and TransactNamedPipe_win32_TransactNamedPipe functions can be performed either synchronously or asynchronously. The ReadFileEx_win32_ReadFileEx and WriteFileEx_win32_WriteFileEx functions can be performed asynchronously only.
When a function is executed synchronously, it does not return until the operation has been completed. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. Functions called for overlapped operation can return immediately, even though the operation has not been completed. This enables a time-consuming I/O operation to be executed in the background while the calling thread is free to perform other tasks. For example, a single thread can perform simultaneous I/O operations on different handles, or even simultaneous read and write operations on the same handle. To synchronize its execution with the completion of the overlapped operation, the calling thread uses the GetOverlappedResult_win32_GetOverlappedResult function or one of the wait functions to determine when the operation has been completed.
Overlapped operations require a file, named pipe, or communications device that was created with the FILE_FLAG_OVERLAPPED flag. To call a function as an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED_win32_OVERLAPPED_str structure. If this pointer is NULL, the operation is performed synchronously, even though the handle was created with FILE_FLAG_OVERLAPPED enabled. The OVERLAPPED structure must contain a handle to a manual-reset ¾ not an auto-reset ¾ event object. The system sets the state of the event object to nonsignaled when a call to the I/O function returns before the operation has been completed. The system sets the state of the event object to signaled when the operation has been completed.
When a function is called as an overlapped operation, the operation may have been completed before the function returns. When this happens, the results are handled as if the operation had been performed synchronously. If the operation has not yet been completed, however, the function's return value is FALSE, and the GetLastError_win32_GetLastError function returns ERROR_IO_PENDING.
A thread can manage overlapped operations by either of two methods. It can use the GetOverlappedResult function to wait for the overlapped operation to be completed. Alternatively, it can specify a handle of the OVERLAPPED structure's manual-reset event object in one of the wait functions and then call GetOverlappedResult after the wait function returns. GetOverlappedResult returns the results of the completed overlapped operation, and for functions in which such information is appropriate, it reports the actual number of bytes that were transferred.
When performing multiple simultaneous overlapped operations, the calling thread must specify a separate OVERLAPPED structure with its own manual-reset event object for each operation. The thread can then specify handles of the different event objects in the handle-array parameter of the WaitForMultipleObjects_win32_WaitForMultipleObjects function (or one of the other multiple-object wait functions) to wait for any one of the overlapped operations to be completed. The return value of the multiple-object wait function indicates which event object was signaled, so the thread can determine which operation was completed.
If no event object is specified in the OVERLAPPED_win32_OVERLAPPED_str structure, the system signals the state of the file, named pipe, or communications device when the overlapped operation has been completed. Thus, you can specify these handles as synchronization objects in a wait function, though their use for this purpose can be difficult to manage. When performing simultaneous overlapped operations on the same file, named pipe, or communications device, there is no way to know which operation caused the object's state to be signaled. It is safer to use a separate event object for each overlapped operation.
For examples that illustrate the use of overlapped operations, completion routines, and the GetOverlappedResult_win32_GetOverlappedResult function, see Pipes_win32_Pipes.
+$#A Critical Section Objects
Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization. Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access. For example, a process could use a critical section object to prevent more than one thread at a time from modifying a global data structure.
The process is responsible for allocating the memory used by a critical section object. Typically, this is done by simply declaring a variable of type CRITICAL_SECTION. Before the threads of the process can use it, the critical section object must be initialized by using the InitializeCriticalSection_win32_InitializeCriticalSection function.
A thread uses the EnterCriticalSection_win32_EnterCriticalSection function to request ownership and the LeaveCriticalSection_win32_LeaveCriticalSection function to release its ownership. If the critical section object is currently owned by another thread, EnterCriticalSection waits indefinitely for ownership. This differs from the use of a mutex object for mutual exclusion, where the wait functions accept a specified time-out interval.
Once a thread owns a critical section object, it can make additional calls to EnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section object that it already owns. To release its ownership, the thread must call LeaveCriticalSection once for each time that it entered the critical section.
Any thread of the process can use the DeleteCriticalSection_win32_DeleteCriticalSection function to release the system resources that were allocated when the critical section object was initialized. After this function has been called, the critical section object can no longer be used for synchronization.
When a critical section object is owned, the only other threads affected are those that are waiting for ownership in a call to EnterCriticalSection. Threads that are not waiting are free to continue their normal execution.
+$#A Interlocked Variable Access
The InterlockedIncrement_win32_InterlockedIncrement and InterlockedDecrement_win32_InterlockedDecrement functions provide a simple mechanism for synchronizing access to a specified 32-bit variable that is shared by multiple threads in the same or in different processes. These functions combine the operations of incrementing or decrementing the variable and checking the resulting value. This is useful in a multitasking operating system, in which the system can interrupt one thread's execution to grant a slice of processor time to another thread. Without such synchronization, one thread could increment a variable but be interrupted by the system before it can check the resulting value of the variable. A second thread could then increment the same variable. When the first thread receives its next timeslice, it will check the value of the variable, which has now been incremented not once but twice. The interlocked variable-access functions protect against this kind of error.
The threads of different processes can use this mechanism if the variable is in shared memory.
+$#A Using Synchronization
· Using named objects_win32_Using_Named_Objects
· Waiting for multiple objects_win32_Waiting_for_Multiple_Objects
· Using mutex objects_win32_Using_Mutex_Objects
· Using semaphore objects_win32_Using_Semaphore_Objects
· Using event objects_win32_Using_Event_Objects
· Using critical section objects_win32_Using_Critical_Section_Objects

+$#A Using Named Objects
This example illustrates the use of an object name to let two processes open handles of the same mutex object.
The first process uses the CreateMutex_win32_CreateMutex function to create the mutex object. Note that the function succeeds even if there is an existing object with the same name.
/* One process creates the mutex object. */

HANDLE hMutex;
DWORD dwErr;

hMutex = CreateMutex(
NULL, /* no security descriptor */
FALSE, /* mutex not owned */
"NameOfMutexObject"); /* object name */
if (hMutex == NULL)
printf("CreateMutex error: %d\n", GetLastError() );
else
if ( GetLastError() == ERROR_ALREADY_EXISTS )
printf("CreateMutex opened existing mutex\n");
else
printf("CreateMutex created new mutex\n");

The second process uses the OpenMutex_win32_OpenMutex function to open a handle of the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.
/* Another process opens a handle of the existing mutex. */

HANDLE hMutex;

hMutex = OpenMutex(
MUTEX_ALL_ACCESS, /* request full access */
FALSE, /* handle not inheritable */
"NameOfMutexObject"); /* object name */
if (hMutex == NULL)
printf("OpenMutex error: %d\n", GetLastError() );

The following example uses the CreateSemaphore_win32_CreateSemaphore function to illustrate a named-object creation operation that fails if the object already exists.
HANDLE CreateNewSemaphore(LPSECURITY_ATTRIBUTES lpsa,
LONG cInitial, LONG cMax, LPTSTR lpszName) {
HANDLE hSem;

/* Create or open a named semaphore. */

hSem = CreateSemaphore(
lpsa, /* security attributes */
cInitial, /* initial count */
cMax, /* maximum count */
lpszName); /* semaphore name */

/* Close handle, and return NULL if existing semaphore opened. */

if (hSem != NULL &&
GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(hSem);
return NULL;
}

/* If new semaphore was created, return the handle. */

return hSem;

}

+$#A Waiting for Multiple Objects
This example uses the CreateEvent_win32_CreateEvent function to create two event objects. It then uses the WaitForMultipleObjects_win32_WaitForMultipleObjects function to wait for one of them to be signaled.
HANDLE hEvents[2];
DWORD i, dwEvent;

/* Create two event objects. */

for (i = 0; i < 2; i++) {
hEvents[i] = CreateEvent(
NULL, /* no security attributes */
FALSE, /* auto-reset event object */
FALSE, /* initial state is nonsignaled */
NULL); /* unnamed object */
if (hEvents[i] == NULL) {
printf("CreateEvent error: %d\n", GetLastError() );
ExitProcess(0);
}
}

/*
* The creating thread waits for other threads or processes
* to signal the event objects.
*/

dwEvent = WaitForMultipleObjects(
2, /* number of objects in array */
hEvents, /* array of objects */
FALSE, /* wait for any */
INFINITE); /* indefinite wait */

/* Return value indicates which event is signaled. */

switch (dwEvent) {

/* hEvent[0] was signaled. */

case WAIT_OBJECT_0 + 0:

/* Perform tasks required by this event. */

break;

/* hEvent[1] was signaled. */

case WAIT_OBJECT_0 + 1:

/* Perform tasks required by this event. */

break;

/* Return value is invalid. */

default:

printf("Wait error: %d\n", GetLastError());
ExitProcess(0);

}

+$#A Using Mutex Objects
You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource. For example, if several threads share access to a database, the threads can use a mutex object to permit only one thread at a time to write to the database.
In the following example, a process uses the CreateMutex_win32_CreateMutex function to create a named mutex object or open a handle of an existing mutex object.
HANDLE hMutex;

/* Create a mutex with no initial owner. */

hMutex = CreateMutex(
NULL, /* no security attributes */
FALSE, /* initially not owned */
"MutexToProtectDatabase"); /* name of mutex */
if (hMutex == NULL) {
/* check for error */
}

When a thread of this process writes to the database, as in the next example, it first requests ownership of the mutex. If it gets ownership, the thread writes to the database and then releases its ownership.
The example uses the try-finally structured exception-handling syntax to ensure that the thread properly releases the mutex. The finally block of code is executed no matter how the try block terminates (unless the try block includes a call to the TerminateThread_win32_TerminateThread function). This prevents the mutex from being abandoned inadvertently.
BOOL FunctionToWriteToDatabase(HANDLE hMutex) {
DWORD dwWaitResult;

/* Request ownership of mutex. */

dwWaitResult = WaitForSingleObject(
hMutex, /* handle of mutex */
5000L); /* five-second time-out interval */

switch (dwWaitResult) {

/* The thread got mutex ownership. */

case WAIT_OBJECT_0:

try {

.
. /* Write to the database. */
.

}

finally {

/* Release ownership of the mutex object. */

if (! ReleaseMutex(hMutex)) {
/* Deal with error. */
}

break;
}

/* Cannot get mutex ownership due to time-out. */

case WAIT_TIMEOUT:

return FALSE;

/* Got ownership of the abandoned mutex object. */

case WAIT_ABANDONED:

return FALSE;

}

return TRUE;

}

+$#A Using Semaphore Objects
In the following example, a process uses a semaphore object to limit the number of windows it creates. First, it uses the CreateSemaphore_win32_CreateSemaphore function to create the semaphore and to specify initial and maximum counts.
HANDLE hSemaphore;
LONG cMax = 10;
LONG cPreviousCount;

/* Create a semaphore with initial and max. counts of 10. */

hSemaphore = CreateSemaphore(
NULL, /* no security attributes */
cMax, /* initial count */
cMax, /* maximum count */
NULL); /* unnamed semaphore */
if (hSemaphore == NULL) {
/* Check for error. */
}

Before any thread of the process creates a new window, it uses the WaitForSingleObject_win32_WaitForSingleObject function to determine whether the semaphore's current count permits the creation of additional windows. The wait function's time-out parameter is set to zero, so the function returns immediately if the semaphore is nonsignaled.
DWORD dwWaitResult;

/* Try to enter the semaphore gate. */

dwWaitResult = WaitForSingleObject(
hSemaphore, /* handle of semaphore */
0L); /* zero-second time-out interval */

switch (dwWaitResult) {

/* The semaphore object was signaled. */

case WAIT_OBJECT_0:
.
. /* OK to open another window. */
.
break;

/* Semaphore was nonsignaled, so a time-out occurred. */

case WAIT_TIMEOUT:
.
. /* Cannot open another window. */
.
break;

}

When a thread closes a window, it uses the ReleaseSemaphore_win32_ReleaseSemaphore function to increment the semaphore's count.
/* Increment the count of the semaphore. */

if (! ReleaseSemaphore(
hSemaphore, /* handle of semaphore */
1, /* increase count by one */
NULL) ) { /* not interested in previous count */

/* Deal with the error. */

}

+$#A Using Event Objects
The Win32 API uses event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion. For more information about the use of event objects in overlapped I/O operations, see Synchronization and Overlapped Input and Output_win32_Synchronization_and_Overlapped_Input_and_Output.
In the following example, an application uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent_win32_CreateEvent function to create a manual-reset event object. The master thread sets the event object to nonsignaled when it is writing to the buffer and then resets the object to signaled when it has finished writing. Then it creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.
#define NUMTHREADS 4

HANDLE hGlobalWriteEvent;

void CreateEventsAndThreads(void) {

HANDLE hReadEvents[NUMTHREADS], hThread;
DWORD i, IDThread;

/*
* Create a manual-reset event object. The master thread sets
* this to nonsignaled when it writes to the shared buffer.
*/

hGlobalWriteEvent = CreateEvent(
NULL, /* no security attributes */
TRUE, /* manual-reset event */
TRUE, /* initial state is signaled */
"WriteEvent" /* object name */
);
if (hGlobalWriteEvent == NULL) {
/* error exit */
}

/*
* Create multiple threads and an auto-reset event object
* for each thread. Each thread sets its event object to
* signaled when it is not reading from the shared buffer.
*/

for(i = 1; i <= NUMTHREADS; i++) {

/* Create the auto-reset event. */

hReadEvents[i] = CreateEvent(
NULL, /* no security attributes */
FALSE, /* auto-reset event */
TRUE, /* initial state is signaled */
NULL); /* object not named */
if (hReadEvents[i] == NULL) {
/* error exit */
}

hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ThreadFunction,
&hReadEvents[i], /* pass event handle */
0, &IDThread);
if (hThread == NULL) {
/* error exit */
}
}

}

Before the master thread writes to the shared buffer, it uses the ResetEvent_win32_ResetEvent function to set the state of hGlobalWriteEvent (an application-defined global variable) to nonsignaled. This blocks the reader threads from starting a read operation. The master then uses the WaitForMultipleObjects_win32_WaitForMultipleObjects function to wait for all reader threads to finish any current read operations. When WaitForMultipleObjects returns, the master thread can safely write to the buffer. After it has finished, it sets hGlobalWriteEvent and all the reader-thread events to signaled, enabling the reader threads to resume their read operations.
VOID WriteToBuffer(VOID) {
DWORD dwWaitResult, i;

/* Reset hGlobalWriteEvent to nonsignaled, to block readers. */

if (! ResetEvent(hGlobalWriteEvent) ) {
/* error exit */
}

/* Wait for all reading threads to finish reading. */

dwWaitResult = WaitForMultipleObjects(
NUMTHREADS, /* number of handles in array */
hReadEvents, /* array of read-event handles */
TRUE, /* wait until all are signaled */
INFINITE); /* indefinite wait */

switch (dwWaitResult) {

/* All read-event objects were signaled. */

case WAIT_OBJECT_0:
.
. /* Write to the shared buffer. */
.
break;

/* An error occurred. */

default:

printf("Wait error: %d\n", GetLastError());
ExitProcess(0);

}

/* Set hGlobalWriteEvent to signaled. */

if (! SetEvent(hGlobalWriteEvent) ) {
/* error exit */
}

/* Set all read events to signaled. */

for(i = 1; i <= NUMTHREADS; i++)
if (! SetEvent(hReadEvents[i]) ) {
/* error exit */
}

}

Before starting a read operation, each reader thread uses WaitForMultipleObjects_win32_WaitForMultipleObjects to wait for the application-defined global variable hGlobalWriteEvent and its own read event to be signaled. When WaitForMultipleObjects returns, the reader thread's auto-reset event has been reset to nonsignaled. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent_win32_SetEvent function to set the event's state back to signaled.
VOID ThreadFunction(LPVOID lpParam) {
DWORD dwWaitResult, i;
HANDLE hEvents[2];

hEvents[0] = (HANDLE) *lpParam; /* thread's read event */
hEvents[1] = hGlobalWriteEvent;

dwWaitResult = WaitForMultipleObjects(
2, /* number of handles in array */
hEvents, /* array of event handles */
TRUE, /* wait till all are signaled */
INFINITE); /* indefinite wait */

switch (dwWaitResult) {

/* Both event objects were signaled. */

case WAIT_OBJECT_0:
.
. /* Read from the shared buffer. */
.
break;

/* An error occurred. */

default:

printf("Wait error: %d\n", GetLastError());
ExitThread(0);

}

/* Set the read event to signaled. */

if (! SetEvent(hEvents[0]) ) {
/* error exit */
}

}

+$#A Using Critical Section Objects
The following example shows how a thread initializes, enters, and leaves a critical section. As with the mutex example (see Using Mutex Objects_win32_Using_Mutex_Objects), this example uses the try-finally structured exception-handling syntax to ensure that the thread calls the LeaveCriticalSection_win32_LeaveCriticalSection function to release its ownership of the critical section object.
CRITICAL_SECTION GlobalCriticalSection;

/* Initialize the critical section. */

InitializeCriticalSection(&GlobalCriticalSection);

/* Request ownership of the critical section. */

try {

EnterCriticalSection(&GlobalCriticalSection);
.
. /* Access the shared resource. */
.
}
finally {

/* Release ownership of the critical section. */

LeaveCriticalSection(&GlobalCriticalSection);

}

+$#A Synchronization Functions
Following are the functions used in synchronization.
CreateEvent_win32_CreateEventCreateMutex_win32_CreateMutexCreateSemaphore_win32_CreateSemaphoreDeleteCriticalSection_win32_DeleteCriticalSectionEnterCriticalSection_win32_EnterCriticalSectionGetOverlappedResult_win32_GetOverlappedResultInitializeCriticalSection_win32_InitializeCriticalSectionInterlockedDecrement_win32_InterlockedDecrementInterlockedExchange_win32_InterlockedExchangeInterlockedIncrement_win32_InterlockedIncrementLeaveCriticalSection_win32_LeaveCriticalSectionMsgWaitForMultipleObjects_win32_MsgWaitForMultipleObjectsOpenEvent_win32_OpenEventOpenMutex_win32_OpenMutexOpenSemaphore_win32_OpenSemaphorePulseEvent_win32_PulseEventReleaseMutex_win32_ReleaseMutexReleaseSemaphore_win32_ReleaseSemaphoreResetEvent_win32_ResetEventSetEvent_win32_SetEventWaitForMultipleObjects_win32_WaitForMultipleObjectsWaitForMultipleObjectsEx_win32_WaitForMultipleObjectsExWaitForSingleObject_win32_WaitForSingleObjectWaitForSingleObjectEx_win32_WaitForSingleObjectEx

+$#AK System Information
The Microsoft® Win32® application programming interface (API) includes functions that describe the current system configuration.
+$#A About System Information
The functions described in this overview retrieve a variety of data, such as the computer name, user name, settings of environment variables, processor type, system colors, and so on. The Win32 API also includes functions that can change the system configuration, shut down the system, and start the Microsoft® Windows™ Help application.
+$#A System Configuration
Several of the functions in the Win32 API are specifically intended to describe or change the system configuration. At startup, for example, a computer is assigned the name listed in the registry. An application can retrieve this name by using the GetComputerName_win32_GetComputerName function and can change the name by using the SetComputerName_win32_SetComputerName function. SetComputerName changes the registry, not the current computer name. As a result, the new name is not assigned until the computer is restarted. For more information about the registry, see Registry_win32_Registry.
The GetUserName_win32_GetUserName function retrieves the name of the user currently logged onto the system. The user name is either the logon name or the user's full name, if the latter is included in the registry.
The GetSystemInfo_win32_GetSystemInfo function retrieves processor and memory information, such as the page size, original equipment manufacturer (OEM) identifier, number and type of processors, application address range, and so on. The GetVersion_win32_GetVersion function retrieves the major and minor revision numbers of the installed version of Microsoft Windows. The GetKeyboardType_win32_GetKeyboardType function retrieves such information as the type of keyboard and the number of function keys on the current keyboard.
The SystemParametersInfo_win32_SystemParametersInfo function retrieves or sets various system attributes, such as double-click time, screen saver time-out, window border width, and desktop pattern. When an application uses SystemParametersInfo to set a parameter, the change takes place immediately. This function also enables applications to update the user profile, so changes to the system will be preserved when the system is restarted.
The GetThreadDesktop_win32_GetThreadDesktop function retrieves a handle of the desktop for a given thread. This handle can be used in calls to functions that retrieve and set security attributes.
The Windows directory is the directory that contains Windows-based applications, initialization files, and Help files. The GetWindowsDirectory_win32_GetWindowsDirectory function retrieves the path to this directory. Applications that create files should do so in the Windows directory. This is the only directory guaranteed to be private to a user running a shared version of Windows.
The system directory is the directory that contains Windows libraries, drivers, and font files. The GetSystemDirectory_win32_GetSystemDirectory function retrieves the path to this directory.
An environment variable is a symbolic variable that represents some element of the system, such as a path, a filename, or other literal data. For example, the environment variable PATH represents the directories in which to search for executable files. When a user logs on, the system initializes environment variables based on the environment section of the registry. The ExpandEnvironmentStrings_win32_ExpandEnvironmentStrings function retrieves the values of specified environment variables.
System metrics are the dimensions of various Windows display elements. (Display elements are the parts of a window and the Windows display that appear on the system display screen.) Typical system metrics include the window border width, icon height, and so on. System metrics also describe other aspects of the system, such as whether a mouse is installed, double-byte characters are supported, or a debugging version of Windows is installed. The GetSystemMetrics_win32_GetSystemMetrics function retrieves a specified system metric.
Applications can also retrieve and set the color of window elements such as menus, scroll bars, and buttons by using the GetSysColor_win32_GetSysColor and SetSysColors_win32_SetSysColors functions, respectively.
+$#A System Shutdown
The Win32 API also provides a way for an application to shut down a local or remote computer. This is particularly useful for system administrators who update system software on remote computers. The InitiateSystemShutdown_win32_InitiateSystemShutdown function starts a timer and displays a dialog box that prompts the user to log off. While the dialog box is displayed, the AbortSystemShutdown_win32_AbortSystemShutdown function can stop the timer and prevent the computer from shutting down. However, if the timer expires, the computer is shut down. InitiateSystemShutdown can also restart the computer following shutdown.
+$#A Using System Information
· Getting system configuration information_win32_Getting_System_Configuration_Information
· Getting hardware information_win32_Getting_Hardware_Information
· Changing the colors of window elements_win32_Changing_the_Colors_of_Window_Elements
· Shutting down the system_win32_Shutting_Down_the_System

+$#A Getting System Configuration Information
The following example uses the GetComputerName_win32_GetComputerName, GetUserName_win32_GetUserName, GetSystemDirectory_win32_GetSystemDirectory, GetWindowsDirectory_win32_GetWindowsDirectory, and ExpandEnvironmentStrings_win32_ExpandEnvironmentStrings functions to get information that describes the system configuration. This example displays the information in a window's client area.
LPTSTR lpszSystemInfo; /* ptr. to system info. string */
DWORD cchBuff = 256; /* size of comp. or user name */
TCHAR tchBuffer2[BUFFER]; /* buffer for concat'd. str. */

DWORD dwResult; /* function return value */

SYSTEM_INFO siSysInfo; /* struct. for hardware info. */
int aTabs[1] = {260}; /* tab stop for TabbedTextOut */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

lpszSystemInfo = tchBuffer2;

/* Get and display the name of the computer. */

GetComputerName(lpszSystemInfo, &cchBuff);

nSize = sprintf(tchBuffer, "Computer name: %s",
lpszSystemInfo);
TextOut(hdc, 15, 20, tchBuffer, nSize);

/* Get and display the user name. */

GetUserName(lpszSystemInfo, &cchBuff);

nSize = sprintf(tchBuffer, "User name: %s",
lpszSystemInfo);
TextOut(hdc, 15, 40, tchBuffer, nSize);

/* Get and display the system directory. */

nSize = GetSystemDirectory(lpszSystemInfo, MAX_PATH);

nSize = sprintf(tchBuffer, "System directory: %s",
lpszSystemInfo);
TextOut(hdc, 15, 60, tchBuffer, nSize);

/* Get and display the Windows directory. */

nSize = GetWindowsDirectory(lpszSystemInfo, MAX_PATH);

nSize = sprintf(tchBuffer, "Windows directory: %s",
lpszSystemInfo);
TextOut(hdc, 15, 80, tchBuffer, nSize);

/* Display the "environment variables" header. */

nSize = sprintf(tchBuffer,
"Environment variables (partial list):");
TextOut(hdc, 15, 105, tchBuffer, nSize);

/* Expand the OS and NTVERSION environment variables. */

dwResult = ExpandEnvironmentStrings(
"OS=%OS% NTVERSION=%NTVERSION%",
lpszSystemInfo,
BUFFER);

TextOut(hdc, 25, 120, (LPCTSTR) lpszSystemInfo,
dwResult - 1);

/* Expand the PATH environment variable. */

dwResult = ExpandEnvironmentStrings(
"PATH=%PATH%",
lpszSystemInfo,
BUFFER);

TextOut(hdc, 25, 135, (LPCTSTR) lpszSystemInfo,
dwResult - 1);

/* Expand the LIBPATH environment variable. */

dwResult = ExpandEnvironmentStrings(
"LIBPATH=%LIBPATH%",
lpszSystemInfo,
BUFFER);

TextOut(hdc, 25, 150, (LPCTSTR) lpszSystemInfo,
dwResult - 1);

/* Expand the TMP environment variable. */

dwResult = ExpandEnvironmentStrings(
"TMP=%TMP%",
lpszSystemInfo,
BUFFER);

TextOut(hdc, 25, 165, (LPCTSTR) lpszSystemInfo,
dwResult - 1);

The next example uses the GetVersion_win32_GetVersion function to obtain the current version of Windows and then displays the version number in a message box.
DWORD dwResult; /* function return value */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

dwResult = GetVersion();

nSize = sprintf(tchBuffer, "Windows version %d.%d\n",
LOBYTE(LOWORD(dwResult)),
HIBYTE(LOWORD(dwResult)));

MessageBox(NULL, tchBuffer, "GetVersion", MB_ICONINFORMATION);

+$#A Getting Hardware Information
The following example uses the GetSystemInfo_win32_GetSystemInfo function to obtain hardware information such as the OEM identifier, processor type, page size, and so on. The example displays the information in a window's client area.
SYSTEM_INFO siSysInfo; /* struct. for hardware info. */
int aTabs[1] = {260}; /* tab stop for TabbedTextOut */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

/* Display the "hardware information" header. */

nSize = sprintf(tchBuffer,
"Hardware information:");
TextOut(hdc, 15, 20, tchBuffer, nSize);

/* Copy the hardware information to the SYSTEM_INFO structure. */

GetSystemInfo(&siSysInfo);

/* Display the contents of the SYSTEM_INFO structure. */

nSize = sprintf(tchBuffer,
"OEM ID: %u\tNumber of Processors: %u",
siSysInfo.dwOemId,
siSysInfo.dwNumberOfProcessors);
TabbedTextOut(hdc, 25, 40, tchBuffer,
nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,
"Page size: %u\tProcessor Type: %u",
siSysInfo.dwPageSize,
siSysInfo.dwProcessorType);
TabbedTextOut(hdc, 25, 60, tchBuffer,
nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,
"Minimum app address: %lx\tMaximum app address: %lx",
siSysInfo.lpMinimumApplicationAddress,
siSysInfo.lpMaximumApplicationAddress);
TabbedTextOut(hdc, 25, 80, tchBuffer,
nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,
"Active processor mask: %u",
siSysInfo.dwActiveProcessorMask);
TextOut(hdc, 25, 100, tchBuffer, nSize);

The following example uses the GetSystemMetrics_win32_GetSystemMetrics function to determine whether a mouse is installed and whether the mouse buttons are swapped. The example also uses the SystemParametersInfo_win32_SystemParametersInfo function to retrieve the mouse threshold and speed. It displays the information in a message box.
TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

BOOL fResult; /* system shutdown flag */

int aMouseInfo[3]; /* array for mouse info. */

/* Is there a mouse? */

fResult = GetSystemMetrics(SM_MOUSEPRESENT);

if (fResult == 0) {

/* Indicate if there is no mouse. */

nSize = sprintf(tchBuffer, "No mouse installed.");
} else {

/*
* If there is a mouse, determine whether
* its buttons are swapped.
*/

fResult = GetSystemMetrics(SM_SWAPBUTTON);

if (fResult == 0) {
nSize = sprintf(tchBuffer, "Buttons not swapped.\r");
} else {
nSize = sprintf(tchBuffer, "Buttons swapped.\r");
}

/* Get the mouse speed and the threshold values. */

SystemParametersInfo(SPI_GETMOUSE, /* get mouse info. */
NULL, /* not used */
&aMouseInfo, /* holds mouse info. */
NULL); /* not used */

nSize += sprintf(tchBuffer + nSize,
"Speed: %d\r", aMouseInfo[2]);
sprintf(tchBuffer + nSize,
"Threshold (x,y): %d,%d",
aMouseInfo[0], aMouseInfo[1]);
}

/* Display the mouse information. */

MessageBox(NULL, tchBuffer, "Mouse information",
MB_ICONINFORMATION);

This next example uses SystemParametersInfo_win32_SystemParametersInfo to double the mouse speed and update the MouseSpeed value in the WIN.INI file.
TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

int aMouseInfo[3]; /* array for mouse info. */

/* Get the current mouse speed. */

SystemParametersInfo(SPI_GETMOUSE, /* get mouse info. */
NULL, /* not used */
&aMouseInfo, /* holds mouse info. */
NULL); /* not used */

/* Double it. */

aMouseInfo[2] = 2 * aMouseInfo[2];

/* Change the mouse speed to the new value and update WIN.INI. */

SystemParametersInfo(SPI_SETMOUSE, /* set mouse info. */
NULL, /* not used */
aMouseInfo, /* mouse info. to set */
SPIF_UPDATEINIFILE); /* update win.ini */

+$#A Changing the Colors of Window Elements
The following example uses the SetSysColors_win32_SetSysColors function to change the color of active window borders to dark purple, of active title bars to dark cyan, and of the window background to light gray.
/* window elements to change */
int aiElements[3] = {COLOR_ACTIVEBORDER,
COLOR_ACTIVECAPTION,
COLOR_WINDOW};
DWORD aColors[3]; /* array of RGB values */

/* Define the new colors. */

aColors[0] = RGB(0x80, 0x00, 0x80); /* dark purple */
aColors[1] = RGB(0x00, 0x80, 0x80); /* dark cyan */
aColors[2] = RGB(0xC0, 0xC0, 0xC0); /* light gray */

/*
* Set the window elements in aiElements to the colors
* specified in aColors.
*/

SetSysColors(3, aiElements, aColors);

The next example uses the GetSysColor_win32_GetSysColor function to retrieve the color of the window background and displays the red, green, blue (RGB) value, in hexadecimal notation, in a message box.
DWORD dwResult; /* function return value */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */
int nSize; /* size of string */

/* Get the color of the window background. */

dwResult = GetSysColor(COLOR_WINDOW);

nSize = sprintf(tchBuffer,
"Window color: {%x, %x, %x}",
GetRValue(dwResult),
GetGValue(dwResult),
GetBValue(dwResult));

MessageBox(NULL, tchBuffer, "GetSysColor", MB_ICONINFORMATION);

+$#A Shutting Down the System
The following example uses the InitiateSystemShutdown_win32_InitiateSystemShutdown function to begin the system shutdown process on the local computer. Note that the application must first enable the SE_SHUTDOWN_NAME privilege to shut down the computer the user is logged onto.
HANDLE hToken; /* handle to process token */
TOKEN_PRIVILEGES tkp; /* ptr. to token structure */

BOOL fResult; /* system shutdown flag */

/*
* Get the current process token handle
* so we can get shutdown privilege.
*/

if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
ErrorHandler("OpenProcessToken failed.");

/* Get the LUID for shutdown privilege. */

LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)
ErrorHandler("AdjustTokenPrivileges enable failed.");

/* Display the shutdown dialog box and start the time-out countdown. */

fResult = InitiateSystemShutdown(
NULL, /* shut down local computer */
"Click on the main window and press \
the Escape key to cancel shutdown.",
/* msg. to user */
20, /* time-out period */
FALSE, /* prompt user to close apps. */
TRUE); /* reboot after shutdown */

if (!fResult) {
ErrorHandler("InitiateSystemShutdown failed.");
}

/* Disable shutdown privilege. */

tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);

if (GetLastError() != ERROR_SUCCESS)
ErrorHandler("AdjustTokenPrivileges disable failed.");

If the AbortSystemShutdown_win32_AbortSystemShutdown function is executed in the time-out period specified by InitiateSystemShutdown_win32_InitiateSystemShutdown, the system does not shut down. The following example enables the user to prevent the system from shutting down by clicking on the application's main window and pressing the ESC key. As noted earlier, your application must enable the SE_SHUTDOWN_NAME privilege to stop the system shutdown_win32_shutdown.
HANDLE hToken; /* handle to process token */
TOKEN_PRIVILEGES tkp; /* ptr. to token structure */

BOOL fResult; /* system shutdown flag */

case WM_KEYDOWN:

/* Process only the Escape key. */

if (wParam != VK_ESCAPE) {
break;
}

/*
* Get the current process token handle
* so we can get shutdown privilege.
*/

if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
ErrorHandler("OpenProcessToken failed.");

/* Get the LUID for shutdown privilege. */

LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES)NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)
ErrorHandler("AdjustTokenPrivileges enable failed.");

/* Prevent the system from shutting down. */

fResult = AbortSystemShutdown(NULL);

if (!fResult) {
ErrorHandler("AbortSystemShutdown failed.");
}

/* Disable shutdown privilege. */

tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);

if (GetLastError() != ERROR_SUCCESS)
ErrorHandler("AdjustTokenPrivileges disable failed.");

break;

+$#A System Information Functions
Following are the functions used to retrieve or set system information, shut down the system, or start Windows Help.
AbortSystemShutdown_win32_AbortSystemShutdownExpandEnvironmentStrings_win32_ExpandEnvironmentStringsGetComputerName_win32_GetComputerNameGetKeyboardType_win32_GetKeyboardTypeGetSysColor_win32_GetSysColorGetSystemDirectory_win32_GetSystemDirectoryGetSystemInfo_win32_GetSystemInfoGetSystemMetrics_win32_GetSystemMetricsGetThreadDesktop_win32_GetThreadDesktopGetUserName_win32_GetUserNameGetVersion_win32_GetVersionGetVersionEx_win32_GetVersionExGetWindowsDirectory_win32_GetWindowsDirectoryInitiateSystemShutdown_win32_InitiateSystemShutdownSetComputerName_win32_SetComputerNameSetSysColors_win32_SetSysColorsSystemParametersInfo_win32_SystemParametersInfoWinHelp_win32_WinHelp

Obsolete or Deleted Functions
DOS3Call_win32_DOS3CallGetCurrentPDB_win32_GetCurrentPDBGetCurrentTask_win32_GetCurrentTaskGetDOSEnvironment_win32_GetDOSEnvironmentGetWinFlags_win32_GetWinFlagsIsTask_win32_IsTask

+$#AK System Messages
System messages provide the way to uniformly notify all applications and other components in the system of changes that may affect their operation and access to resources. Applications and other components use and process system messages to take advantage of new resources when they become available and to prevent loss of data when existing resources become unavailable.
+$#A About System Messages
The Windows operating system provides two predefined classes of system messages: device messages and power messages. Device messages notify applications and installable drivers of device change events; power messages notify these of power management events. Applications and other components can also define and use their own system messages to provide for notification of other types of events.
This overview describes system messages, system-message broadcasts, and the specific class of system messages called device messages. For more information about power messages, see Power Management_win32_Power_Management.
+$#A System Messages and System-Message Broadcasts
Each system message consists of a message identifier and two 32-bit parameters, wParam and lParam. The message identifier is a unique value that specifies the message purpose. The parameters provide additional information that is message-specific, but the wParam parameter is often a notification value that further specifies the message purpose.
A system-message broadcast is simply the sending of a system message to components in the system. You broadcast a system message by using the BroadcastSystemMessage_win32_BroadcastSystemMessage function and specifying the recipients of the message. Rather than specify individual recipients, you must specify one or more types of recipients. These types are applications, installable drivers, Windows-based network drivers, and system-level device drivers. BroadcastSystemMessage sends messages to all members of each type you specify.
Most applications do not broadcast system messages. Instead, they process system messages sent by other components. The operating system typically broadcasts system messages in response to changes that usually take place within system-level device drivers. The device driver or related component generates the system message and broadcasts it to applications and other components to notify them of the change. For example, the subsystem responsible for disk drives generates and broadcasts a system message whenever the device driver for the floppy disk drive detects a change of media such as when the user inserts a disk in the drive.
Applications receive system messages through the window procedure of their top-level windows. System messages are not sent to child windows. The action an application takes in response to a system message depends on the message. Some system messages, called query messages, require the application to respond by returning either TRUE or FALSE to indicate whether the system should continue to broadcast the message to other recipients.
+$#A Custom System Messages
You can create your own system messages and use them to coordinate activities between applications and other components in the system. This is especially useful if you have created your own installable drivers or system-level device drivers. Your custom system messages can carry information to and from your driver and the applications that use the driver.
You broadcast custom system messages using the BroadcastSystemMessage_win32_BroadcastSystemMessage function.(System-level device drivers use a related, system-level function.) The function sends the messages to these recipients in this order: system-level device drivers, Windows-based network drivers, installable drivers, and applications. This means that system-level device drivers, if chosen as recipients, always get the first opportunity to respond to a system message. Within a given recipient type, no driver is guaranteed to receive a given message before any other driver. This means a system message intended for a specific driver must have an globally-unique message identifier so that no other driver unintentionally processes it.
Query messages are a useful way to poll recipients for permission to carry out a given action. You can generate your own a query messages by setting the BSF_QUERY value in the dwFlags parameter when calling BroadcastSystemMessage. Each recipient of the query message must return TRUE for the function to send the message to the next recipient. If any recipient returns FALSE, the broadcast ends immediately and the function returns 0.
You can create installable drivers that broadcast and process system messages. An installable driver is a dynamic-link library (DLL) that exports a DriverProc function. The driver receives system messages through its DriverProc function and can broadcast messages using BroadcastSystemMessage. Installable drivers are typically used to support multimedia devices, such as sound boards, but can be used for other devices and purposes too.
Windows-based network drivers are dynamic-link libraries that provide the underlying support for applications that use the Windows network functions to connect to and browse network resources. System-level device drivers are operating-system-specific executable components that provide direct access to and management of the hardware devices of the computer. The details regarding how these components process system messages is beyond the scope of this book.
+$#A Device Messages and Device Change Events
Device messages are system messages that notify applications and other components of device change events. These events occur whenever the system detects a change to the system hardware such as when the user docks or undocks a laptop computer, or inserts or removes a device such as a PCMCIA card. Change events can occur while the system is running or when the system resumes operation after temporarily being suspended.
To help ensure that applications and installable drivers do not lose data when devices become unavailable, the operating system monitors the hardware configuration and sends device message to the applications and installable drivers to notify them of the changes and to give them the opportunity to prepare for the changes before they occur.
For each event, the system broadcasts a WM_DEVICECHANGE_win32_WM_DEVICECHANGE message to all applications and installable drivers. In this message, the wParam parameter identifies the event type and the lParam parameter is usually the address of event-specific data.
The event-specific data identifies the device and provides additional detail about the event. The format of this data depends on the device type, but the first few bytes always has the same format as the DEV_BROADCAST_HDR_win32_DEV_BROADCAST_HDR_str structure. This means you can always check the dbch_devicetype member in the data to determine the device type.
The system sends a DBT_DEVICEARRIVAL_win32_DBT_DEVICEARRIVAL message (that is, a WM_DEVICECHANGE message with wParam set to DBT_DEVICEARRIVAL) whenever a device has been inserted and is available for use. Applications typically check the device type and begin using the device immediately if appropriate.
The system sends a DBT_DEVICEQUERYREMOVE_win32_DBT_DEVICEQUERYREMOVE to request permission to remove a device. If an application determines that it needs the device, it can deny this request and cancel the removal by returning FALSE. To determine whether it needs the device, an application can display a dialog box to prompt the user for instructions. If the application does not need the device, it must return TRUE. The system immediately sends a DBT_DEVICEQUERYREMOVEFAILED_win32_DBT_DEVICEQUERYREMOVEFAILED message if any application or driver canceled a previous request to remove a device.
The system sends a DBT_DEVICEREMOVEPENDING_win32_DBT_DEVICEREMOVEPENDING message as a last warning before a device is removed. At this point, the application cannot cancel the removal, so if it is using the device it must prepare for its removal to prevent loss of data. This is especially important when a network connection is being removed. The application must determine whether any of its open files or pipes are on that connection. It can do this by comparing the network resource identifier in the event-specific data of the message with the resource identifiers previously obtained for the files and pipes. The system sends a DBT_DEVICEREMOVECOMPLETE_win32_DBT_DEVICEREMOVECOMPLETE when a device has been removed and is no longer available.
The system sends DBT_DEVICETYPESPECIFIC_win32_DBT_DEVICETYPESPECIFIC message whenever a device-specific event occurs. It sends a DBT_CONFIGCHANGED_win32_DBT_CONFIGCHANGED message if current configuration has changed, such as when the operational parameters of a device have been changed by the user.
+$#A Using System Messages
· Processing a Request to Remove a Device_win32_Processing_a_Request_to_Remove_a_Device

+$#A Processing a Request to Remove a Device
An application receives a DBT_DEVICEQUERYREMOVE_win32_DBT_DEVICEQUERYREMOVE message when a component in the system has decided to remove a given device. When the application receives this message, it should determine whether it is using the given device and either cancel or prepare for the removal. In the following example, the application processes the message by checking the type of device that is to be removed.
PDEV_BROADCAST_HDR pdbch;PDEV_BROADCAST_VOLUME pdbcv;case WM_DEVICECHANGE: pdbch = (PDEV_BROADCAST_HDR) lParam; switch (pdbch->dbch_devicetype) { case DBT_DEVTYP_VOLUME: pdbcv = (PDEV_BROADCAST_VOLUME) pdbch; if (pdbcv->dbcv_flags == DBTF_MEDIA) // pdbcv->dbcv_unitmask identifies which logical drive } return TRUE; default: return TRUE; }
+$#A System Message Reference
This section describes the functions, messages, and structures that support system messages.
+$#A System Message Functions
This section describes the functions that support system messages.
BroadcastSystemMessage_win32_BroadcastSystemMessage
+$#A System Messages Reference
This section describes the device messages.
DBT_CONFIGCHANGED_win32_DBT_CONFIGCHANGEDDBT_DEVICEARRIVAL_win32_DBT_DEVICEARRIVALDBT_DEVICEQUERYREMOVE_win32_DBT_DEVICEQUERYREMOVEDBT_DEVICEQUERYREMOVEFAILED_win32_DBT_DEVICEQUERYREMOVEFAILEDDBT_DEVICEREMOVECOMPLETE_win32_DBT_DEVICEREMOVECOMPLETEDBT_DEVICEREMOVEPENDING_win32_DBT_DEVICEREMOVEPENDINGDBT_DEVICETYPESPECIFIC_win32_DBT_DEVICETYPESPECIFICWM_DEVICECHANGE_win32_WM_DEVICECHANGE
+$#A System Message Structures
This section describes the structures associated with device messages.
_DEV_BROADCAST_USERDEFINED_win32__DEV_BROADCAST_USERDEFINED_strDEV_BROADCAST_HDR_win32_DEV_BROADCAST_HDR_strDEV_BROADCAST_OEM_win32_DEV_BROADCAST_OEM_strDEV_BROADCAST_PORT_win32_DEV_BROADCAST_PORT_strDEV_BROADCAST_VOLUME_win32_DEV_BROADCAST_VOLUME_str

+$#AK Tape Backup
The tape functions provided in the Microsoft® Win32® application programming interface (API) enable backup applications to read from and write to a tape, initialize a tape, and retrieve tape or tape drive information.
+$#A About Tape Backup
In Microsoft® Windows™, a tape volume consists of a recording medium and its physical carrier. The entire length of tape in a volume is not available for recording data. Short sections at the beginning and the end of the tape are reserved for attaching the tape to the hubs in the carrier. The first position on the tape where data can be recorded is the called the beginning-of-medium marker, and the last position is called the end-of-medium marker.
Every tape volume has one or more partitions. A partition is a portion of the volume, containing its own beginning and ending points, that does not overlap with any other portion of the volume. Each partition has three predefined positions. The first position in a partition where you can record data is called the beginning-of-partition marker, and the last is called end-of-partition marker. The early-warning position is located immediately before the end-of-partition marker. The early-warning position notifies tape applications to transfer buffered data to the tape before reaching the end-of-partition marker.
The area between a partition's beginning and ending points is typically divided into sections by filemarks or setmarks. Filemarks and setmarks are special recorded elements that do not contain user data; they simply divide the partition into smaller areas to provide an address scheme. Filemarks and setmarks serve similar purposes, but setmarks provide faster positioning on high-capacity tape drives.
Typically, tape devices support filemarks and setmarks. Support of both enables tape data to be formatted such that setmarks separate data from different disk volumes and filemarks separate data from individual files on a disk volume.
Another recorded element that denotes locations on the tape is an erase gap, an area of erased tape or a pattern that the device does not recognize as a mark or as user data.
There are three types of filemarks. A short filemark contains a short erase gap that cannot be overwritten unless the write operation is performed from the beginning of the partition or from an earlier long filemark. A long filemark contains a long erase gap that enables an application to position the tape at the beginning of the filemark and to overwrite the filemark and the erase gap. A normal filemark does not contain an erase gap. Tape devices that use filemarks support either short and long filemarks or normal filemarks, but not all three.
The area on a partition between setmarks or filemarks is available for recording data. A unit of data written to or read from a tape is referred to as a block.
+$#A Tape Initialization
An application must use the CreateFile_win32_CreateFile function to create a handle of a tape device. This handle is used in subsequent operations on the tape in the device.
Before an application writes to a tape, the tape must be formatted according to the needs of the application and the capabilities of the tape drive being used. The CreateTapePartition_win32_CreateTapePartition function reformats a tape, creating on it a given number of partitions of a specified size.
The PrepareTape_win32_PrepareTape function prepares a tape to be accessed or removed. This function can load, unload, lock, or unlock a tape. This function can also tension the tape by moving the tape to the end of the tape and back to the beginning.
To retrieve and set information about a tape and tape drive, an application uses the GetTapeParameters_win32_GetTapeParameters, SetTapeParameters_win32_SetTapeParameters, and GetTapeStatus_win32_GetTapeStatus functions.
GetTapeParameters retrieves information that describes a tape or a tape drive. The tape information includes the tape's type, density, and block size; the number of partitions on the tape; the amount of tape remaining; and so on. The tape drive information includes the drive's default block size, the maximum partition count, and the features that are supported.
SetTapeParameters either sets the tape block size or sets the tape drive flags that indicate whether the drive supports hardware error correction, data compression, data padding, or any combination of the three.
GetTapeStatus indicates whether the tape drive is ready to process tape commands.
+$#A Tape Input and Output
The Win32 API provides several functions that applications can use to perform input and output (I/O) on a tape drive. Tape I/O is similar to I/O performed on a communications device.
When performing tape I/O, some tape drives store tape firmware information in the first few blocks on a tape, typically using some portion of the first 100 blocks. Applications should not use those blocks. More specific information on this subject is available from individual tape system manufacturers. In general, an application that skips the first 100 blocks on a tape will avoid tape drive idiosyncracies.
The GetTapePosition_win32_GetTapePosition and SetTapePosition_win32_SetTapePosition functions retrieve and move the current tape position. The WriteTapemark_win32_WriteTapemark function writes a specified number of setmarks, filemarks, short filemarks, and long filemarks. The EraseTape_win32_EraseTape function erases all or part of a tape.
The ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions read and write file data from and to the tape. The data is read and written in complete blocks. If the tape's block size is 512 bytes, all read and write operations must use buffers that are simple integer multiples of that block size: 512, 1024, 1536, 2048, and so on. Most, if not all, drives only allow a write operation after the tape is rewound or after a read operation produces an end-of-data error message.
If ReadFile encounters a filemark, the data up to the filemark is read and the function fails. (The GetLastError_win32_GetLastError function returns an error code indicating the type of filemark that was encountered.) To continue reading beyond the filemark, an application must move the tape past the filemark and call ReadFile again.
ReadFile and WriteFile read and write only the data stream. The BackupRead_win32_BackupRead and BackupWrite_win32_BackupWrite functions read and write all the streams associated with a file. These include data, extended attributes, security, and alternative data streams. The security and alternate data streams are relevant only on the new technology file system (NTFS) partition.
The BackupSeek_win32_BackupSeek function seeks forward in a file initially accessed by BackupRead or BackupWrite. This function enables an application to skip information that causes access errors.
If an application needs to access only the file data, it should use ReadFile_win32_ReadFile and WriteFile_win32_WriteFile. These functions can also read alternative data streams if the streams were created by using the CreateFile_win32_CreateFile function.
A tape-backup application must use BackupRead and BackupWrite to copy all information pertaining to a file. However, these functions do not read or write file characteristics such as attributes, file creation time, and so on. Applications must use the file input and output functions, such as GetFileAttributes_win32_GetFileAttributes and SetFileAttributes_win32_SetFileAttributes, to retrieve and set those values.
+$#A Using Tape Backup
· Creating a backup application_win32_Creating_a_Backup_Application
· Backing up and restoring Posix file links_win32_Backing_Up_and_Restoring_Posix_File_Links

+$#A Creating a Backup Application
To perform any input or output on a tape, a backup application must first obtain a handle of the tape device. The following code sample shows how to use the CreateFile_win32_CreateFile function to open a tape device.
HANDLE hTape; /* handle to tape device */

hTape = CreateFile(
"\\\\.\\TAPE0", /* name of tape device to open */
GENERIC_READ | GENERIC_WRITE, /* read-write access */
0, /* not used */
0, /* not used */
OPEN_EXISTING, /* required for tape devices */
0, /* not used */
NULL); /* not used */

To back up a directory tree to a tape, an application must use the FindFirstFile_win32_FindFirstFile and FindNextFile_win32_FindNextFile functions to traverse the directory tree. Each time a file is found, the application should get the file's attributes by using the GetFileAttributes_win32_GetFileAttributes function. If there are hard links, the application should determine the number of links and save the file's unique identifier in a table for future comparisons. The first time a file is found, the application should use CreateFile_win32_CreateFile to open the file and the BackupRead_win32_BackupRead function to begin the backup. Then it can use the WriteFile_win32_WriteFile function repeatedly to transfer all the information in the buffer filled by BackupRead to the tape. The second time a file is found (checked against the table of file identifiers when there are hard links), the application can simply write the general file information to the tape, followed by a stream whose identifier is BACKUP_LINK.
When restoring files from tape to disk, an application must use the CreateFile, BackupWrite_win32_BackupWrite, and ReadFile_win32_ReadFile functions. For each file on the tape, the application should use CreateFile to create a new file on disk and BackupWrite to begin restoring the file. Then it should use ReadFile repeatedly until all the information for the file has been read from the tape into the buffer filled by BackupWrite. If one of the streams in the BackupWrite buffer has a BACKUP_LINK stream identifier, the application must establish a hard link. If the data needed to establish the link does not exist, BackupWrite fails. The application can use a preexisting catalog to locate and restore the original data, or it can notify the user that the file data to be restored is in a different location.
+$#A Backing Up and Restoring Posix File Links
A backup application can use the BackupWrite_win32_BackupWrite function to recreate Posix file links.
The BackupRead_win32_BackupRead function does not return Posix file link data. A backup application must maintain the link information itself. The following pseudocode presents algorithms for backing up and restoring Posix file link data along with other file information.
PseudoCode Algorithm for Backing Up Posix File Links
1. Initialize and empty a list of known links.
2. While there are more files to back up
3. Read the disk and get the next file.
4. Open the file for read.
5. Call GetFileInformationByHandle() to get the
NumberOfLinks and the FileIndex.
6. If the NumberOfLinks is greater than 1
7. Search the list of know links looking for
the same FileIndex.
8. If a match is NOT found
9. add the full path of the file and the
FileIndex to the list.
10. Call BackupRead() to copy all data to
your backup media.
10. Else
11. Mark the data as a LINK on your backup media
11. store the full path from the list
to your backup media.
12. Endif
13. Else
14. Call BackupRead() to copy all data to your
backup media.
15. Endif
16. EndWhile

PseudoCode Algorithm for Restoring Posix File Links
1. While there are more files to restore
2. If the file is a LINK
3. use the full path which was saved as data
to open the file.
4. Initialize a WIN32_STREAM_ID structure with
dwStreamId equal to BACKUP_LINK.
5. Initialize the dwStreamAttributes to 0.
6. Initialize the dwStreamNameSize to 0.
7. Initialize a buffer containing the full path
of the file you are restoring in UNICODE.
8. Initialize the dwStreamSizeHigh to 0.
9. Initialize the dwStreamSizeLow to the size
in bytes of the buffer containing the full path.
10. Call BackupWrite() with the WIN32_STREAM_ID
11. Call BackupWrite() with the buffer containing
the full path.
13. Else
14. Call BackupWrite() with the data stored on
your backup media.
15. Endif
16. EndWhile

+$#A Tape Backup Functions
Following are the functions used for tape backup.
BackupRead_win32_BackupReadBackupSeek_win32_BackupSeekBackupWrite_win32_BackupWriteCreateTapePartition_win32_CreateTapePartitionEraseTape_win32_EraseTapeGetTapeParameters_win32_GetTapeParametersGetTapePosition_win32_GetTapePositionGetTapeStatus_win32_GetTapeStatusPrepareTape_win32_PrepareTapeSetTapeParameters_win32_SetTapeParametersSetTapePosition_win32_SetTapePositionWriteTapemark_win32_WriteTapemark

+$#AK Time
The Microsoft® Win32® application programming interface (API) provides a variety of date and time functions, structures, and messages to retrieve and set the date and time for the system, files, and the local time zone.
+$#A About Time
This overview describes various functions, structures, and messages and explains how to use them to examine and modify dates and times.
For information about related topics, see the following overviews:
· Files_win32_Files
· File Systems_win32_File_Systems

+$#A System Time
System time is the current date and time of day. Windows keeps system time so that your applications have ready access to accurate time. When Windows first starts, it sets the system time to a value based on the real-time clock of the computer and thereafter regularly updates the time.
Windows bases system time on coordinated universal time (UTC). UTC-based time is loosely defined as the current date and time of day in Greenwich, England. You can retrieve the system time by using the GetSystemTime_win32_GetSystemTime function. GetSystemTime copies the time to a SYSTEMTIME_win32_SYSTEMTIME_str structure that contains individual members for month, day, year, weekday, hour, minute, second, and milliseconds.
Because system time is UTC-based, the time copied to the SYSTEMTIME structure may not be the same as the local time¾the date and time of day for your time zone. You can retrieve the local time by using the GetLocalTime_win32_GetLocalTime function. GetLocalTime converts the system time to a local time based on the current time-zone settings and copies the result to a SYSTEMTIME structure.
The current time-zone settings control how Windows converts between UTC and local time. You can retrieve the current time-zone settings by using the GetTimeZoneInformation_win32_GetTimeZoneInformation function. The function copies the setting to a TIME_ZONE_INFORMATION_win32_TIME_ZONE_INFORMATION_str structure and returns a value indicating whether local time is currently in standard or daylight-savings time. You can set the time-zone settings by using the SetTimeZoneInformation_win32_SetTimeZoneInformation function.
You can set the system time by using the SetSystemTime_win32_SetSystemTime or SetLocalTime_win32_SetLocalTime function. SetSystemTime assumes you have specified a UTC-based time; SetLocalTime assumes local time and converts to UTC before setting the system time. If you change the system time, you should send the WM_TIMECHANGE_win32_WM_TIMECHANGE message to all top-level windows by using the SendMessage_win32_SendMessage function.
+$#A Windows Time
Windows time is the number of milliseconds elapsed since Windows started running. To ensure that applications designed for earlier versions of Windows continue to run successfully, the GetTickCount_win32_GetTickCount and GetCurrentTime_win32_GetCurrentTime functions return the current Windows time. However, if your application requires the current date and time of day, you should use the GetSystemTime_win32_GetSystemTime or GetLocalTime_win32_GetLocalTime function whenever possible.
Because GetTickCount and GetCurrentTime are identical in operation, you should use GetTickCount to retrieve Windows time.You typically use GetTickCount to compare the current Windows time with the time returned by the GetMessageTime_win32_GetMessageTime function. GetMessageTime returns the Windows time when the given message was created.
Windows time is stored as a 32-bit value, which means Windows can record no more than 2^32 millisecond intervals before the 32-bit value overflows to zero. This is approximately 49.7 days. If you use Windows time, check for the overflow condition when comparing times.
+$#A File Times
A file time represents the specific date and time at which a given file was created, last accessed, or last written to. Windows records file times whenever applications create, access, and write to files. Windows records the times using a 64-bit value specifying the number of 100-nanosecond intervals that have elapsed since 12:00 A.M. January 1, 1601 (UTC). Writing to a file changes the last write time; writing to or reading from the file (including running an executable file) changes the last access time.

Note Not all file systems can record creation and last access time. In particular, the file allocation table (FAT) file system records only last-write times.

You can retrieve the file times for a given file by using the GetFileTime_win32_GetFileTime function. GetFileTime copies the creation, last access, and last write times to individual FILETIME_win32_FILETIME_str structures. Each structure consists of two 32-bit values that combine to form the single 64-bit value. You can also retrieve the file times by using the FindFirstFile_win32_FindFirstFile and FindNextFile_win32_FindNextFile functions. These functions copy the times to FILETIME structures in a WIN32_FIND_DATA_win32_WIN32_FIND_DATA_str structure.
You can set the file times for a file by using the SetFileTime_win32_SetFileTime function. This function lets you modify creation, last access, and last write times without changing the content of the file. You can compare the times of different files by using the CompareFileTime_win32_CompareFileTime function. The function compares two file times and returns a value indicating which time is greater or returns zero if the times are equal.
To make a file time more meaningful to a user, you can extract the month, day, year, and time of day from a file time by using the FileTimeToSystemTime_win32_FileTimeToSystemTime function. FileTimeToSystemTime converts the file time and copies the individual values for date and time of day to a SYSTEMTIME_win32_SYSTEMTIME_str structure.
Windows records all file times in UTC-based times, but you can convert a file time to the local time for your time zone by using the FileTimeToLocalFileTime_win32_FileTimeToLocalFileTime function. Before displaying a file time to a user, applications typically convert the file time to local time, then extract the month, day, year and time of day using FileTimeToSystemTime.
If you plan to modify file times for given files, you can convert a date and time of day to a file time by using the SystemTimeToFileTime_win32_SystemTimeToFileTime function. If the original date and time of day are given in the local time for your time zone, you can convert the resulting file time to UTC by using the LocalFileTimeToFileTime_win32_LocalFileTimeToFileTime function. Always make sure the file times you set using SetFileTime are valid UTC-based times.
+$#A MS-DOS Date and Time
MS-DOS date and MS-DOS time are packed 16-bit values that specify the month, day, year, and time of day an MS-DOS file was last written to. MS-DOS records the date and time whenever an MS-DOS application creates or writes to a file. MS-DOS applications retrieve this date and time using MS-DOS functions. When you use the GetFileTime_win32_GetFileTime function to retrieve the file times for files that were created by MS-DOS, GetFileTime automatically converts MS-DOS dates and times to UTC-based times.
If you encounter an MS-DOS date and time that has not been converted, you can convert it to a UTC-based time by using the DosDateTimeToFileTime_win32_DosDateTimeToFileTime function. This function copies the converted date and time to a FILETIME_win32_FILETIME_str structure. You can convert the value back to an MS-DOS date and time by using the FileTimeToDosDateTime_win32_FileTimeToDosDateTime function.
+$#A Using Time
· Retrieving the last-write time_win32_Retrieving_the_Last_Write_Time
· Changing a file time to the current time_win32_Changing_a_File_Time_to_the_Current_Time
· Setting the system time_win32_Setting_the_System_Time

+$#A Retrieving the Last-Write Time
The following example retrieves the last-write time for a file, converts it to local time based on the current time-zone settings, and creates a date and time string that can be shown to the user.
// GetLastWriteTime - retrieves the last-write time and converts the// time to a string// Return value - TRUE if successful, FALSE otherwise// hFile - must be a valid file handle// lpszString - address of buffer to receive stringBOOL GetLastWriteTime(HANDLE hFile, LPSTR lpszString){ FILETIME ftCreate, ftAccess, ftWrite, ftLocal; SYSTEMTIME stCreate; // Retrieve the file times for the file. if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite)) return FALSE; // Convert the last-write time to local time. if (!FileTimeToLocalFileTime(&ftWrite, &ftLocal)) return FALSE; // Convert the local file time from UTC to system time. FileTimeToSystemTime(&ftLocal, &stCreate); // Build a string showing the date and time. wsprintf(lpszString, "%02d/%02d/%d %02d:%02d", stCreate.wDay, stCreate.wMonth, stCreate.wYear, stCreate.wHour, stCreate.wMinute); return TRUE;}
+$#A Changing a File Time to the Current Time
The following example sets the last-write time for a file to the current system time.
// SetFileToCurrentTime - sets last write time to current system time// Return value - TRUE if successful, FALSE otherwise// hFile - must be a valid file handleBOOL SetFileToCurrentTime(HANDLE hFile){ FILETIME ft; SYSTEMTIME st; BOOL f; GetSystemTime(&st); // gets current time SystemTimeToFileTime(&st, &ft); // converts to file time format f = SetFileTime(hf, // sets last-write time for file (LPFILETIME) NULL, (LPFILETIME) NULL, &ft); return f;}
+$#A Setting the System Time
The following example sets the system time.
// SetNewTime - sets system time// Return value - TRUE if successful, FALSE otherwise// hour - new hour (0-23)// minutes - new minutes (0-59)
BOOL SetNewTime(WORD hour, WORD minutes){ SYSTEMTIME st; char *pc; GetSystemTime(&st); // gets current time st.wHour = hour; // adjusts hours st.wMinute = minutes; // and minutes if (!SetSystemTime(&st)) // sets system time return FALSE; return TRUE;}
+$#A Time Functions
Following are the functions used with time formats.
CompareFileTime_win32_CompareFileTimeDosDateTimeToFileTime_win32_DosDateTimeToFileTimeFileTimeToDosDateTime_win32_FileTimeToDosDateTimeFileTimeToLocalFileTime_win32_FileTimeToLocalFileTimeFileTimeToSystemTime_win32_FileTimeToSystemTimeGetCurrentTime_win32_GetCurrentTimeGetFileTime_win32_GetFileTimeGetLocalTime_win32_GetLocalTimeGetSystemTime_win32_GetSystemTimeGetSystemTimeAdjustment_win32_GetSystemTimeAdjustmentGetSystemTimeAsFileTime_win32_GetSystemTimeAsFileTimeGetTickCount_win32_GetTickCountGetTimeZoneInformation_win32_GetTimeZoneInformationLocalFileTimeToFileTime_win32_LocalFileTimeToFileTimeSetFileTime_win32_SetFileTimeSetLocalTime_win32_SetLocalTimeSetSystemTime_win32_SetSystemTimeSetSystemTimeAdjustment_win32_SetSystemTimeAdjustmentSetTimeZoneInformation_win32_SetTimeZoneInformationSystemTimeToFileTime_win32_SystemTimeToFileTimeSystemTimeToTZSpecificLocalTime_win32_SystemTimeToTzSpecificLocalTime

+$#AK Window Stations and Desktops
This overview describes the new window station and desktop functionality, and is primarily intended for developers of Win32 services. Developers of typical applications that will be run by the logged-on user do not need to be concerned about window stations and desktops.
Beginning with Microsoft® Windows NT™ version 3.5, the Win32® API provides new window station and desktop functionality that enables Win32 services to call USER32 and GDI32 functions regardless of the logon account in which the service is running. This new functionality enables Win32 services to make these calls while maintaining the security of applications running in the account of the logged-on interactive user.
For more information on how Win32 services use the window station and desktop support, see Interactive Services_win32_Interactive_Services.
+$#A Window Stations
A window station is a secure object that contains a clipboard, a set of global atoms and a group of desktop objects. The interactive window station assigned to the logon session of the interactive user also contains the keyboard, mouse, and display device. The interactive window station is visible to the user and can receive input from the user. All other window stations are noninteractive, which means that they cannot be made visible to the user, and cannot receive user input.
Applications can use the following functions for manipulating window station objects:
Function Description
CloseWindowStation_win32_CloseWindowStation Closes a specified window station.
CreateWindowStation_win32_CreateWindowStation Creates a new window station.
EnumWindowStations_win32_EnumWindowStations Enumerates the window stations in the system by repeatedly calling an application-defined EnumWindowStationProc_win32_EnumWindowStationProc callback function.
GetProcessWindowStation_win32_GetProcessWindowStation Returns a handle of the window station assigned to the calling process.
GetUserObjectInformation_win32_GetUserObjectInformation Gets information about a window station or desktop object.
GetUserObjectSecurity_win32_GetUserObjectSecurity Gets security information for a window station or desktop object.
OpenWindowStation_win32_OpenWindowStation Opens a handle of an existing window station.
SetProcessWindowStation_win32_SetProcessWindowStation Assigns a specified window station to the calling process.
SetUserObjectInformation_win32_SetUserObjectInformation Sets information about a window station or desktop object.
SetUserObjectSecurity_win32_SetUserObjectSecurity Sets security information for a window station or desktop object.

+$#A Desktops
A desktop is a secure object contained within a window station. A desktop has a logical display surface and contains windows, menus, and hooks. A window station can have multiple desktops. Only the desktops of the interactive window station can be visible and receive user input. On the interactive window station, only one desktop at a time is active. This active desktop, also known as the input desktop, is the one that is currently visible to the user and that receives user input. Applications can use the OpenInputDesktop_win32_OpenInputDesktop function to get a handle of the input desktop. Applications that have the necessary access can use the SwitchDesktop_win32_SwitchDesktop function to specify a different input desktop.
Invisible, noninteractive window stations enable Win32 services that do not have access to the interactive window station to start GUI applications. Applications running in a noninteractive window station cannot receive input or be visible. For example, the schedule service could use this support to start a GUI backup application on a remote computer on which the interactive window station was not accessible. For more information, see Interactive Services_win32_Interactive_Services.
Applications can use the following functions for manipulating desktop objects:
Function Description
CloseDesktop_win32_CloseDesktop Closes a specified desktop.
CreateDesktop_win32_CreateDesktop Creates a new desktop on a specified window station.
EnumDesktops_win32_EnumDesktops Enumerates the desktops on a specified window station by repeatedly calling an application-defined EnumDesktopProc_win32_EnumDesktopProc callback function.
EnumDesktopWindows_win32_EnumDesktopWindows Enumerates the windows on a specified desktop by repeatedly calling an application-defined EnumWindowsProc_win32_EnumWindowsProc callback function.
GetThreadDesktop_win32_GetThreadDesktop Returns a handle of the desktop assigned to the calling thread.
GetUserObjectInformation_win32_GetUserObjectInformation Gets information about a window station or desktop object.
GetUserObjectSecurity_win32_GetUserObjectSecurity Gets security information for a window station or desktop object.
OpenDesktop_win32_OpenDesktop Opens a handle of an existing desktop.
SetThreadDesktop_win32_SetThreadDesktop Assigns a specified desktop to the calling thread.
SetUserObjectInformation_win32_SetUserObjectInformation Sets information about a window station or desktop object.
SetUserObjectSecurity_win32_SetUserObjectSecurity Sets security information for a window station or desktop object.

+$#A Window Station and Desktop Creation
The system automatically creates the interactive window station. When an interactive user logs on, the system associates the interactive window station with the user's logon session. The system also creates the default input desktop for the interactive window station.
When a noninteractive process such as a Win32 service attempts to connect to Win32 and no window station exists for the process' logon session, Win32 attempts to create a window station and desktop for the session. The name of the created window station is based on the logon session identifier, and the desktop is named "Default." The discretionary access control lists (DACLs) for the window station and desktop grant the following accesses to the service's user account:
Window Station: WINSTA_READATTRIBUTES | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | STANDARD_RIGHTS_REQUIRED
Desktop: DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_ENUMERATE | STANDARD_RIGHTS_REQUIRED
A process can use the CreateWindowStation_win32_CreateWindowStation function to create a new window station, and the CreateDesktop_win32_CreateDesktop function to create a new desktop.
+$#A Process Connection to a Window Station
A process automatically establishes a connection to a window station and desktop when it first calls a USER32 or GDI32 function (other than the window station or desktop functions) that must go to the Win32 server. The Win32 server determines the window station to which a process connects according to the following rules:
1. If the process has called the SetProcessWindowStation_win32_SetProcessWindowStation function, it connects to the window station specified in that call.
2. If the process did not call SetProcessWindowStation, it connects to the window station inherited from the parent process.
3. If the process did not call SetProcessWindowStation and did not inherit a window station, the Win32 server attempts to open for MAXIMUM_ACCESS and connect to a window station as follows:
a. If a window station name was specified in the lpDesktop member of the STARTUPINFO_win32_STARTUPINFO_str structure that was passed to the CreateProcess_win32_CreateProcess function when the process was created, the process connects to the specified window station.
b. Otherwise, if the process is running in the logon session of the interactive user, the process connects to the interactive window station.
c. If the process is running in a noninteractive logon session, the window station name is formed based on the logon session identifier and an attempt is made to open that window station. If the open operation fails because this window station does not exist, Win32 tries to create the window station and a default desktop.

+$#A Thread Connection to a Desktop
After a process connects to a window station_win32_Process_Connection_to_a_Window_Station, the Win32 server assigns a desktop to the thread making the connection to Win32. The desktop assigned during this connection process cannot be closed by calling the CloseDesktop_win32_CloseDesktop function. The Win32 server determines the desktop to assign to the thread according to the following rules:
1. If the thread has called the SetThreadDesktop_win32_SetThreadDesktop function, it connects to the specified desktop.
2. If the thread did not call SetThreadDesktop, it connects to the desktop inherited from the parent process.
3. If the thread did not call SetThreadDesktop and did not inherit a desktop, the Win32 server attempts to open for MAXIMUM_ACCESS and connect to a desktop as follows:
a. If a desktop name was specified in the lpDesktop member of the STARTUPINFO_win32_STARTUPINFO_str structure that was passed to the CreateProcess_win32_CreateProcess function when the process was created, the thread connects to the specified desktop.
b. Otherwise, the thread connects to the default desktop of the window station to which the process connected.
The desktop assigned during this connection process cannot be closed by calling the CloseDesktop_win32_CloseDesktop function.

+$#A Inherited Handles
When a process is connecting to a window station or desktop, the Win32 server searches the process's handle table for inherited handles. The Win32 server uses the first window station and desktop handles that it finds. If you want a child process to connect to a particular inherited window station or desktop, you must ensure that the only the desired handles are marked inheritable. If a child process inherits multiple window station or multiple desktop handles, the results of the window station and desktop connection are undefined.
Handles to a window station and desktop that the Win32 server opens while connecting a process to a window station or desktop are not inheritable.
+$#A Logoff Events
A logoff event occurs when a process calls the ExitWindows_win32_ExitWindows or ExitWindowsEx_win32_ExitWindowsEx function. The logoff event is identified by the window station of the process calling ExitWindowsEx.
When a logoff event occurs, the system notifies all processes in the system as follows:
· All windows of processes in the logon session being logged off receive the WM_QUERYENDSESSION_win32_WM_QUERYENDSESSION and WM_ENDSESSION_win32_WM_ENDSESSION messages. All windows on the desktops of the window station being logged off receive these messages, even if the windows belong to processes running in another logon session.
· Every process in the system receives the CTRL_LOGOFF_EVENT control signal. This signal does not indicate which session is being logged off. The system sends this signal after sending the window messages. For more information on control signals, see the SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler function.

If the process that called ExitWindowsEx is running in the logon session of the interactive user, all processes in the logon session are terminated. If the process calling ExitWindowsEx is in some other logon session, only the notifications are made; no processes are terminated.
+$#A Using Window Stations and Desktops
· Creating an interactive process_win32_Creating_an_Interactive_Process
· Interacting with the user by a win32 service_win32_Interacting_with_the_User_by_a_Win32_Service
· Handling logoff events_win32_Handling_Logoff_Events

+$#A Creating an Interactive Process
The following code fragment shows how a Win32 service process could use the STARTUPINFO_win32_STARTUPINFO_str structure in a CreateProcess_win32_CreateProcess call to create a process that has access to the user's interactive window station and default desktop. A noninteractive Win32 service could use this technique to interact with the logged on user. The new process could then use a named pipe or some other means of interprocess communication to communicate with the Win32 service.
To create an interactive process as shown in this example, a Win32 service must be logged in to the LocalSystem account.
#include <windows.h>

STARTUPINFO si;
PROCESS_INFORMATION ProcessInformation;

si.cb = sizeof(STARTUPINFO);
si.lpReserved = NULL;
si.lpTitle = NULL;
si.lpDesktop = "WinSta0\\Default";
si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
si.dwFlags = 0;;
si.wShowWindow = SW_SHOW;
si.lpReserved2 = NULL;
si.cbReserved2 = 0;
if (CreateProcess(NULL, lpszCmdLine, NULL, NULL, FALSE,
0, NULL, NULL, &si, &ProcessInformation)) {
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
}

+$#A Interacting with the User by a Win32 Service
For a noninteractive Win32 service to interact with the user, it must open the user's window station ("WinSta0") and desktop ("Default"). By default, only the logged-on user and Win 32 services running in the LocalSystem account are granted access to the user's window station and desktop. This means that services running in other accounts must either impersonate the user when opening the interactive window station and desktop, or have access granted to those accounts by the user.
For compatibility with future versions of Windows NT that may support multiple users and/or multiple desktops per user, the user's window station and desktop names should not be hard-coded. Client applications should pass the names of their window station and desktop to the service as part of the request to the service.
This code sample displays a message on the user's desktop in response to an RPC request from one of the user's applications. Note that a global variable dwGuiThreadId in this example is also used in the logoff handling example_win32_Handling_Logoff_Events. If the thread that displays the message box terminates after it is done with the message box, it is not necessary to save and restore the thread's desktop. It is necessary to restore the process's window station.
DWORD dwGuiThreadId = 0;

int
UserMessageBox(
RPC_BINDING_HANDLE h,
LPSTR lpszWindowStation,
LPSTR lpszDesktop,
LPSTR lpszText,
LPSTR lpszTitle,
UINT fuStyle)
{
DWORD dwThreadId;
HWINSTA hwinstaSave;
HDESK hdeskSave;
HWINSTA hwinstaUser;
HDESK hdeskUser;
int result;

/*
* Ensure connection to service window station and desktop, and
* save their handles.
*/
GetDesktopWindow();
hwinstaSave = GetProcessWindowStation();
dwThreadId = GetCurrentThreadId();
hdeskSave = GetThreadDesktop(dwThreadId);

/*
* Impersonate the client and connect to the User's
* window station and desktop.
*/
RpcImpersonateClient(h);
hwinstaUser = OpenWindowStation(lpszWindowStation, FALSE, MAXIMUM_ALLOWED);
if (hwinstaUser == NULL) {
RpcRevertToSelf();
return 0;
}
SetProcessWindowStation(hwinstaUser);
hdeskUser = OpenDesktop(lpszDesktop, 0, FALSE, MAXIMUM_ALLOWED);
RpcRevertToSelf();
if (hdeskUser == NULL) {
SetProcessWindowStation(hwinstaSave);
CloseWindowStation(hwinstaUser);
return 0;
}
SetThreadDesktop(hdeskUser);

/*
* Display message box.
*/
dwGuiThreadId = dwThreadId;
result = MessageBox(NULL, lpszText, lpszTitle, fuStyle);
dwGuiThreadId = 0;

/*
* Restore window station and desktop.
*/
SetThreadDesktop(hdeskSave);
SetProcessWindowStation(hwinstaSave);
CloseDesktop(hdeskUser);
CloseWindowStation(hwinstaUser);

return result;
}

+$#A Handling Logoff Events
Win32 services that interact with the user should be prepared to handle logoff events_win32_Logoff_Events. When a logoff event occurs, the Win32 service must close all handles to the user's window station and desktop.
This sample demonstrates how the message box in the interaction example code should be dismissed at logoff. The ConsoleCtrlHandler function in this example is a HandlerRoutine_win32_HandlerRoutine that was specified by a call to the SetConsoleCtrlHandler_win32_SetConsoleCtrlHandler function.
BOOL CALLBACK EnumProc(
HWND hwnd,
LPARAM lParam)
{
/*
* Send a WM_CLOSE to destroy the window, because DestroyWindow does
* not work across threads.
*/
SendMessage(hwnd, WM_CLOSE, 0, 0);
return TRUE;
}

BOOL ConsoleCtrlHandler(
DWORD dwCtrlType)
{
if (dwCtrlType == CTRL_LOGOFF_EVENT && dwGuiThreadId != 0) {
SetThreadDesktop(GetThreadDesktop(dwGuiThreadId));
EnumThreadWindows(dwGuiThreadId, EnumProc, 0);
}
return FALSE;
}

+$#AK Window Station and Desktop Reference
The following functions and structures are associated with window station and desktop features.
+$#A Window Station and Desktop Functions
Use the following functions to implement window station and desktop features:
CloseDesktop_win32_CloseDesktopCloseWindowStation_win32_CloseWindowStationCreateDesktop_win32_CreateDesktopCreateWindowStation_win32_CreateWindowStationEnumDesktops_win32_EnumDesktopsEnumDesktopWindows_win32_EnumDesktopWindowsEnumWindowStations_win32_EnumWindowStationsGetProcessWindowStation_win32_GetProcessWindowStationGetThreadDesktop_win32_GetThreadDesktopGetUserObjectInformation_win32_GetUserObjectInformationGetUserObjectSecurity_win32_GetUserObjectSecurityOpenDesktop_win32_OpenDesktopOpenInputDesktop_win32_OpenInputDesktopOpenWindowStation_win32_OpenWindowStationSetProcessWindowStation_win32_SetProcessWindowStationSetThreadDesktop_win32_SetThreadDesktopSetUserObjectInformation_win32_SetUserObjectInformationSetUserObjectSecurity_win32_SetUserObjectSecuritySwitchDesktop_win32_SwitchDesktop

+$#A Window Station and Desktop Structures
Use the following structure to manipulate window station and desktop objects:
USEROBJECTFLAGS_win32_USEROBJECTFLAGS_str

+$#AK End User Defined Characters
End-user-defined characters (EUDC) are customized characters that users install for viewing and printing documents.
This overview discusses the TrueType fonts that support end-user-defined characters and describes the functions used to access and manage these characters in Win32-based applications.
+$#A About End User Defined Characters
End-user-defined characters give users a way to form names and other words using characters that are not available in standard screen and printer fonts.
End-user-defined characters are available only in Asian-language versions of the Microsoft Windows operating system.
+$#A Character Sets and Fonts
An end-user-defined character is always associated with a double-byte character set (DBCS) and a TrueType font. Applications identify the specified character by using the character's assigned DBCS character value, and the operating system uses this value to locate shape and style information in a corresponding TrueType font. The shape and style information specifies how the character is drawn on the screen or printed page.
The DBCS character values that can be assigned depend on the specified character set. Each set has at least one range of reserved values for use as end-user-defined characters. The system or applications explicitly define these ranges by setting appropriate values under the EUDCCodeRange key in the registry. In the registry, each character set is identified by a unique code-page number.
To create an end-user-defined character, the user chooses a character value that is within the specified range and adds the shape and style information to the TrueType font in the entry that corresponds to that character value. Users create the shape and style information using an EUDC editor or by purchasing end-user-defined font packages from font vendors. Any DBCS TrueType font can contain end-user-defined characters. The font is called a "separate" EUDC font if it contains only end-user-defined characters. The font is an "integrated" EUDC font if it contains standard characters as well as end-user-defined characters.
Separate EUDC fonts are said to be either "typeface-aware" or "typeface-unaware." A typeface-unaware font is designed to be a general purpose font that can be used with fonts of different typeface styles and of different implementations, such as GDI raster, WIFE, device, and TrueType fonts. A typeface-aware font is designed for use with a specific TrueType font.
The system default EUDC font is a typeface-unaware font that the system automatically associates with all DBCS fonts except those TrueType fonts that have explicitly associated typeface-aware fonts. Applications set the system default EUDC font by setting the value of the SystemDefaultEUDCFont name under the EUDC key in the registry. Similarly, applications can associate typeface-aware fonts with corresponding TrueType fonts by specifying a typeface name and associated font file under the EUDC key. Separate EUDC fonts cannot be associated with integrated EUDC fonts.
The system "hides" the system default EUDC and typeface-aware fonts. This means applications cannot enumerate or otherwise examine these fonts using GDI functions. Applications, such as EUDC editors and Control Panel, must use the registry entries to add, modify, and delete EUDC fonts.
End-user-defined characters can also be used in Unicode-enabled applications. The reserved ranges for each character set are mapped to corresponding values in the Unicode private use area (values 0xE000 and higher). There are mappings for both Japanese and Chinese character sets. For a list of these mappings, see Japanese EUDC Ranges_win32_Japanese_EUDC_Ranges and Chinese EUDC Ranges_win32_Chinese_EUDC_Ranges.
+$#A Writing, Mapping, and Sorting Characters
Applications write end-user-defined characters to the screen or printer the same way as writing other characters, by using output functions such as TextOut_win32_TextOut and ExtTextOut_win32_ExtTextOut. These functions automatically retrieve character information from EUDC fonts if EUDC is enabled. When writing end-user-defined characters, the action of the text output function depends on the currently selected font. If the selected font is an integrated EUDC font, the function retrieves character information from that font. If the selected font is a DBCS TrueType font that has an associated typeface-aware EUDC font, the function retrieves information from the specified typeface-aware font. If the selected font does not have an associated typeface-aware font, the function retrieves information from the system default EUDC font. If the character is not in the system default EUDC font or there is no system default EUDC font, the function writes the default character defined by the selected font.
Applications can map end-user-defined characters to and from Unicode by using the MultiByteToWideChar_win32_MultiByteToWideChar and WideCharToMultiByte_win32_WideCharToMultiByte functions. The MultiByteToWideChar function maps an end-user-defined character to a character position in the Unicode private use area. The WideCharToMultiByte function maps a character in the private use area to its multibyte character counterpart, if such a mapping exists. The code page specified in this function must contain an EUDC code range for the mapping to occur. If it does not, the function returns the default character for any characters in the Unicode private use area.
Both MultiByteToWideChar and WideCharToMultiByte preserve the "round trip" mapping of end-user-defined characters from a code page to Unicode and back to the original code page. But these functions do not necessarily preserve mapping to other code pages. For example, if a character from code page 932 is mapped to Unicode, then mapped from Unicode to another code page with an EUDC range, it is likely that the original character will be mapped to a different end-user-defined character in the destination code page. Similarly, mapping a Unicode string to a code page that has an EUDC range may have unintended results. If the Unicode string contains a private-use code point, that code point may be mapped to an end-user-defined character whether or not the code point actually represents such a character.
Applications can compare strings that contain end-user-defined characters by using the CompareStringA function. The function maps the characters to the Unicode end user zone before comparing character values. Applications can create a sort key for the string by using the LCMapStringA function and the LCMAP_SORTKEY value. This function maps characters to Unicode first. All characters in the private use area are sorted after all other Unicode characters. Within the area, characters are sorted in numerical order. If an application attempts to retrieve CTYPE ifnormation for an end-user-defined character by using the GetStringTypeA_win32_GetStringTypeA function, the function returns NULL for each character.
+$#A End User Defined Character Reference
This section provides reference information on:
· Registry entries.
· End user defined character constants.

+$#A Registry Entries
The following registry information shows the appropriate entries for use with end user defined characters.
+$#A EUDCCodeRange
EUDCCodeRange CodePage=FromTo[,FromTo]
The EUDCCodeRange registry entry contains entries that define end-user-defined characters (EUDC) code ranges for the various code pages (character sets).
EUDCCodeRange Key
The EUDCCodeRange key has the following location in the registry:
HKEY_LOCAL_MACHINE\ System\CurrentControlSet\Control\NLS\CodePage\EUDCCodeRange

The key contains one or more values specifying the range of character values within a codepage that are available for end-user-defined characters. Under this key, a value name is one of these code-page values:
Value Meaning
932 Japan
936 Simplified Chinese
949 Unified Hangeul Code (Hangeul TongHabHyung Code)
950 Traditional Chinese
1361 Johab
The value associated with each name is a string consisting of one or more pairs of 4-digit hexadecimal numbers, with the numbers in each pair separated by a hyphen (-) and each pair separated from any preceding pair by a comma (,). The following example illustrates this format:
HKEY_LOCAL_MACHINE\
System\CurrentControlSet\Control\NLS\CodePage\EUDCCodeRange
923=F040-F9FC
950=FA40-FEFE, 8E40-A0FE, 8140-8DFE, C6A1-C8FE
CodePage
A user-defined value name that identifies the code page. It must be the letters "CP" followed by 5 decimal digits specifying the code page.
FromTo
A string value consisting of a pair of 4-digit hexadecimal values separated by a hyphen (-). Multiple FromTo values may be specified, but each must be separated from the previous by a comma (,).

The following examples show possible EUDC ranges for code pages 923 and 950:
CP00923=F040-F9FCCP00950=FA40-FEFE,8E40-A0FE,8140-8DFE,C6A1-C8FE

+$#A EUDC
EUDC SystemDefaultEUDCFont=TrueTypeEUDCFontFileName TrueTypeFontTypeface=TrueTypeEUDCFontFileName
EUDC Key
The EUDC registry key has the following location in the registry:
HKEY_CURRENT_USER\EUDC

The EUDC key contains one or more subkeys with each subkey containing values defining the fonts associated with end-user-defined characters for a given code page. Under the EUDC, a subkey name can be one of these code-page numbers:
Value Meaning
932 Japan
936 Simplified Chinese
949 Unified Hangeul Code (Hangeul TongHabHyung Code)
950 Traditional Chinese
1361 Johab

One value under the subkey has the name SystemDefaultEUDCFont. Other values, if any, have names that identify a typeface. The values associated with these names are strings specifying the name of TrueType font file containing the given font. The following example shows the EUDC key for code page 932:
HKEY_CURRENT_USER\EUDC\932
SystemDefaultEUDCFont=EUDC.TTF
MS Mincho=MINEUDC.TTF
MS Gothic=GTEUDC.TTF

SystemDefaultEUDCFont
Predefined value name used to set the system default font. The corresponding value must be a string that identifies a typeface-unaware EUDC font. There is no system default EUDC font unless this entry is explicitly specified.
TrueTypeFontTypeface
A user-defined value name that identifies the typeface name associated with a TrueType font. The corresponding value must be a string that identifies a typeface-aware EUDC font.
TrueTypeEUDCFontFileName
A string value consisting of the filename of a "separate" EUDC font file.

The following example sets the system default EUDC font to be EUDC.TTF and associates the typeface-aware fonts, MINEUDC.TTF and GOTEUDC.TTF, with the typeface names "MS Mincho" and "MS Gothic":
SystemDefaultEUDCFont=EUDC.TTFMS Mincho=MINEUDC.TTFMS Gothic=GOTEUDC.TTF

+$#A End User Defined Character Constants
The following constants are used with end user defined characters in the following Far Eastern languages.
+$#A Japanese EUDC Ranges
The following table shows the mapping between the Japanese Microsoft Standard Character Set (ShiftJIS) and Unicode.
Shift JIS Unicode
F040 U+E000
F041 U+E001
F042 U+E002
: :
F9FA U+E755
F9FB U+E756
F9FC U+E757

+$#A Chinese EUDC Ranges
The following table shows the mapping between the Chinese character sets (BIG-5 and GB 2312-80) and Unicode.
BIG-5 Unicode
FA40 - FEFE U+E000 - U+E310
8E40 - A0FE U+E311 - U+EEB7
8140 - 8DFE U+EEB8 - U+F6B0
C6A1 - C8FE U+F6B1 - U+F8FF

GB 2312-80 Unicode
F8A1 - FEFE U+E000 - U+E29F
AAA1 - AFFE U+E2A0 - U+E4DF

+$#A Korean EUDC Ranges
The following table shows the mapping from Korean EUDC characters (Unified Hangeul Code) and Unicode.

Unified Hangeul Unicode
C9A1 - C9FE U+E000 - U+E05D
FEA1 - FEFE U+E05E - U+E0BB

+$#AK Input Method Editor
The input method editor (IME), available in Asian versions of the Microsoft Windows operating system, helps minimize the effort needed by users to enter text containing characters from Unicode and double-byte character sets.
This overview describes the IME and explains how to use the input method manager functions to create and manage IME windows.
+$#A About Input Method Editor
The input method editor relieves users of the need to remember all possible character values. Instead, the IME monitors the user's keystrokes, anticipates the characters the user may want, and presents a list of candidate characters from which to choose.
By default, the IME provides an IME window through which users enter keystrokes and view and select candidates. Applications developed for the Win32 application programming interface (API) can use the input method manager (IMM) functions and messages to create and manage their own IME windows, providing a custom interface while using the conversion capabilities of the IME.
+$#A Status, Composition, and Candidates Windows
The status, composition, and candidates windows form the user interface for the IME. The status window indicates that the IME is open and provides the user the means to set the conversion modes. The composition window appears when the user enters text and, depending on the conversion mode, either displays the text as entered or displays converted text. The candidates window appears in conjunction with the composition window. It contains a list of "candidates" (alternative characters) for the selected character or characters in the composition window. The user can scroll through the candidates list and select the desired character(s), then return to the composition window. The user can compose the desired text in this way until the composition string is finalized and the window is closed. The IME sends the composed characters to the application in the form of WM_IME_CHAR_win32_WM_IME_CHAR or WM_IME_COMPOSITION_win32_WM_IME_COMPOSITION/GCS_RESULT messages. If the application does not process these messages, the DefWindowProc_win32_DefWindowProc function translates them into one or more WM_CHAR_win32_WM_CHAR messages.
By default, the system automatically creates and manages status, composition, and candidates windows for all windows that require text input. For many applications, this default processing is sufficient. These applications rely entirely on the system for IME support and are said to be IME-unaware because they are unaware of the many tasks the system carries out to manage the IME windows.
An IME-aware application, on the other hand, participates in the creation and management of IME windows. Such applications control the operation, position, and appearance of the default windows by sending messages to and by intercepting and processing messages intended for these windows. In some cases, applications create their own IME windows and provide complete processing for their custom status, composition and candidates windows.
+$#A IME Window Class
The "IME" window class is a predefined system global class that defines the appearance and behavior of the standard IME windows. The class is similar to common control classes in that you create a window of this class by using the CreateWindowEx_win32_CreateWindowEx function. Like static controls, an IME window does not respond to user input by itself. Instead, it notifies the IME of user input actions and processes control messages sent to it by the IME or applications to carry out a response to the user action.
IME-aware applications sometimes create their own IME windows using the IME class. This allows the application to take advantage of the default processing of the IME window while having control of the positioning of the window.
+$#A IME Messages
The system sends IME window messages to the window procedure of the application when certain event occur that affect the IME windows. For example, the system sends the WM_IME_SETCONTEXT_win32_WM_IME_SETCONTEXT message to the application when a window is activated. IME-unaware application pass these messages to the DefWindowProc_win32_DefWindowProc function which sends them to the corresponding default IME window. IME-aware applications either process these messages or forward them the their own IME windows.
You can direct an IME window to carry out an action, such as change the position of composition window, by using the WM_IME_CONTROL_win32_WM_IME_CONTROL message. The IME notifies the application about changes to the composition string by using the WM_IME_COMPOSITION_win32_WM_IME_COMPOSITION message and about general changes to the status of the IME windows by sending the WM_IME_NOTIFY_win32_WM_IME_NOTIFY message.
+$#A Input Context
An input context is an internal structure, maintained by the IME, that contains information about the status of the IME and is used by IME windows. By default, the system creates and assigns an input context to each thread. Within the thread, this default input context is a shared resource and is associated with each newly created window.
To retrieve or set information in the IME, an application must first retrieve a handle to the input context associated with a given window. You retrieve the handle by using the ImmGetContext_win32_ImmGetContext function. You can use the retrieved handle in subsequent calls to the input method manager functions to retrieve and set IME values, such as the composition window style, the composition style, and the status window position. Once you have finished using the context, you must release it using the ImmReleaseContext_win32_ImmReleaseContext function.
Because the default input context is a shared resource, any changes you make to it apply to all windows in the thread. However, you can override this default behavior by creating and associating your own input context to one or more window of the thread. The changes you make to your own input context apply only to the windows with which it is associated.
You can create an input context by using the ImmCreateContext_win32_ImmCreateContext function. You assign the context to a window by using the ImmAssociateContext_win32_ImmAssociateContext function. This function returns the handle of the previously associated input context. If you have not associated an input context with the window before, the returned handle is for the default input context. You should save this handle and later reassociate with the window if you no longer want to use your own input context.
Once an input context is associated with a window, the system automatically selects that context when the window is activated and receives the input focus. The style and other information in the input context affects subsequent keyboard input for that window, determining whether and how the IME operates.
You must destroy any input context you create before terminating your application. You destroy an input context by using the ImmDestroyContext_win32_ImmDestroyContext function. Before destroying it, you must remove the input context from any association it has with windows in the thread by using the ImmAssociateContext function.
+$#A Composition String
The composition string is the current text in the composition window. This is the text that the IME converts to final characters. Each composition string consists of one or more clauses, where a clause is the smallest combination of characters that the IME can convert to a final character. You can get and set the composition string and get and set the composition string by using the ImmGetCompositionString_win32_ImmGetCompositionString and ImmSetCompositionString_win32_ImmSetCompositionString functions.
As the user enters text in the composition window, the IME tracks the status of the composition string. This status includes attribute information, clause information, typing information, and cursor position. You can retrieve the composition status by using the ImmGetCompositionString function.
In the attribute information array, all characters of one clause must have the same attribute. The attribute information is an array of 8-bit values that specifies the status of characters in the composition string. There is one value for each byte in the string, including one byte each for the lead and second bytes of any double-byte characters in the string. For each value in the array, bits 0 through 3 can be one combination of these values:
ATTR_INPUT
Character being entered by the user. It is yet to be converted by the IME.
ATTR_INPUT_ERROR
Character is the error character and cannot be converted by the IME.
ATTR_TARGET_CONVERTED
Character converted by the IME. The user has selected this character and the IME has converted it.
ATTR_CONVERTED
A converted character. The IME has already converted this character.
ATTR_TARGET_NOTCONVERTED
Character being converted. The user has selected this character but the IME has not yet converted it.

All other values are reserved. In Japanese, any unconverted character having the ATTR_INPUT attribute is a Hiragana, Katakana, or alphanumeric character. In Korean, this character is a Hangeul character that is not converted by IME yet. In Traditional and Simplified Chinese, each IME may limit its character in some range.
You can retrieve the composition status by using the ImmGetCompositionString function.
The clause information is an array of 32-bit values that specify the positions of the clauses in the composition string. There is one value for each clause and a final value that specifies the length of the full string. Each value in the array specifies the offset, in bytes, from the beginning of the string to the clause. The first value is always 0 because the first clause always starts at the beginning of the string. For example, if a string has two clauses, the clause information has three values: the first value is 0, the second value is the offset of the second clause, and the third value is the length of the string.
The typing information a null-terminated character string representing the characters entered at the keyboard.
The cursor position is a value indicating the position of the cursor relative to the characters in the composition string. The value is the offset, in bytes, from the beginning of the string. If this value is 0, the cursor is immediately before the first character in the string. If the value is equal to the length of the string, the cursor is immediately after the last character. If -1, the cursor is not present.
You can set the composition string or elements of the composition status by using the ImmSetCompositionString_win32_ImmSetCompositionString function. To ensure that the composition window updates it appearance based on these changes, the function allows for generating a notification message to be sent to the window. Applications that set a combination of composition status elements typically set the fNotify parameter to FALSE for all but the last call to this function so that only one notification message is generated for the composition window.
+$#A Candidate Lists
A candidate list is a CANDIDATELIST_win32_CANDIDATELIST_str structure consisting of an array of strings that specify the characters or character strings that the user may choose from. You can retrieve the candidate lists by using the ImmGetCandidateListCount_win32_ImmGetCandidateListCount and ImmGetCandidateList_win32_ImmGetCandidateList functions.
+$#A Hot Keys
Hot keys give the user a way to quickly change the input mode of the IME or to switch to another IME. Although applications cannot add hot keys to the system, they can initiate the same action as a hot key by using the ImmSimulateHotKey_win32_ImmSimulateHotKey function.

+$#A Using Input Method Editor
· Processing the WM_IME_COMPOSITION message_win32_Processing_the_WM_IME_COMPOSITION_Message

+$#A Processing the WM_IME_COMPOSITION Message
Applications that process the WM_IME_COMPOSITION_win32_WM_IME_COMPOSITION message test the bits in lParam parameter and call the ImmGetCompositionString_win32_ImmGetCompositionString function to retrieve the indicated string or data. The following example checks for the result string, allocates sufficient memory for the string, and retrieves the result string from the IME:
HIMC hIMC;HWND hWnd;DWORD dwSize;HGLOBAL hstr;LPSTR lpstr;case WM_IME_COMPOSITION: if (lParam & GCS_RESULTSTR) { hIMC = ImmGetContext(hWnd); If (!hIMC) MyError(ERROR_NULLCONTEXT); // Get the size of the result string. dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); // increase buffer size for NULL terminator, // maybe it is in UNICODE dwSize += sizeof(WCHAR); hstr = GlobalAlloc(GHND,dwSize); if (hstr == NULL) MyError(ERROR_GLOBALALLOC); lpstr = GlobalLock(hstr); if (lpstr == NULL) MyError(ERROR_GLOBALLOCK); // Get the result strings that is generated by IME into lpstr. ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize); ImmReleaseContext(hWnd, hIMC); // add this string into text buffer of application GlobalUnlock(hstr); GlobalFree(hstr); }
+$#A Input Method EditorReference
This section contains an alphabetical list of the input method editor functions, messages, and structures.
+$#A Input Method Editor Functions
This section describes the input method editor functions.
EnumRegisterWordProc_win32_EnumRegisterWordProcImmAssociateContext_win32_ImmAssociateContextImmConfigureIME_win32_ImmConfigureIMEImmCreateContext_win32_ImmCreateContextImmDestroyContext_win32_ImmDestroyContextImmEnumRegisterWord_win32_ImmEnumRegisterWordImmEscape_win32_ImmEscapeImmGetCandidateList_win32_ImmGetCandidateListImmGetCandidateListCount_win32_ImmGetCandidateListCountImmGetCandidateWindow_win32_ImmGetCandidateWindowImmGetCompositionFont_win32_ImmGetCompositionFontImmGetCompositionString_win32_ImmGetCompositionStringImmGetCompositionWindow_win32_ImmGetCompositionWindowImmGetContext_win32_ImmGetContextImmGetConversionList_win32_ImmGetConversionListImmGetConversionStatus_win32_ImmGetConversionStatusImmGetDefaultIMEWnd_win32_ImmGetDefaultIMEWndImmGetDescription_win32_ImmGetDescriptionImmGetGuideLine_win32_ImmGetGuideLineImmGetIMEFileName_win32_ImmGetIMEFileNameImmGetOpenStatus_win32_ImmGetOpenStatusImmGetProperty_win32_ImmGetPropertyImmGetRegisterWordStyle_win32_ImmGetRegisterWordStyleImmGetStatusWindowPos_win32_ImmGetStatusWindowPosImmGetVirtualKey_win32_ImmGetVirtualKeyImmInstallIME_win32_ImmInstallIMEImmIsIME_win32_ImmIsIMEImmIsUIMessage_win32_ImmIsUIMessageImmNotifyIME_win32_ImmNotifyIMEImmRegisterWord_win32_ImmRegisterWordImmReleaseContext_win32_ImmReleaseContextImmSetCandidateWindow_win32_ImmSetCandidateWindowImmSetCompositionFont_win32_ImmSetCompositionFontImmSetCompositionString_win32_ImmSetCompositionStringImmSetCompositionWindow_win32_ImmSetCompositionWindowImmSetConversionStatus_win32_ImmSetConversionStatusImmSetOpenStatus_win32_ImmSetOpenStatusImmSetStatusWindowPos_win32_ImmSetStatusWindowPosImmSimulateHotKey_win32_ImmSimulateHotKeyImmUnregisterWord_win32_ImmUnregisterWord
+$#A Input Method Editor Messages
This section describes the input method editor messages.
IMC_GETCANDIDATEPOS_win32_IMC_GETCANDIDATEPOSIMC_GETCOMPOSITIONFONT_win32_IMC_GETCOMPOSITIONFONTIMC_GETCOMPOSITIONWINDOW_win32_IMC_GETCOMPOSITIONWINDOWIMC_GETCONVERSIONMODE_win32_IMC_GETCONVERSIONMODEIMC_GETOPENSTATUS_win32_IMC_GETOPENSTATUSIMC_GETSENTENCEMODE_win32_IMC_GETSENTENCEMODEIMC_GETSTATUSWINDOWPOS_win32_IMC_GETSTATUSWINDOWPOSIMC_SETCANDIDATEPOS_win32_IMC_SETCANDIDATEPOSIMC_SETCOMPOSITIONFONT_win32_IMC_SETCOMPOSITIONFONTIMC_SETCOMPOSITIONWINDOW_win32_IMC_SETCOMPOSITIONWINDOWIMC_SETCONVERSIONMODE_win32_IMC_SETCONVERSIONMODEIMC_SETOPENSTATUS_win32_IMC_SETOPENSTATUSIMC_SETSENTENCEMODE_win32_IMC_SETSENTENCEMODEIMC_SETSTATUSWINDOWPOS_win32_IMC_SETSTATUSWINDOWPOSIMN_CHANGECANDIDATE_win32_IMN_CHANGECANDIDATEIMN_CLOSECANDIDATE_win32_IMN_CLOSECANDIDATEIMN_CLOSESTATUSWINDOW_win32_IMN_CLOSESTATUSWINDOWIMN_GUIDELINE_win32_IMN_GUIDELINEIMN_OPENCANDIDATE_win32_IMN_OPENCANDIDATEIMN_OPENSTATUSWINDOW_win32_IMN_OPENSTATUSWINDOWIMN_SETCANDIDATEPOS_win32_IMN_SETCANDIDATEPOSIMN_SETCOMPOSITIONFONT_win32_IMN_SETCOMPOSITIONFONTIMN_SETCOMPOSITIONWINDOW_win32_IMN_SETCOMPOSITIONWINDOWIMN_SETCONVERSIONMODE_win32_IMN_SETCONVERSIONMODEIMN_SETOPENSTATUS_win32_IMN_SETOPENSTATUSIMN_SETSENTENCEMODE_win32_IMN_SETSENTENCEMODEIMN_SETSTATUSWINDOWPOS_win32_IMN_SETSTATUSWINDOWPOSWM_IME_CHAR_win32_WM_IME_CHARWM_IME_COMPOSITION_win32_WM_IME_COMPOSITIONWM_IME_COMPOSITIONFULL_win32_WM_IME_COMPOSITIONFULLWM_IME_CONTROL_win32_WM_IME_CONTROLWM_IME_ENDCOMPOSITION_win32_WM_IME_ENDCOMPOSITIONWM_IME_NOTIFY_win32_WM_IME_NOTIFYWM_IME_SELECT_win32_WM_IME_SELECTWM_IME_SETCONTEXT_win32_WM_IME_SETCONTEXTWM_IME_STARTCOMPOSITION_win32_WM_IME_STARTCOMPOSITION
+$#A Input Method Editor Structures
This section describes the structures used with input method editor functions and messages.
CANDIDATEFORM_win32_CANDIDATEFORM_strCANDIDATELIST_win32_CANDIDATELIST_strCOMPOSITIONFORM_win32_COMPOSITIONFORM_strSTYLEBUF_win32_STYLEBUF_str
+$#A Input Method Editor Constants
This section describes the constants used by the input method editor functions and messages.
+$#A IME Conversion Mode Values
Bit Meaning
IME_CMODE_CHARCODE Set to 1 if character code input mode; 0 if not.
IME_CMODE_EUDC Set to 1 if EUDC conversion mode; 0 if not.
IME_CMODE_FULLSHAPE Set to 1 if full shape mode; 0 if half shape mode.
IME_CMODE_HANJACONVERT Set to 1 if HANJA convert mode; 0 if not.
IME_CMODE_KATAKANA Set to 1 if KATAKANA mode; 0 if HIRAGANA mode.
IME_CMODE_NATIVE Set to 1 if NATIVE mode; 0 if ALPHANUMERIC mode.
IME_CMODE_NOCONVERSION Set to 1 to prevent processing of conversions by IME; 0 if not.
IME_CMODE_ROMAN Set to 1 if ROMAN input mode; 0 if not.
IME_CMODE_SOFTKBD Set to 1 if Soft Keyboard mode; 0 if not.

All other bits are reserved.
+$#A IME Composition String Values
Value Description
GCR_ERRORSTR Retrieves or updates the error string.
GCR_INFORMATIONSTR Retrieves or updates the information string.
GCS_COMPATTR Retrieves or updates the attribute of the composition string.
GCS_COMPCLAUSE Retrieves or updates clause information of the composition string.
GCS_COMPREADATTR Retrieves or updates the attributes of the reading string of the current composition.
GCS_COMPREADCLAUSE Retrieves or updates the clause information of the reading string of the composition string.
GCS_COMPREADSTR Retrieves or updates the reading string of the current composition.
GCS_COMPSTR Retrieves or updates the current composition string.
GCS_CURSORPOS Retrieves or updates the cursor position in composition string.
GCS_DELTASTART Retrieves or updates the starting position of any changes in composition string.
GCS_RESULTCLAUSE Retrieves or updates clause information of the result string.
GCS_RESULTREADCLAUSE Retrieves or updates clause information of the reading string.
GCS_RESULTREADSTR Retrieves or updates the reading string.
GCS_RESULTSTR Retrieves or updates the string of the composition result.
GCS_TYPINGINFO Retrieves or updates the typing information of reading string. In contrast to reading information, this shows raw key strokes that the end user types.

+$#A IME Hot Key Identifiers
IME_CHOTKEY_IME_NONIME_TOGGLE (Simplified Chinese) Toggles between IME and non-IME operation.
IME_CHOTKEY_SHAPE_TOGGLE (Simplified Chinese) Toggles the shape conversion mode of IME.
IME_CHOTKEY_SYMBOL_TOGGLE (Simplified Chinese) Toggles the symbol conversion mode of IME. Symbol mode indicates that the user can input Chinese punctuation and symbols by mapping to the punctuation and symbols on the keyboard.
IME_JHOTKEY_CLOSE_OPEN (Japanese) Alternately opens and closes the IME.
IME_KHOTKEY_ENGLISH (Korean) Switches to English.
IME_KHOTKEY_SHAPE_TOGGLE (Korean) Toggles the shape conversion mode of IME.
IME_KHOTKEY_HANJACONVERT (Korean) Switches to Hanja converision.
IME_THOTKEY_IME_NONIME_TOGGLE (Traditional Chinese) Toggles between IME and non-IME operation.
IME_THOTKEY_SHAPE_TOGGLE (Traditional Chinese) Toggles the shape conversion mode of IME.
IME_THOTKEY_SYMBOL_TOGGLE (Traditional Chinese) Toggles the symbol conversion mode of IME.

+$#A IME Sentence Mode Values
Constant Definition
IME_SMODE_AUTOMATIC The IME carries out conversion processing in automatic mode.
IME_SMODE_NONE No information for sentence.
IME_SMODE_PHRASEPREDICT The IME uses phrase information to predict the next character.
IME_SMODE_PLURALCLAUSE The IME uses plural clause information to carry out conversion processing.
IME_SMODE_SINGLECONVERT The IME carries out conversion processing in single character mode.

Bits 16 through 31 are reserved for IME use.
+$#A IME Escapes
Escape Description
IME_ESC_GET_EUDC_DICTIONARY Retrieves the path of the EUDC dictionary file. On input, the lpData parameter must be the pointer to the buffer to receive the path. This buffer must be no less than 80 characters in length. On return, the buffer contains the null-terminated string specifying the full path. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_HANJA_MODE Converts from Hangeul to Hanja. On input, lpData must be the pointer to the buffer that contains the Hangeul character to convert; on output, it receives the converted Hanja as a null-terminated string. For use by the Korean editor; do not use in other applications.
IME_ESC_IME_NAME Retrieves the name of the IME. On input, the lpData parameter must be the pointer to the buffer to receive the name. This buffer must be no less than 64 characters in length. On return, the buffer contains the null-terminated string specifying the IME name. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_MAX_KEY Returns the maximum number of key stokes for an EUDC character. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_QUERY_SUPPORT Checks for implementation. Returns zero if the escape is not implemented.
IME_ESC_SEQUENCE_TO_INTERNAL Returns the character code that matches the given sequence code. On input, the lpData parameter is the pointer to a 32-bit variable that contains the sequence code. For use by the Chinese EUDC editor; do not use in other applications. Normally, the Chinese IME will encode its reading character codes into sequence 1 to n.
IME_ESC_SET_EUDC_DICTIONARY Sets the EUDC dictionary file. On input, the lpData parameter is the pointer to a null-terminated string specifying the full path. For use by the Chinese EUDC editor; do not use in other applications.

The system reserves the escapes in the range IME_ESC_RESERVED_FIRST to IME_ESC_RESERVED_LAST for its own use.
The system reserves the escapes in the range IME_ESC_PRIVATE_FIRST to IME_ESC_PRIVATE_LAST for private use by IMEs.

+$#AK National Language Support
National language support functions help applications developed for the Win32 application programming interface (API) adapt to the differing language- and location-specific needs of users around the world.
This overview describes the national language support functions and explains how to use them in your Win32-based applications.
+$#A About National Language Support
The national language support functions let applications set the locale for the user, identifying the language in which the user carries out work, and retrieve strings, representing times, dates and other information, that are correctly formatted for the given language and location of the world.
National language support also includes support for keyboard layouts and language-specific fonts. For more information about these topics, see Keyboard Input_win32_Keyboard_Input and Fonts and Text_win32_Fonts_and_Text.
+$#A Locales
A locale is a collection of language-related, user preference information represented as a list of values. Each system has at least one installed locale and usually has many locales from which the user may choose. Each locale has a unique locale identifier (LCID), a 32-bit value that consists in part of a language identifier. The language identifier is a standard international numeric abbreviation for a country or geographical region.
The system assigns a locale to each thread. Initially, the system assigns the system default locale to the thread. This default locale is set by the user when the system is installed or through the International applet of the Control Panel. If a thread is run in a process belonging to a user, the system assigns the user default locale to the thread. An application can override either default by using the the SetThreadLocale_win32_SetThreadLocale function to explicitly set the locale for a thread.
There are two predefined locale identifiers: LOCALE_SYSTEM_DEFAULT, which identifies the system default locale, and LOCALE_USER_DEFAULT, which identifies the locale of the current user. An application can retrieve the current locale identifiers by using the GetSystemDefaultLCID_win32_GetSystemDefaultLCID and GetUserDefaultLCID_win32_GetUserDefaultLCID functions. Similarly, an application can retrieve the current language identifiers by using the GetSystemDefaultLangID_win32_GetSystemDefaultLangID and GetUserDefaultLangID_win32_GetUserDefaultLangID functions.
An application can retrieve specific information about any available language or locale. This can be important for handling language- and locale-specific strings. An application can retrieve information about a locale by using the GetLocaleInfo_win32_GetLocaleInfo function. Each element of locale information has a unique LCTYPE value that identifies a specific information type. Applications use these values to retrieve the information they need.
All LCTYPE values are mutually-exclusive, so only one type of information can be retrieved at a time. An exception is the LOCALE_NOUSEROVERRIDE which can be used to bypasses user overrides and retrieve the system default values for the requested locale.
Locale information is always stored and manipulated as a null-terminated string. No binary data is allowed; any numeric values must be specified as text. Each type of information has a particular format. Also, several of types are linked together, such that changing one changes the value of the other as well.
Although a given locale identifier may be supported, it is not available for use by an application unless it is also installed.
+$#A Time and Date
You can retrieve strings for any given time or date in a format that is appropriate for the current locale by using the GetTimeFormat_win32_GetTimeFormat and GetDateFormat_win32_GetDateFormat functions.
For GetTimeFormat, the time values in the SYSTEMTIME_win32_SYSTEMTIME_str@win32.hlp structure pointed to by lpTime must be valid. The function checks each of the time values to determine that it is within the appropriate range of values. If any of the time values are outside the correct range, the function fails, and sets the last-error to ERROR_INVALID_PARAMETER.
The function ignores the date portions of the SYSTEMTIME structure pointed to by lpTime: wYear, wMonth, wDayOfWeek, and wDay.
If a time marker exists and the TIME_NOTIMEMARKER flag is not set, the function localizes the time marker based on the specified locale identifier. Examples of time markers are "AM" and "PM" for US English, "de." and "du." for Mexican Spanish.
If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function removes the separator(s) preceding the minutes and/or seconds element(s).
If TIME_NOTIMEMARKER is specified, the function removes the separator(s) preceding and following the time marker.
If TIME_FORCE24HOURFORMAT is specified, the function displays any existing time marker, unless the TIME_NOTIMEMARKER flag is also set.
The function does not include milliseconds as part of the formatted time string.
To use the LOCALE_NOUSEROVERRIDE flag, lpFormat must be NULL.
No errors are returned for a bad format string. The function simply forms the best time string that it can. If more than two hour, minute, second, or time marker format pictures are passed in, then the function defaults to two. For example, the only time marker pictures that are valid are L"t" and L"tt" (the 'L' indicates a Unicode (16-bit characters) string). If L"ttt" is passed in, the function assumes L"tt".
For GetDateFormat_win32_GetDateFormat, the date values in the SYSTEMTIME_win32_SYSTEMTIME_str@win32.hlp structure pointed to by lpDate must be valid. The function checks each of the date values: year, month, day, and day of week. If the day of the week is incorrect, the function uses the correct value, and returns no error. If any of the other date values are outside the correct range, the function fails, and sets the last-error to ERROR_INVALID_PARAMETER.
The day name, abbreviated day name, month name, and abbreviated month name are all localized based on the given locale identifier. The function ignores the time portions of the SYSTEMTIME structure pointed to by lpDate: wHour, wMinute, wSecond, and wMilliseconds.
The flag DATE_USE_ALT_CALENDAR will always use the format defined as the default format for that alternate calendar, rather than using any user overrides. The user overrides will only be used in the event that there is no default format for the alternate calendar specified.
The DATE_SHORTDATE and DATE_LONGDATE flag options are mutually exclusive. If neither one is specified and lpFormat is NULL, then DATE_SHORTDATE is the default.
If the lpFormat parameter is not NULL, the dwFlags parameter must be zero. None of the flags make sense when the format string is specified.
No errors are returned for a bad format string. The function simply forms the best date string that it can. For example, the only year pictures that are valid are L"yyyy" and L"yy" (the 'L' indicates a Unicode (16-bit characters) string). If L"y" is passed in, the function assumes L"yy". If L"yyy" is passed in, the function assumes L"yyyy". If more than 4 date (L"dddd") or 4 month (L"MMMM") pictures are passed in, then the function defaults to L"dddd" or L"MMMM".
Any text that should remain in its exact form in the date string should be enclosed within single quotes in the date format picture. The single quote may also be used as an escape character to allow the single quote itself to be displayed in the date string. However, the escape sequence must be enclosed within two single quotes. For example, to display the date as "May '93", the format string would be: L"MMMM ''''yy " The first and last single quotes are the enclosing quotes. The second and third single quotes are the escape sequence to allow the single quote to be displayed before the century.
+$#A Date and Calendar
Most locales use the standard Gregorian calendar and a set number of date formats. These "default choices" for date formats are available for display by using the EnumDateFormats_win32_EnumDateFormats function. There are other locales which require special considerations when creating a complete list of format choices. Some of these require text strings to be inserted within the date format string, others require a completely different method of computation of the values. These special requirements are addressed by the addition of new LCTYPE and CALTYPE values.
Each locale identifier has a default calendar type associated with it. A locale identifier can also have an alternate calendar type, which can be set using the LOCALE_IOPTIONALCALENDAR type for this locale.
+$#A Localization and the Shell Font
Windows NT is available in localized editions of many different languages, but the U.S. English edition can also be used to run applications written in other languages. This is true even when the base character sets are different, such as when applications are written in the Central and Eastern European languages, or in Greek. These applications require a user interface (UI) with dialog boxes, icons, and applets that provide information in the application's language, which may be different than the language being used in the current Windows NT UI.
To enable the U.S. English Windows NT user interface, as well as characters outside the Windows character set for the US and Western Europe (code page 1252), to display correctly within applications and in system edit controls, Windows NT uses intelligent font switching based on the currently selected locale. This allows currently shipping localized applications to run on the U.S. English version of Windows NT without modifying the application in any way.
An example of the problem being addressed here is that the U.S. English Windows NT shell font is MS Sans Serif but the shell font for Greek Windows 3.1 is MS Sans Serif Greek. These character sets cannot be directly mapped to each other so simply replacing the MS Sans Serif font with the MS Sans Serif Greek font when the locale is set to Greek does not allow existing applications with this kind of difference to run adequately or to display Greek characters in system menus, dialogs and edit controls. To resolve this problem at run time, Microsoft has defined a new font face name, MS Shell Dlg. It is mapped by an entry in the registry to the appropriate shell font for the currently active locale.
+$#A MS Shell Dlg
MS Shell Dlg is a mapping mechanism that enables U.S. English Windows NT to support non-code page 1252 locales. It is not a font but only a face name for a nonexistent font. It can be specified in either the Windows NT Setup file during the installation process or when customizing a local system by double-clicking on the Control Panel's International icon. When you double-click on the icon, select the appropriate Country and Language in the International dialog box, and then reboot the system, the appropriate change occurs in the Registry under the following key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\Current Version\FontSubstitutes
An added benefit of this new means of font mapping in the registry is that you can change the font for the entire shell by simply changing one .inf file entry. For example, Far East localization is much easier because you can change the .inf file entry, instead of changing every instance of MS Sans Serif in every .rc and .dlg file, and the change is complete without rebuilding any binaries.
Windows NT applications can also take advantage of this feature. It is very helpful when your application contains multiple language resources that cross character-set boundaries. It is also useful when you have an English application in which supporting multiple languages and character sets is important.
+$#A Windows 95 and MS Shell Dlg
Windows 95 does not support the concept of switching Windows character sets so the role of MS Shell Dlg is more limited. It does, however, facilitate localization in that Windows 95 is equipped with a static entry to replace the standard system font. For example, the system font, MS San Serif, is easily replaced with MS San Serif Greek or another language. This also ensures compatibility with the applets provided with Windows NT.
+$#A When MS Shell Dlg Should Not Be Used
At this time, MS Shell Dlg is not intended for use when you are working with a static, non-English UI application that you want to run when the user has chosen a locale with a different Windows base character set. This is because the application's UI language may not be supported with the font that is substituted for MS Shell Dlg. For example, if you are using a German localized version of Windows NT and want to install a non-Unicode, Greek application, you can use the Control Panel's International applet to change the locale to Greek. This will reset MS Shell Dlg to be a Greek font but this font does not contain all of the glyphs necessary to display German. Therefore, any non-ASCII characters in your German UI will not be displayed properly. To support this scenario, you would have to set MS Shell Dlg to a font that contains both the Western European and the Greek glyphs.
+$#A National Language Support Reference
This section provides detailed reference information for the national language support functions, structures, and messages. The information is separated into the following functional groups.
+$#A National Language Support Functions
This section describes the national language support functions. Many national language support functions generate extended error information on errors. Use the GetLastError_win32_GetLastError function to get this information.
SetLocaleInfo_win32_SetLocaleInfo
+$#A National Language Support Macros
This section describes the macros used with the national language support functions.
LANGIDFROMLCID_win32_LANGIDFROMLCID MAKELANGID_win32_MAKELANGID MAKELCID_win32_MAKELCID

+$#A National Language Support Constants
This section describes the constants used with the national language support functions.
+$#A Locale Information
LOCALE_ILANGUAGE
Language identifier indicating the language. The maximum number of characters allowed for this string is 5.
LOCALE_SLANGUAGE
Full localized name of the language.
LOCALE_SENGLANGUAGE
Full English name of the language from the International Organization for Standardization (ISO) Standard 639. This is always restricted to characters mappable into the ASCII 127-character subset.
LOCALE_SABBREVLANGNAME
Abbreviated name of the language, created by taking the 2-letter language abbreviation from the ISO Standard 639 and adding a third letter, as appropriate, to indicate the sublanguage.
LOCALE_SNATIVELANGNAME
Native name of the language.
LOCALE_ICOUNTRY
Country code, based on international phone codes, also referred to as IBM country codes. The maximum number of characters allowed for this string is 6.
LOCALE_SCOUNTRY
Full localized name of the country.
LOCALE_SENGCOUNTRY
Full English name of the country. This is always restricted to characters mappable into the ASCII 127-character subset.
LOCALE_SABBREVCTRYNAME
Abbreviated name of the country from the ISO Standard 3166.
LOCALE_SNATIVECTRYNAME
Native name of the country.
LOCALE_IDEFAULTLANGUAGE
Language identifier for the principal language spoken in this locale. This is provided so that partially specified locales can be completed with default values. The maximum number of characters allowed for this string is 5.
LOCALE_IDEFAULTCOUNTRY
Country code for the principal country in this locale. This is provided so that partially specified locales can be completed with default values. The maximum number of characters allowed for this string is 6.
LOCALE_IDEFAULTANSICODEPAGE
American National Standards Institute (ANSI) code page associated with this locale. The maximum number of characters allowed for this string is 6.
LOCALE_IDEFAULTOEMCODEPAGE
Original equipment manufacturer (OEM) code page associated with the locale. The maximum number of characters allowed for this string is 6.
LOCALE_IDEFAULTCODEPAGE
Original equipment manufacturer (OEM) code page associated with the country. The maximum number of characters allowed for this string is 6.
LOCALE_SLIST
Character(s) used to separate list items. For example, a comma is used in many locales.
LOCALE_IMEASURE
System of measurement. This value is 0 if the metric system (Systéme International d'Unités, or S.I.) is used and 1 if the U.S. system is used. The maximum number of characters allowed for this string is 2.
LOCALE_SDECIMAL
Character(s) used as the decimal separator.
LOCALE_STHOUSAND
Character(s) used to separate groups of digits to the left of the decimal.
LOCALE_SGROUPING
Sizes for each group of digits to the left of the decimal. An explicit size is needed for each group; sizes are separated by semicolons. If the last value is zero, the preceding value is repeated. To group thousands, specify 3;0, for example.
LOCALE_IDIGITS
Number of fractional digits. The maximum number of characters allowed for this string is 3.
LOCALE_ILZERO
Specifier for leading zeros in decimal fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 No leading zeros
1 Leading zeros

LOCALE_INEGNUMBER
Negative number mode. The mode can be one of these values:
0 (1.1)
1 -1.1
2 - 1.1
3 1.1-
4 1.1 -

LOCALE_SNATIVEDIGITS
Native equivalents to ASCII 0 through 9.
LOCALE_SCURRENCY
String used as the local monetary symbol.
LOCALE_SINTLSYMBOL
Three characters of the international monetary symbol specified in ISO 4217, "Codes for the Representation of Currencies and Funds," followed by the character separating this string from the amount.
LOCALE_SMONDECIMALSEP
Character(s) used as the monetary decimal separator.
LOCALE_SMONTHOUSANDSEP
Character(s) used as the monetary separator between groups of digits to the left of the decimal.
LOCALE_SMONGROUPING
Sizes for each group of monetary digits to the left of the decimal. An explicit size is needed for each group; sizes are separated by semicolons. If the last value is zero, the preceding value is repeated. To group thousands, specify 3;0, for example.
LOCALE_ICURRDIGITS
Number of fractional digits for the local monetary format. The maximum number of characters allowed for this string is 3.
LOCALE_IINTLCURRDIGITS
Number of fractional digits for the international monetary format. The maximum number of characters allowed for this string is 3.
LOCALE_ICURRENCY
Positive currency mode. The maximum number of characters allowed for this string is 2. The mode can be one of the following values:
0 Prefix, no separation
1 Suffix, no separation
2 Prefix, 1-char. separation
3 Suffix, 1-char. separation

LOCALE_INEGCURR
Negative currency mode. The maximum number of characters allowed for this string is 3. The mode can be one of the following values:
0 ($1.1)
1 -$1.1
2 $-1.1
3 $1.1-
4 (1.1$)
5 -1.1$
6 1.1-$
7 1.1$-
8 -1.1 $ (space before $)
9 -$ 1.1 (space after $)
10 1.1 $- (space before $)
11 $ 1.1- (space after $)
12 $ -1.1 (space after $)
13 1.1- $ (space before $)
14 ($ 1.1) (space after $)
15 (1.1 $) (space before $)

LOCALE_SDATE
Character(s) for the date separator.
LOCALE_STIME
Character(s) for the time separator.
LOCALE_STIMEFORMAT
Time formatting strings for this locale. The string can consist of a combination of the hour, minute, and second format pictures defined in in the Hour, Minute, and Second Format Pictures_win32_Hour_Minute_and_Second_Format_Pictures table in National Language Support Constants.
LOCALE_SSHORTDATE
Short date formatting string for this locale. The string can consist of a combination of day, month, and year format pictures defined in in Day, Month, Year, and Era Format Pictures_win32_Day_Month_Year_and_Era_Format_Pictures table in National Language Support Constants.
LOCALE_SLONGDATE
Long date formatting string for this locale. The string can consist of a combination of day, month, and year format pictures defined in in the Day, Month, Year, and Era Format Pictures_win32_Day_Month_Year_and_Era_Format_Pictures table in National Language Support Constantsand any string of characters enclosed in single quotes. Characters in single quotes remain as given.
LOCALE_IDATE
Short date format-ordering specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 Month-Day-Year
1 Day-Month-Year
2 Year-Month-Day

LOCALE_ILDATE
Long date format-ordering specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 Month-Day-Year
1 Day-Month-Year
2 Year-Month-Day

LOCALE_ITIME
Time format specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 AM / PM 12-hour format
1 24-hour format

LOCALE_ICENTURY
Specifier for full 4-digit century. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 Abbreviated 2-digit century
1 Full 4-digit century

LOCALE_ITLZERO
Specifier for leading zeros in time fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 No leading zeros for hours
1 Leading zeros for hours

LOCALE_IDAYLZERO
Specifier for leading zeros in day fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 No leading zeros for days
1 Leading zeros for days

LOCALE_IMONLZERO
Specifier for leading zeros in month fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:
0 No leading zeros for months
1 Leading zeros for months

LOCALE_S1159
String for the AM designator.
LOCALE_S2359
String for the PM designator.
LOCALE_ICALENDARTYPE
Current calendar type. This type can be one of these values:
1 Gregorian (as in United States)
2 Gregorian (English strings always)
3 Era: Year of the Emperor (Japan)
4 Era: Year of the Republic of China
5 Tangun Era (Korea)

LOCALE_IOPTIONALCALENDAR
Additional calendar types. This can be a zero-separated list of one or more of these calendars type values:
0 No additional types valid
1 Gregorian (as in United States)
2 Gregorian (English strings always)
3 Era: Year of the Emperor (Japan)
4 Era: Year of the Republic of China
5 Tangun Era (Korea)

LOCALE_IFIRSTDAYOFWEEK
Specifier for the first day in a week. The specifier can be one of these values:
0 LOCALE_SDAYNAME1
1 LOCALE_SDAYNAME2
2 LOCALE_SDAYNAME3
3 LOCALE_SDAYNAME4
4 LOCALE_SDAYNAME5
5 LOCALE_SDAYNAME6
6 LOCALE_SDAYNAME7

LOCALE_IFIRSTWEEKOFYEAR
Specifier for the first week of the year. The specifier can be one of these values:
0 Week containing 1/1 is the first week of that year.
1 First full week following 1/1 is the first week of that year.
2 First week containing at least 4 days is the first week of that year.

LOCALE_SDAYNAME1
Native long name for Monday.
LOCALE_SDAYNAME2
Native long name for Tuesday.
LOCALE_SDAYNAME3
Native long name for Wednesday.
LOCALE_SDAYNAME4
Native long name for Thursday.
LOCALE_SDAYNAME5
Native long name for Friday.
LOCALE_SDAYNAME6
Native long name for Saturday.
LOCALE_SDAYNAME7
Native long name for Sunday.
LOCALE_SABBREVDAYNAME1
Native abbreviated name for Monday.
LOCALE_SABBREVDAYNAME2
Native abbreviated name for Tuesday.
LOCALE_SABBREVDAYNAME3
Native abbreviated name for Wednesday.
LOCALE_SABBREVDAYNAME4
Native abbreviated name for Thursday.
LOCALE_SABBREVDAYNAME5
Native abbreviated name for Friday.
LOCALE_SABBREVDAYNAME6
Native abbreviated name for Saturday.
LOCALE_SABBREVDAYNAME7
Native abbreviated name for Sunday.
LOCALE_SMONTHNAME1
Native long name for January.
LOCALE_SMONTHNAME2
Native long name for February.
LOCALE_SMONTHNAME3
Native long name for March.
LOCALE_SMONTHNAME4
Native long name for April.
LOCALE_SMONTHNAME5
Native long name for May.
LOCALE_SMONTHNAME6
Native long name for June.
LOCALE_SMONTHNAME7
Native long name for July.
LOCALE_SMONTHNAME8
Native long name for August.
LOCALE_SMONTHNAME9
Native long name for September.
LOCALE_SMONTHNAME10
Native long name for October.
LOCALE_SMONTHNAME11
Native long name for November.
LOCALE_SMONTHNAME12
Native long name for December.
LOCALE_SMONTHNAME13
Native name for 13th month, if exists.
LOCALE_SABBREVMONTHNAME1
Native abbreviated name for January.
LOCALE_SABBREVMONTHNAME2
Native abbreviated name for February.
LOCALE_SABBREVMONTHNAME3
Native abbreviated name for March.
LOCALE_SABBREVMONTHNAME4
Native abbreviated name for April.
LOCALE_SABBREVMONTHNAME5
Native abbreviated name for May.
LOCALE_SABBREVMONTHNAME6
Native abbreviated name for June.
LOCALE_SABBREVMONTHNAME7
Native abbreviated name for July.
LOCALE_SABBREVMONTHNAME8
Native abbreviated name for August.
LOCALE_SABBREVMONTHNAME9
Native abbreviated name for September.
LOCALE_SABBREVMONTHNAME10
Native abbreviated name for October.
LOCALE_SABBREVMONTHNAME11
Native abbreviated name for November.
LOCALE_SABBREVMONTHNAME12
Native abbreviated name for December.
LOCALE_SABBREVMONTHNAME13
Native abbreviated name for 13th month, if exists.
LOCALE_SPOSITIVESIGN
String value for the positive sign.
LOCALE_SNEGATIVESIGN
String value for the negative sign.
LOCALE_IPOSSIGNPOSN
Formatting index for positive values. The maximum number of characters allowed for this string is 2. The index can be one of the following values:
0 Parentheses surround the amount and the monetary symbol.
1 The sign string precedes the amount and the monetary symbol.
2 The sign string succeeds the amount and the monetary symbol.
3 The sign string immediately precedes the monetary symbol.
4 The sign string immediately succeeds the monetary symbol.

LOCALE_INEGSIGNPOSN
Formatting index for negative values. This index uses the same values as LOCALE_IPOSSIGNPOSN. The maximum number of characters allowed for this string is 2.
LOCALE_IPOSSYMPRECEDES
Position of monetary symbol in a positive monetary value. This value is 1 if the monetary symbol precedes the positive amount, 0 if it follows it. The maximum number of characters allowed for this string is 2.
LOCALE_IPOSSEPBYSPACE
Separation of monetary symbol in a positive monetary value. This value is 1 if the monetary symbol is separated by a space from a positive amount, 0 if it is not. The maximum number of characters allowed for this string is 2.
LOCALE_INEGSYMPRECEDES
Position of monetary symbol in a negative monetary value. This value is 1 if the monetary symbol precedes the negative amount, 0 if it follows it. The maximum number of characters allowed for this string is 2.
LOCALE_INEGSEPBYSPACE
Separation of monetary symbol in a negative monetary value. This value is 1 if the monetary symbol is separated by a space from the negative amount, 0 if it is not. The maximum number of characters allowed for this string is 2.
LOCALE_NOUSEROVERRIDE
Can be combined with other LOCALE values to bypass any user override and return the system default value for the given locale information.

Many of the locale types listed above are closely related, such that changing one affects the value of others. The following shows the relationships between these types.
LOCALE_ICURRENCY
LOCALE_IPOSSEPBYSPACE, LOCALE_IPOSSYMPRECEDES
LOCALE_INEGCURR
LOCALE_INEGSEPBYSPACE, LOCALE_INEGSYMPRECEDES, LOCALE_INEGSIGNPOSN, LOCALE_IPOSSIGNPOSN
LOCALE_SSHORTDATE
LOCALE_SDATE, LOCALE_IDATE
LOCALE_SLONGDATE
LOCALE_ILDATE
LOCALE_STIMEFORMAT
LOCALE_STIME, LOCALE_ITIME, LOCALE_ITLZERO

+$#A Day, Month, Year, and Era Format Pictures
The format picture for a date string consists of a combination of null-terminated strings.
Day
d Day of the month as digits without leading zeros for single digit days.
dd Day of the month as digits with leading zeros for single digit days
ddd Day of the week as a 3-letter abbreviation as given by a LOCALE_SABBREVDAYNAME value.
dddd Day of the week as given by a LOCALE_SDAYNAME value.
Month
M Month as digits without leading zeros for single digit months.
MM Month as digits with leading zeros for single digit months
MMM Month as a three letter abbreviation as given by a LOCALE_SABBREVMONTHNAME value.
MMMM Month as given by a LOCALE_SMONTHNAME value.
Year
y Year represented only be the last digit.
yy Year represented only be the last two digits.
yyyy Year represented by the full 4 digits.
Period/Era
gg Period/era string as given by the CAL_SERASTRING value.

The "gg" format picture in a date string is ignored if there is no associated era string.
+$#A Hour, Minute, and Second Format Pictures
The format picture for a time string consists of a combination of one string from each of these format types:
Hours
h Hours without leading zeros for single digit hours (12 hour clock)
hh Hours with leading zeros for single digit hours (12 hour clock)
H Hours without leading zeros for single digit hours (24 hour clock)
HH Hours with leading zeros for single digit hours (24 hour clock)
Minutes
m Minutes without leading zeros for single digit minutes
mm Minutes with leading zeros for single digit minutes
Seconds
s Seconds without leading zeros for single digit seconds
ss Seconds with leading zeros for single digit seconds
Time Marker
t One character time marker string
tt Multi-character time marker string

+$#A Primary Language Identifiers
0x00 Neutral
0x36 Afrikaans
0x1c Albanian
0x01 Arabic
0x2d Basque
0x02 Bulgarian
0x23 Byelorussian
0x03 Catalan
0x04 Chinese
0x05 Czech
0x06 Danish
0x13 Dutch
0x09 English
0x25 Estonian
0x29 Farsi
0x0b Finnish
0x0c French
0x07 German
0x08 Greek
0x0d Hebrew
0x0e Hungarian
0x0f Icelandic
0x21 Indonesian
0x10 Italian
0x11 Japanese
0x2c Kampuchean
0x12 Korean
0x2b Laotian
0x26 Latvian
0x27 Lithuanian
0x2f Macedonian
0x28 Maori
0x14 Norwegian
0x15 Polish
0x16 Portuguese
0x17 Rhaeto Roman
0x18 Romanian
0x19 Russian
0x1a Serbo Croatian
0x1b Slovak
0x24 Slovenian
0x2e Sorbian
0x0a Spanish
0x30 Sutu
0x1d Swedish
0x1e Thai
0x31 Tsonga
0x32 Tswana
0x1f Turkish
0x22 Ukrainian
0x20 Urdu
0x33 Venda
0x2a Vietnamese
0x34 Xhosa
0x35 Zulu

+$#A Secondary Language Identifiers

0x00 Neutral
0x01 Default
0x02 System Default
0x01 Arabic (Saudi Arabia)
0x02 Arabic (Iraq)
0x03 Arabic (Egypt)
0x04 Arabic (Libya)
0x05 Arabic (Algeria)
0x06 Arabic (Morocco)
0x07 Arabic (Tunisia)
0x08 Arabic (Oman)
0x09 Arabic (Yemen)
0x10 Arabic (Syria)
0x11 Arabic (Jordan)
0x12 Arabic (Lebanon)
0x13 Arabic (Kuwait)
0x14 Arabic (U.A.E.)
0x15 Arabic (Bahrain)
0x16 Arabic (Qatar)
0x01 Chinese (Taiwan)
0x02 Chinese (PRC)
0x03 Chinese (Hong Kong)
0x04 Chinese (Singapore)
0x01 Dutch
0x02 Dutch (Belgian)
0x01 English (US)
0x02 English (UK)
0x03 English (Australian)
0x04 English (Canadian)
0x05 English (New Zealand)
0x06 English (Ireland)
0x01 French
0x02 French (Belgian)
0x03 French (Canadian)
0x04 French (Swiss)
0x05 French (Luxembourg)
0x01 German
0x02 German (Swiss)
0x03 German (Austrian)
0x04 German (Luxembourg)
0x05 German (Liechtenstein)
0x04 Hebrew (Israel)
0x01 Italian
0x02 Italian (Swiss)
0x01 Norwegian (Bokmal)
0x02 Norwegian (Nynorsk)
0x01 Portuguese (Brazilian)
0x02 Portuguese
0x01 Serbo Croatian (Latin)
0x02 Serbo Croatian (Cyrillic)
0x01 Spanish (Traditional Sort)
0x02 Spanish (Mexican)
0x03 Spanish (Modern Sort)

+$#A Language Identifiers
Identifier Locale
0x0436 Afrikaans
0x041C Albania
0x1401 Algeria
0x0409 American
0x0C09 Australian
0x0C07 Austrian
0x3C01 Bahrain
0x042D Basque
0x080C Belgian
0x0813 Belgian (Flemish)
0x0809 British
0x0402 Bulgaria
0x0423 Byelorussia
0x1009 Canadian
0x0403 Catalan
0x041A Croatian
0x0405 Czech
0x0406 Danish
0x0413 Dutch (Standard)
0x0C01 Egypt
0x0425 Estonia
0x0429 Farsi
0x040B Finnish
0x040C French (Standard)
0x0C0C French Canadian
0x0407 German (Standard)
0x042E Germany
0x0408 Greek
0x0C04 Hong Kong
0x040E Hungarian
0x040F Icelandic
0x0421 Indonesia
0x0801 Iraq
0x1809 Ireland
0x040D Israel
0x0410 Italian (Standard)
0x0411 Japan
0x2C01 Jordan
0x0412 Korea
0x3401 Kuwait
0x0426 Latvia
0x3001 Lebanon
0x1001 Libya
0x1407 Liechtenstein
0x0427 Lithuania
0x140C Luxembourg (French)
0x1007 Luxembourg (German)
0x042f Macedonia
0x080A Mexican
0x0818 Moldavia
0x0819 Moldavia
0x1801 Morocco
0x1409 New Zealand
0x0414 Norwegian (Bokmal)
0x0814 Norwegian (Nynorsk)
0x2001 Oman
0x0415 Polish
0x0416 Portuguese (Brazilian)
0x0816 Portuguese (Standard)
0x0804 PRC
0x4001 Qatar
0x0417 Rhaeto-Romanic
0x0418 Romania
0x0419 Russian
0x0401 Saudi Arabia
0x081A Serbian
0x1004 Singapore
0x041B Slovak
0x0424 Slovenia
0x0C0A Spanish (Modern Sort)
0x040A Spanish (Traditional Sort)
0x0430 Sutu
0x041D Swedish
0x100C Swiss (French)
0x0807 Swiss (German)
0x0810 Swiss (Italian)
0x2801 Syria
0x0404 Taiwan
0x041E Thailand
0x0431 Tsonga
0x0432 Tswana
0x1C01 Tunisia
0x041f Turkish
0x3801 U.A.E.
0x0422 Ukraine
0x0420 Urdu
0x0433 Venda
0x0435 Xhosa
0x2401 Yemen
0x0436 Zulu

+$#A Calendar Type Information
CAL_ICALINTVALUE
An integer value indicating the calendar type of the alternate calendar.
CAL_IYEAROFFSETRANGE
One or more null-terminated strings that specify the year offsets for each of the era ranges. The last string has an extra terminating null character.
CAL_SABBREVDAYNAME1
Abbreviated native name of the first day of the week.
CAL_SABBREVDAYNAME2
Abbreviated native name of the second day of the week.
CAL_SABBREVDAYNAME3
Abbreviated native name of the third day of the week.
CAL_SABBREVDAYNAME4
Abbreviated native name of the fourth day of the week.
CAL_SABBREVDAYNAME5
Abbreviated native name of the fifth day of the week.
CAL_SABBREVDAYNAME6
Abbreviated native name of the sixth day of the week.
CAL_SABBREVDAYNAME7
Abbreviated native name of the seventh day of the week.
CAL_SABBREVMONTHNAME1
Abbreviated native name of the first month of the year.
CAL_SABBREVMONTHNAME2
Abbreviated native name of the second month of the year.
CAL_SABBREVMONTHNAME3
Abbreviated native name of the third month of the year.
CAL_SABBREVMONTHNAME4
Abbreviated native name of the fourth month of the year.
CAL_SABBREVMONTHNAME5
Abbreviated native name of the fifth month of the year.
CAL_SABBREVMONTHNAME6
Abbreviated native name of the sixth month of the year.
CAL_SABBREVMONTHNAME7
Abbreviated native name of the seventh month of the year.
CAL_SABBREVMONTHNAME8
Abbreviated native name of the eighth month of the year.
CAL_SABBREVMONTHNAME9
Abbreviated native name of the ninth month of the year.
CAL_SABBREVMONTHNAME10
Abbreviated native name of the tenth month of the year.
CAL_SABBREVMONTHNAME11
Abbreviated native name of the eleventh month of the year.
CAL_SABBREVMONTHNAME12
Abbreviated native name of the twelfth month of the year.
CAL_SABBREVMONTHNAME13
Abbreviated native name of the thirteenth month of the year, if it exists.
CAL_SCALNAME
The native name of the alternate calendar.
CAL_SDAYNAME1
Native name of the first day of the week.
CAL_SDAYNAME2
Native name of the second day of the week.
CAL_SDAYNAME3
Native name of the third day of the week.
CAL_SDAYNAME4
Native name of the fourth day of the week.
CAL_SDAYNAME5
Native name of the fifth day of the week.
CAL_SDAYNAME6
Native name of the sixth day of the week.
CAL_SDAYNAME7
Native name of the seventh day of the week.
CAL_SERASTRING
One or more null-terminated strings that specify each of the Unicode codepoints specifying the era associated with the given CAL_IYEAROFFSETRANGE. The last string has an extra terminating null character. See example below.
CAL_SLONGDATE
Long date formats for this calendar type.
CAL_SMONTHNAME1
Native name of the first month of the year.
CAL_SMONTHNAME2
Native name of the second month of the year.
CAL_SMONTHNAME3
Native name of the fifth month of the year.
CAL_SMONTHNAME4
Native name of the fourth month of the year.
CAL_SMONTHNAME5
Native name of the fifth month of the year.
CAL_SMONTHNAME6
Native name of the sixth month of the year.
CAL_SMONTHNAME7
Native name of the seventh month of the year.
CAL_SMONTHNAME8
Native name of the eighth month of the year.
CAL_SMONTHNAME9
Native name of the ninth month of the year.
CAL_SMONTHNAME10
Native name of the tenth month of the year.
CAL_SMONTHNAME11
Native name of the eleventh month of the year.
CAL_SMONTHNAME12
Native name of the twelfth month of the year.
CAL_SMONTHNAME13
Native name of the thirteenth month of the year, if it exists.
CAL_SSHORTDATE
Short date formats for this calendar type.

If the native name for the day of the week or for a month is an empty string, that name is identical to the name given in the corresponding locale information and therefore is not duplicated here.
The CAL_IYEAROFFSETRANGE and CAL_SERASTRING values vary in format depending on the type of optional calendar. The following example shows the values for these types (for each supported alternate calendar type) along with the formula for how to use the CAL_IYEAROFFSETRANGE value to compute the correct year given the Gregorian current year value Y:
CAL_ICALINTVALUE = "1"CAL_IYEAROFFSETRANGE = ""CAL_SERASTRING = ""CAL_ICALINTVALUE = "2"CAL_IYEAROFFSETRANGE = ""CAL_SERASTRING = ""CAL_ICALINTVALUE = "3"CAL_IYEAROFFSETRANGE = "1989\01926\01912\01868\0"CAL_SERASTRING = "Ux337B\0Ux337C\0Ux337D\0Ux337E\0"if (Y>=1989) { Y = (Y-1989)+1; } if (Y>=1926 && Y<1989) { Y = (Y-1926)+1; } if (Y>=1912 && Y<1926) { Y = (Y-1912)+1; }if (Y>=1868 && Y<1912) { Y = (Y-1868)+1; } if (Y<1868) { Y = Y; } CAL_ICALINTVALUE = "4"CAL_IYEAROFFSETRANGE = "1912\0"CAL_SERASTRING = "Ux4E2D\0Ux83EF\0Ux6C11\0Ux570B\0"if (Y>=1912) { Y = (Y-1912)+1; } if (Y<1912) { Y = Y; }CAL_ICALINTVALUE = "5"CAL_IYEAROFFSETRANGE = "2333\0"CAL_SERASTRING = ""Y = Y+2333;
+$#A Language Code and Scripts

Language/Locale Code Script
Greek ELL Other
Russian RUS Cyrillic
Turkish TRK Latin 2
Polish PLK Latin 2
Czech CSY Latin 2
Slovak SKY Latin 2
Hungarian HUN Latin 2
Danish DAN Latin 1
Dutch (Standard) NLD Latin 1
Belgian (Flemish) NLB Latin 1
English (American) ENU Latin 1
English (British) ENG Latin 1
English (Australian) ENA Latin 1
English (Canadian) ENC Latin 1
English (New Zealand) ENZ Latin 1
English (Ireland) ENI Latin 1
Finnish FIN Latin 1
French (Standard) FRA Latin 1
French (Belgian) FRB Latin 1
French (Canadian) FRC Latin 1
French (Swiss) FRS Latin 1
German (Standard) DEU Latin 1
German (Swiss) DES Latin 1
German (Austrian) DEA Latin 1
Icelandic ISL Latin 1
Italian (Standard) ITA Latin 1
Italian (Swiss) ITS Latin 1
Norwegian (Bokmal) NOR Latin 1
Norwegian (Nynorsk) NON Latin 1
Portuguese (Brazilian) PTB Latin 1
Portuguese (Standard) PTG Latin 1
Swedish SVE Latin 1
Spanish (Standard/Traditional) ESP Latin 1
Spanish (Mexican) ESM Latin 1
Spanish (Modern) ESN Latin 1

+$#AK Unicode and Character Sets
Unicode and traditional character sets give the Microsoft Windows operating system the means to support the many different written languages of the international marketplace. Unicode is a worldwide character-encoding standard that uses 16-bit character values to represent all the characters used in modern computing, including technical symbols and special characters used in publishing. Traditional character sets are previous character-encoding standards, such as the Windows ANSI character set, that use 8-bit character values or combinations of 8-bit values to represent the characters used in a specific language or geographical region.
This overview describes the character set functions and explains how to use them in your Win32-based applications.
+$#A About Unicode and Character Sets
Because the world's character-based data has been developed using both Unicode and traditional characters sets, the Win32 application programming interface (API) provides character set functions that help Win32-based applications convert the character-based data from its original character set to Unicode or another traditional character set. Because not all operating systems support Unicode, these character set functions also help Win32-based applications create character-based data that may be transferred to and used on any operating system.
For details about Unicode beyond the scope of this overview, see The Unicode™ Standard: Worldwide Character Encoding, Version 1.0, Volumes 1 and 2, Addison-Wesley Publishing Company: 1991, 1992, ISBN 0201567881.
+$#A Unicode and Character Set Reference
This section provides detailed reference information for the character set functions, structures, and messages. The information is separated into the following functional groups.
+$#A Character Sets
A character set is a mapping of characters to their identifying numeric values. Most of the character sets commonly used in computers are single-byte character sets in which each character is identified by a value one byte wide. The large number of characters in Asian languages led to the development of multibyte character sets, in particular the double-byte character set (DBCS). Microsoft® Windows NT™ incorporates a new global standard for character encoding: Unicode.
+$#A Single-byte Character Sets
A single-byte character set is a mapping of 256 individual characters to their identifying numeric values. The character codes 0x20 through 0x7E represent standardized displayable characters, but the characters represented by the remaining codes vary among character sets. The ASCII character set covers the range 0x00 through 0x7F.
In Windows, the ANSI character set is used in window manager and graphics device interface (GDI), but the MS-DOS file allocation table (FAT) file system uses a character set called the original equipment manufacturer (OEM) character set. Variations on the character sets, called code pages, include different special characters, typically customized for a language or group of languages. The OEM code page generally used in the United States is code page 437.
Applications that use the Microsoft® Win32® application programming interface (API) can use Unicode_win32_Unicode to avoid the inconsistencies of varied code pages and as an aid in developing easily localized applications.
An application can use the GetACP_win32_GetACP function to retrieve the ANSI code-page identifier for the system or use the GetOEMCP_win32_GetOEMCP function to retrieve the OEM code-page identifier.
The OemToChar_win32_OemToChar and OemToCharBuff_win32_OemToCharBuff functions allow an application to convert a character or string from the OEM code page to either the ANSI code page or Unicode. To convert in the other direction, you can use either the CharToOem_win32_CharToOem or CharToOemBuff_win32_CharToOemBuff function. In addition, an application can use the MultiByteToWideChar_win32_MultiByteToWideChar and WideCharToMultiByte_win32_WideCharToMultiByte functions to map single-byte character set (SBCS) strings to Unicode and Unicode strings to SBCS strings.
The GetCPInfo_win32_GetCPInfo function fills a CPINFO_win32_CPINFO_str structure with information that includes the size, in bytes, of the largest character in the code page and the default character used when a character code is entered that has no corresponding entry in the code page.
+$#A Double-byte Character Sets
The double-byte character set (DBCS) is called an expanded 8-bit character set because its smallest unit is a byte. It can be thought of as the ANSI character set for some Asian versions of Windows (particularly the Japanese version). Win32 functions for the Japanese version of Windows accept DBCS strings for the ANSI versions of the functions. However, unlike the handling of Unicode, DBCS character handling requires detailed changes in the character-processing algorithms throughout an application's source code.
An application can use the IsDBCSLeadByte_win32_IsDBCSLeadByte function to determine whether a given character is the first byte in a 2-byte character; this helps identify double-byte character sets. In addition, an application can use the MultiByteToWideChar_win32_MultiByteToWideChar and WideCharToMultiByte_win32_WideCharToMultiByte functions to map DBCS strings to Unicode and Unicode strings to DBCS strings.
+$#A Unicode
Unicode is a worldwide character-encoding standard. Windows uses it exclusively at the system level for character and string manipulation. Unicode simplifies localization of software and improves multilingual text processing. By implementing it in an application, a developer can enable the application with universal data exchange capabilities for global marketing, using a single binary file for every possible character code.
Unicode defines semantics for each character, standardizes script behavior, provides a standard algorithm for bidirectional text, and defines cross-mappings to other standards. Among the scripts supported by Unicode are Latin, Greek, Han, Hiragana, and Katakana. Supported languages include, but are not limited to, German, French, English, Greek, Chinese, and Japanese.
Unicode can represent all the world's characters in modern computer use, including technical symbols and special characters used in publishing. Because each Unicode character is 16 bits wide, it is possible to have separate values for up to 65,536 characters. Unicode-enabled functions are often referred to as "wide-character" functions.
Win32 functions support applications that use either Unicode or the regular ANSI character set. Mixed use in the same application is also possible. Adding Unicode support to an application is easy, and a developer can even maintain a single set of sources from which to compile an application that supports either Unicode or the Windows ANSI character set.
Win32 functions support Unicode by assigning its strings a specific data type and providing a separate set of entry points and messages to support this new data type. A series of macros and naming conventions make transparent migration to Unicode, or even compiling both non-Unicode and Unicode versions of an application from the same set of sources, a straightforward matter.
Implementing Unicode as a separate data type also enables the compiler's type checking to ensure that only Unicode parameters are used with functions expecting Unicode strings.
+$#A Unicode in Windows
Win32 API elements that use characters are generally implemented in one of three formats:
· A generic version that can be compiled for either ANSI or Unicode
· An ANSI version
· A Unicode version

This guide dicsusses Unicode data types and how they are used in functions and messages; the use of resources, filenames, and command-line arguments; and methods of translating between different types of strings.
+$#A Data Types
Most string operations for Unicode can be written by using the same logic used for handling the Windows ANSI character set, except that the basic unit of operation is a 16-bit character instead of an 8-bit byte. The header files provide a number of type definitions that make it easy to create sources that can be compiled for Unicode or for the ANSI character set.
The following example shows the method used in the Win32 header files to define three sets of data types: a set of generic type definitions that can compile for either ANSI or Unicode, and two sets of specific type definitions. The first set of specific type definitions is for use with the existing Windows (ANSI) character set, and the other is for use with Unicode (or wide) characters.
/* Generic types */

#ifdef UNICODE

typedef wchar_t TCHAR;

#else

typedef unsigned char TCHAR;

#endif

typedef TCHAR * LPTSTR, *LPTCH;

/* 8-bit character specific */

typedef unsigned char CHAR;

typedef CHAR *LPSTR, *LPCH;

/* Unicode specific (wide characters) */

typedef unsigned wchar_t WCHAR;

typedef WCHAR *LPWSTR, *LPWCH;

The letter T preceding a type definition designates a generic type that can be compiled for either ANSI or Unicode. The letter W preceding a type definition designates a wide-character (Unicode) type. For the actual implementation of this method, see the WINNT.H header file.
An application using generic data types can be compiled for Unicode simply by defining UNICODE before the include statements for the header files. To compile the code for ANSI, omit the UNICODE definition.
Developers are encouraged to use the generic data types, but the specific types exist for applications that require mixed-type control.
+$#A Function Prototypes
Function prototypes are also provided in generic, ANSI, and Unicode varieties, as shown below. The generic function prototype consists of the standard function name implemented as a macro. The preprocessor expands each macro into one of the specific function prototypes, depending on whether UNICODE is defined. The letters A (ANSI) and W (wide) are added at the end of the function names in the specific function prototypes. In this example, the generic prototype uses the generic type LPTSTR for the text parameter, but the A and W prototypes use the LPCSTR and LPCWSTR character types, respectively.
SetWindowText(HWND hwnd, LPTSTR lpText);

SetWindowTextA(HWND hwnd, LPCSTR lpText);

SetWindowTextW(HWND hwnd, LPCWSTR lpText);

An application can use the generic function and make Unicode dependent on whether the UNICODE option is used to compile the code, or it can make mixed calls by using the explicit function names ending with A and W.
This three-prototype approach applies to all functions with text arguments. Always use a generic function prototype with the generic string and character types. All function names that end with an uppercase W take wide-character arguments.
Some functions exist only in wide-character versions and can be used only with the appropriate data type.
The reference material for each Unicode-enabled function is labeled with a Unicode icon. If the function exists as a macro that the preprocessor must expand into an ANSI or Unicode form, only the macro form is documented.

Note Whenever a function has a length parameter for a character string, the length should be given as a count of characters (TCHAR units) in the string. However, functions that require or return pointers to untyped memory blocks, such as the GlobalAlloc_win32_GlobalAlloc function, are exceptions.

+$#A Message Translation
Although applications generally use the same window class, messages between windows of different classes are transparently translated by the system.
Even though a window function is implemented to receive messages in Unicode or ANSI format, the window procedure can still send messages or call functions of either type.
The following messages have text arguments and are subject to automatic text translation. (For information about automatic translation, see Subclassing and Automatic Message Translation_win32_Subclassing_and_Automatic_Message_Translation.)
CB_ADDSTRING_win32_CB_ADDSTRING WM_ASKCBFORMATNAME_win32_WM_ASKCBFORMATNAME
CB_DIR_win32_CB_DIR WM_CHAR_win32_WM_CHAR
CB_FINDSTRING_win32_CB_FINDSTRING WM_CHARTOITEM_win32_WM_CHARTOITEM
CB_GETLBTEXT_win32_CB_GETLBTEXT WM_CREATE_win32_WM_CREATE
CB_INSERTSTRING_win32_CB_INSERTSTRING WM_DEADCHAR_win32_WM_DEADCHAR
CB_SELECTSTRING_win32_CB_SELECTSTRING WM_DEVMODECHANGE_win32_WM_DEVMODECHANGE
WM_GETTEXT_win32_WM_GETTEXT
EM_GETLINE_win32_EM_GETLINE WM_MDICREATE_win32_WM_MDICREATE
EM_REPLACESEL_win32_EM_REPLACESEL WM_MENUCHAR_win32_WM_MENUCHAR
EM_SETPASSWORDCHAR_win32_EM_SETPASSWORDCHAR WM_NCCREATE_win32_WM_NCCREATE
WM_SETTEXT_win32_WM_SETTEXT
LB_ADDFILE_win32_LB_ADDFILE WM_SYSCHAR_win32_WM_SYSCHAR
LB_ADDSTRING_win32_LB_ADDSTRING WM_SYSDEADCHAR_win32_WM_SYSDEADCHAR
LB_DIR_win32_LB_DIR WM_WININICHANGE_win32_WM_WININICHANGE
LB_FINDSTRING_win32_LB_FINDSTRING
LB_GETTEXT_win32_LB_GETTEXT
LB_INSERTSTRING_win32_LB_INSERTSTRING
LB_SELECTSTRING_win32_LB_SELECTSTRING

+$#A String Functions
All of the string functions listed in this section exist in ANSI and Unicode implementations to support ANSI and Unicode arguments. There are, however, subtle differences among some of them.
The following string functions do not require special comment; their ANSI and Unicode implementations work identically.
CharNext_win32_CharNextCharPrev_win32_CharPrevlstrcat_win32_lstrcatlstrcpy_win32_lstrcpylstrlen_win32_lstrlen
The value returned by the lstrlen function is always the number of characters, regardless of whether the ANSI or Unicode form is used.
The following string functions are sensitive to the locale of the current thread (as derived from the locale the user selects in Control Panel). The lstrcmp and lstrcmpi functions do not perform byte comparisons like their ANSI C namesakes; they compare strings according to the rules of the selected locale.
CharLower_win32_CharLowerCharLowerBuff_win32_CharLowerBuffCharUpper_win32_CharUpperCharUpperBuff_win32_CharUpperBufflstrcmp_win32_lstrcmplstrcmpi_win32_lstrcmpi
The following functions convert between the OEM character set and either ANSI or Unicode, depending on which version is used.
CharToOem_win32_CharToOemCharToOemBuff_win32_CharToOemBuffOemToChar_win32_OemToCharOemToCharBuff_win32_OemToCharBuff
The print function wsprintf_win32_wsprintf supports Unicode by providing the following new and changed data types in its format specifications. These format specifications affect the way the wsprintf function interprets the corresponding passed-in parameter.
Format specification ANSI version Unicode version
c CHAR WCHAR
C WCHAR CHAR
hc, hC CHAR CHAR
hs, hS LPSTR LPSTR
lc, lC WCHAR WCHAR
ls, lS LPWSTR LPWSTR
s LPSTR LPWSTR
S LPWSTR LPSTR

The data type for the output text always depends on the version of the function. Where the data type of the passed-in parameter and of the output text do not agree, wsprintf will perform a conversion from Unicode to ANSI, or vice versa, as required.
For the Unicode version of wsprintf, the format string is Unicode, as is the output text.
+$#A Standard C Functions
The standard C libraries contain wide-character versions of the ANSI string functions that begin with the letters str. The wide-character versions of the functions start with the letters wcs (or sometimes _wcs). The Unicode data type is compatible with the wide-character data type wchar_t in ANSI C; this allows access to the wide-character string functions.
Generic functions exist for all standard C string functions. They start with the letters _tcs and are listed in the TCHAR.H header file. These functions use the generic data types TCHAR and TCHAR*
An application must add the following lines to its program in order to use the generic functions and compile for Unicode:
#define _UNICODE

#include <tchar.h>
#include <wchar.h>

Note that both the TCHAR.H and WCHAR.H are required, and that the leading underscore on the _UNICODE variable is also required.
The wcstombs and mbstowcs functions can convert from the character set supported by the standard C library to Unicode and back, with some limitations. For more information about translating strings to and from Unicode, see Translation Between String Types_win32_Translation_Between_String_Types.
The printf function defined in TCHAR.H supports the same format specifications as wsprintf_win32_wsprintf; for details, see String Functions_win32_String_Functions. Similarly, TCHAR.H contains a wprintf function, in which the format string itself is a Unicode string.
+$#A Filenames
In Windows, the ANSI character set is used in window manager and GDI; the MS-DOS FAT file system uses the OEM character set. Windows applications that create MS-DOS files have sometimes had to use the CharToOem_win32_CharToOem and OemToChar_win32_OemToChar functions to translate between these character sets. However, the New Technology file system (NTFS) file system is capable of storing filenames in Unicode; no translation is necessary with NTFS.
With Unicode implementations of the file-system functions, it is not necessary to perform translations to and from ANSI and OEM character sets. Instead, a developer can use a single source file to compile non-Unicode versions of an application by providing macros for functions that are not invoked when compiling for Unicode, such as CharToOem and OemToChar.
The special filename characters in MS-DOS are unchanged in Unicode filenames:
"\," "/," ".," "?," "*."
These special characters are in the ASCII range of characters (0x00 through 0x7F) and their Unicode equivalents are simply the same values in a 2-byte form: 0x0000 through 0x007F.
+$#A Translation Between String Types
The following Win32 functions translate character strings from one string type to another.
Function Description
FoldString_win32_FoldString Translates one character string to another.
LCMapString_win32_LCMapString Maps a character string by locale.
ToUnicode_win32_ToUnicode Translates a virtual-key code into a Unicode character.
MultiByteToWideChar_win32_MultiByteToWideChar Maps a multibyte string into a wide-character string.
WideCharToMultiByte_win32_WideCharToMultiByte Maps a wide-character string into a multibyte string.

The WideCharToMultiByte and MultiByteToWideChar functions are particularly useful for applications that support several string types. ANSI C also defines the conversion functions wcstombs and mbstowcs, but they can only convert to and from the character set supported by the standard C library.
+$#A Command-line Arguments
An application can use the GetCommandLine_win32_GetCommandLine function to retrieve Unicode command-line arguments by calling it as a Unicode function.
+$#A Using Strings and Unicode
· Using generic data types_win32_Using_Generic_Data_Types
· Registering window classes_win32_Registering_Window_Classes
· Subclassing and automatic message translation_win32_Subclassing_and_Automatic_Message_Translation
· Using special characters in Unicode_win32_Using_Special_Characters_in_Unicode
· Using functions that have no Unicode equivalents_win32_Using_Functions_That_Have_No_Unicode_Equivalents

+$#A Using Generic Data Types
If you use generic data types in your code, it can be compiled for Unicode simply by defining UNICODE before the include statements for the header files. To compile the code for ANSI, omit the UNICODE definition.
To convert code that processes strings so it can be compiled for either ANSI or Unicode, follow these steps.
1. Change all character and string types used for text to TCHAR, LPTSTR, or LPTCH.
2. Be sure that pointers to nontext data buffers or binary byte arrays are coded with the LPBYTE type and not mistakenly with the LPTSTR or LPTCH type. Declare pointers of indeterminate type explicitly as void pointers by using LPVOID, as appropriate.
3. Make pointer arithmetic type-independent. Using units of TCHAR size yields variables that are two bytes if UNICODE is defined, and one byte if UNICODE is not defined. Using pointer arithmetic always returns the number of elements pointed to by the pointer, whether the elements are one or two bytes in size. The following expression always returns the number of elements, regardless of whether UNICODE is defined:
cCount = lpEnd - lpStart;

The following expression determines the number of bytes used:
cByteCount = (lpEnd - lpStart) * sizeof(TCHAR);

There is no need to change a statement like the following one, because the pointer increment points to the next character element.
chNext = *++lpText;

4. Replace literal strings and manifest character constants with macros. Change expressions like the following one.
while(*lpFileName++ != '\\') {
/* .
/* .
/* .
}

Use the TEXT macro as follows in this expression.
while(*lpFileName++ != TEXT('\\')) {
./*
./*
./*
}

Precede a literal string in the Unicode form of a function by the letter L. The TEXT macro causes strings to be evaluated as L"string" when UNICODE is defined, and to "string" otherwise. The following call to the Unicode version of lstrlen demonstrates the use of the L identifier.
cch == lstrlenW(L"hello world");

For easier management, move literal strings into resources, especially if they contain characters outside the ASCII range (that is, 0x00 through 0x7F).
5. Call only the Unicode versions of the standard C library string functions, as listed in Standard C Functions_win32_Standard_C_Functions.
6. Change any code that relies on 255 as the largest value for a character.

When you compile code that you have changed as outlined above, the compiler creates the same binary file as it did before you made the changes. When you compile the code using the UNICODE option, however, it is compiled as a Unicode application.
+$#A Registering Window Classes
A window class is supported by a window procedure. You can register a window class by using either the RegisterClassA or RegisterClassW_win32_RegisterClass function. If you register the window class by using RegisterClassA, the application tells the system that the windows of the created class expect messages with text or character parameters to use the ANSI character set; if you register it by using RegisterClassW, the application requests that the system pass text parameters of messages as Unicode. The IsWindowUnicode_win32_IsWindowUnicode function enables applications to query the nature of each window.
The following example shows how to register an ANSI window class and a Unicode window class and how to write the window procedures for both cases. For the purposes of this example, all functions and structures are shown with the specific A or W data types. Using the techniques explained in Using Generic Data Types_win32_Using_Generic_Data_Types, you could alternatively write this example by using generic data types, so that it could be compiled as either ANSI or Unicode, depending on whether UNICODE is defined.
/* Register an ANSI window class. */

WNDCLASSA AnsiWndCls;

AnsiWndCls.style = CS_DBLCLKS | CS_PARENTDC;
AnsiWndCls.lpfnWndProc = (WNDPROC)AnsiWndProc;
AnsiWndCls.cbClsExtra = 0;
AnsiWndCls.cbWndExtra = 0;
AnsiWndCls.hInstance = hmodUser;
AnsiWndCls.hIcon = NULL;
AnsiWndCls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_IBEAM);
AnsiWndCls.hbrBackground = NULL;
AnsiWndCls.lpszMenuName = NULL;
AnsiWndCls.lpszClassName = "TestAnsi";

RegisterClassA(&AnsiWndCls);

/* Register a Unicode window class. */

WNDCLASSW UnicodeWndCls;

UnicodeWndCls.style = CS_DBLCLKS | CS_PARENTDC;
UnicodeWndCls.lpfnWndProc = (WNDPROC)UniWndProc;
UnicodeWndCls.cbClsExtra = 0;
UnicodeWndCls.cbWndExtra = 0;
UnicodeWndCls.hInstance = hmodUser;
UnicodeWndCls.hIcon = NULL;
UnicodeWndCls.hCursor = LoadCursor(NULL,(LPTSTR)IDC_IBEAM);
UnicodeWndCls.hbrBackground = NULL;
UnicodeWndCls.lpszMenuName = NULL;
UnicodeWndCls.lpszClassName = L"TestUnicode";

RegisterClassW(&UnicodeWndCls);

The following example shows the difference between handling the WM_CHAR message in an ANSI window procedure and a Unicode window procedure.
/* ANSI Window Procedure */

LONG AnsiWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*
* wParam - the value of the key
* lParam - (not used in this example)
*/

if (lstrcmpA("Q", (LPCSTR) wParam)) {
/*.
/*.
/*.
}
else {
./*
/*.
/*.
}
break;
./*
./* process other messages */
./*
}
}

/* Unicode Window Procedure */

LONG UniWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*
* wParam - the value of the key
* lParam - (not used in this example)
*/

if (lstrcmpW(L"Q", (LPCWSTR) wParam)) {
/*.
/*.
/*.
}
else {
/*.
/* .
/* .
}
break;
/* .
/* process other messages */
/* .
}
}

All text in messages received by AnsiWndProc will be composed of ANSI characters, and all text in messages received by UniWndProc will be composed of Unicode characters.
+$#A Subclassing and Automatic Message Translation
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. The system automatically translates messages into ANSI or Unicode form, depending on the form of the function that subclassed the window procedure.
The following call to the SetWindowLongA_win32_SetWindowLong function subclasses the current window procedure associated with the window identified by the hwnd parameter. The new window procedure, NewWndProc, will receive messages with text in ANSI format.
OldWndProc = (WNDPROC) SetWindowLongA(hwnd,
GWL_WNDPROC, (LONG)NewWndProc);

When NewWndProc has finished processing a message, it uses the CallWindowProc_win32_CallWindowProc function as foll ows to pass the message to OldWndProc.
C allWin dowProc(OldWndProc, hwnd, uMessage, wParam, lParam);

If OldWndProc was created with a class style of UNICODE, messages will be translated from the ANSI form received by NewWndProc into Unicode.
Similarly, a call to the SetWindowLongW_win32_SetWindowLong function would subclass the current window procedure with a window procedure that expects Unicode text messages. Message translation, if necessary, is performed during the processing of the CallWindowProc function.
For more information about subclassing, see Window Procedures_win32_Window_Procedures.
+$#A Using Special Characters in Unicode
There are a few special characters and characters with unusual meanings in Unicode text strings. To avoid unexpected problems with these characters, use the rules provided in the following topics.
· Null-terminated strings_win32_Null_terminated_Strings
· Byte-order mark_win32_Byte_order_Mark
· Escape sequences and control characters_win32_Escape_Sequences_and_Control_Characters
· Line and paragraph separators_win32_Line_and_Paragraph_Separators
· ASCII control codes 0x000D and 0x000A_win32_ASCII_Control_Codes_0x000D_and_0x000A
· Nonspacing characters and diacritics_win32_Nonspacing_Characters_and_Diacritics

+$#A Null-terminated Strings
Always cast zero to TCHAR when using null-terminated strings. The code 0x0000 is the Unicode string terminator for null-terminated strings. A single null byte is not sufficient for this code, because many Unicode characters contain null bytes as either the high or the low byte. An example is the letter A, for which the character code is 0x0041.
+$#A Byte-order Mark
Always prefix a Unicode plain text file with a byte-order mark. Because Unicode plain text is a sequence of 16-bit codes, it is sensitive to the byte ordering used when the text was written.
A byte-order mark is not a control character that selects the byte order of the text; it simply informs an application receiving the file that the file is byte ordered.
Ideally, all Unicode text would follow only one set of byte-ordering rules. This is not possible, however, because microprocessors differ in the position of the least significant byte: Intel® and MIPS® processors have the least significant byte first; Motorola processors (and byte-reversed Unicode files) have it last. With only a single set of byte-ordering rules, users of one type of microprocessor would be forced to swap the byte order every time a plain text file is read from or written to, even if the file is never transferred to another system based on a different microprocessor.
The preferred place to specify byte order is in a file header, but text files do not have headers. Therefore, Unicode has defined a character (0xFEFF) and a noncharacter (0xFFFE) as byte-order marks. They are mirror byte-images of each other.
Since the sequence 0xFEFF is exceedingly rare at the outset of regular non-Unicode text files, it can serve as an implicit marker or signature to identify the file as a Unicode file. Applications written to read both Unicode and non-Unicode text files should use the presence of this sequence as a near-certain indicator that the file is a Unicode file. (Compare this technique to using the MS-DOS EOF marker to terminate text files.)
When an application finds 0xFEFF at the beginning of a text file, it typically processes the file as though it were a Unicode file, although it may also perform further heuristic checks to verify that this is true. Such a check could be as simple as a test of whether the variation in the low-order bytes is much higher than the variation in the high-order bytes. For example, if ASCII text is converted to Unicode text, every second byte is zero. Also, checking both for the linefeed and carriage-return characters (0x000A and 0x000D) and for even or odd file size can provide a strong indicator of the nature of the file.
When an application finds 0xFFFE at the beginning of a text file, it interprets it to mean the file is a byte-reversed Unicode file. The application can either swap the order of the bytes or alert the user that an error has occurred.
The Unicode byte-order mark character is not found in any code page, so it disappears if data is converted to ANSI. Unlike other Unicode characters, it is not replaced by a default character when it is converted. If a byte-order mark is found in the middle of a file, it is not interpreted as a Unicode character and has no effect on text output.
The Unicode value 0xFFFF is illegal in plain text files and cannot be passed between Windows functions. The value 0xFFFF is reserved for an application's private use.
+$#A Escape Sequences and Control Characters
Translate escape sequences character by character into Unicode. When an ASCII plain text file is converted to Unicode, there is a chance that it will subsequently be converted back to ASCII. Converting escape sequences into Unicode on a character-by-character basis, rather than as a single 2-byte characters makes it possible to perform the reverse conversion without recognizing and parsing the escape sequences as such. For example, ESC+A should become 0x001B (ESC), 0x0041 (A), rather than 0x411B.
The first 32 sixteen-bit characters in Unicode are intended for the 32 control characters. This approach supports the existing use of control characters for formatting purposes ¾ that is, Unicode applications can treat these control characters in exactly the same way as they treat their ASCII equivalents.
+$#A Line and Paragraph Separators
Use the line-separator character (0x2028) and the paragraph-separator character (0x2029) to divide plain text. A new line is begun after each line separator. A new paragraph is begun after each paragraph separator.
Because these are separator codes, it is not necessary to start the first line or paragraph in the file with them, or to end the last line or paragraph with them. (Doing so would indicate that there is an empty line or paragraph in that location.)
The line separator can be used to indicate an unconditional end of line. However, line separators do not correspond to the carriage-return and linefeed characters, or to the carriage-return-linefeed character combination; they must be processed separately from carriage return and linefeed characters.
The paragraph separator can be inserted between paragraphs of text. This allows plain text files to be created that can be formatted with different line widths on different systems. The receiving system can ignore any line breaks and break paragraphs only at the paragraph separators.
+$#A ASCII Control Codes 0x000D and 0x000A
The Unicode standard does not prescribe a specific meaning for the control codes 0x000D (carriage return) and 0x000A (linefeed). There is no requirement that these codes be used in combination, and if used individually, either code may represent either itself only or both codes together. It is entirely up to the application to determine what these codes represent. (Applications have always had to interpret these control codes in ASCII; the Unicode policy is not a change in their use.)
+$#A Nonspacing Characters and Diacritics
When breaking lines or otherwise separating text, be sure to keep nonspacing characters with their base characters. Many scripts contain characters that visually combine with other characters on output ¾ for example, the diacritical marks (or "floating accents"). In Unicode, nonspacing characters follow their base character.
+$#A Using Functions That Have No Unicode Equivalents
Windows functions that have not been exported in a Unicode version have typically been replaced by more powerful or extended functions that do support Unicode. If you are porting code that calls the OpenFile_win32_OpenFile function, for example, you can support Unicode by using the CreateFile_win32_CreateFile or CreateFileEx_win32_CreateFile function instead.
If a function has no Unicode equivalent, you can map characters to and from 8-bit character sets before and after the function call. For example, the number-formatting functions atoi and itoa use only the digits 0 through 9. Normally, mapping Unicode to 8-bit characters could cause loss of data, but you can avoid this by making your code type independent and conditionalizing the expressions. For example, the following statements are type dependent and should be changed to support Unicode.
char str[4];

num = atoi(str);

These statements could be rewritten as follows to make them type independent.
TCHAR tstr[4];

CHAR strTmp[SIZE];

#ifdef UNICODE

wcstombs(strTmp, (const wchar_t *) tstr, sizeof(strTmp));

num = atoi(strTmp);

#else

num = atoi(tstr);

#endif

In this example, the wcstombs function is the standard C function that translates Unicode to ASCII. The example relies on the fact that the digits 0 through 9 can always be translated from Unicode to ASCII, even if some of the surrounding text cannot. The atoi function stops at any character that is not a digit. You can use the LCMapString_win32_LCMapString function if you need to process text that includes the native digits provided for some of the scripts in Unicode.
+$#A Unicode and Character Set Functions
This section describes the character set functions. On errors, some of these functions set an extended error value. Use the GetLastError_win32_GetLastError function to retrieve this value.
IsDBCSLeadByteEx_win32_IsDBCSLeadByteExTranslateCharsetInfo_win32_TranslateCharsetInfoWideCharToMultiByte_win32_WideCharToMultiByte
+$#A Obsolete Unicode Functions
The AnsiToOem_win32_AnsiToOem function is obsolete. Use the CharToOem_win32_CharToOem function.
The AnsiToOemBuff_win32_AnsiToOemBuff function is obsolete. Use the CharToOemBuff_win32_CharToOemBuff function.
The OemToAnsi_win32_OemToAnsi function is obsolete. Use the OemToChar_win32_OemToChar function.
The OemToAnsiBuff_win32_OemToAnsiBuff function is obsolete. Use the OemToCharBuff_win32_OemToCharBuff function.
+$#A Unicode and Character Set Structures
This section describes the structures used by the character set functions.
CHARSETINFO_win32_CHARSETINFO_strFONTSIGNATURE_win32_FONTSIGNATURE_strLOCALESIGNATURE_win32_LOCALESIGNATURE_str
+$#A Unicode and Character Set Constants
This section describes the constants used with the character set functions.
+$#A ANSI Code-Page Identifiers

Identifier Meaning
874 Thai
932 Japan
936 Chinese (PRC, Singapore)
949 Korean
950 Chinese (Taiwan, Hong Kong)
1200 Unicode (BMP of ISO 10646)
1250 Windows 3.1 Eastern European
1251 Windows 3.1 Cyrillic
1252 Windows 3.1 Latin 1 (US, Western Europe)
1253 Windows 3.1 Greek
1254 Windows 3.1 Turkish
1255 Hebrew
1256 Arabic
1257 Baltic

+$#A OEM Code-Page Identifiers
Identifier Meaning
437 MS-DOS United States
708 Arabic (ASMO 708)
709 Arabic (ASMO 449+, BCON V4)
710 Arabic (Transparent Arabic)
720 Arabic (Transparent ASMO)
737 Greek (formerly 437G)
775 Baltic
850 MS-DOS Multilingual (Latin I)
852 MS-DOS Slavic (Latin II)
855 IBM Cyrillic (primarily Russian)
857 IBM Turkish
860 MS-DOS Portuguese
861 MS-DOS Icelandic
862 Hebrew
863 MS-DOS Canadian-French
864 Arabic
865 MS-DOS Nordic
866 MS-DOS Russian (former USSR)
869 IBM Modern Greek
874 Thai
932 Japan
936 Chinese (PRC, Singapore)
949 Korean
950 Chinese (Taiwan, Hong Kong)

1361 Korean (Johab)

+$#A Code-Page Identifiers
Identifier Meaning
037 EBCDIC
437 MS-DOS United States
500 EBCDIC "500V1"
708 Arabic (ASMO 708)
709 Arabic (ASMO 449+, BCON V4)
710 Arabic (Transparent Arabic)
720 Arabic (Transparent ASMO)
737 Greek (formerly 437G)
775 Baltic
850 MS-DOS Multilingual (Latin I)
852 MS-DOS Slavic (Latin II)
855 IBM Cyrillic (primarily Russian)
857 IBM Turkish
860 MS-DOS Portuguese
861 MS-DOS Icelandic
862 Hebrew
863 MS-DOS Canadian-French
864 Arabic
865 MS-DOS Nordic
866 MS-DOS Russian
869 IBM Modern Greek
874 Thai
875 EBCDIC
932 Japan
936 Chinese (PRC, Singapore)
949 Korean
950 Chinese (Taiwan, Hong Kong)
1026 EBCDIC
1200 Unicode (BMP of ISO 10646)
1250 Windows 3.1 Eastern European
1251 Windows 3.1 Cyrillic
1252 Windows 3.1 US (ANSI)
1253 Windows 3.1 Greek
1254 Windows 3.1 Turkish
1255 Hebrew
1256 Arabic
1257 Baltic
1361 Korean (Johab)
10000 Macintosh Roman
10001 Macintosh Japanese
10006 Macintosh Greek I
10007 Macintosh Cyrillic
10029 Macintosh Latin 2
10079 Macintosh Icelandic
10081 Macintosh Turkish

+$#A Code-Page Bitfields

Bit Code page Description
ANSI
0 1252 Latin 1
1 1250 Latin 2: Eastern Europe
2 1251 Cyrillic
3 1253 Greek
4 1254 Turkish
5 1255 Hebrew
6 1256 Arabic
7 1257 Baltic
8 - 16 Reserved for ANSI
ANSI and OEM
17 874 Thai
18 932 JIS/Japan
19 936 Chinese: Simplified chars--PRC and Singapore
20 949 Korean Unified Hangeul Code (Hangeul TongHabHyung Code)
21 950 Chinese: Traditional chars--Taiwan and Hong Kong
22 - 29 Reserved for alternate ANSI and OEM
30 - 31 Reserved by system.
OEM
32 - 47 Reserved for OEM
48 869 IBM Greek
49 866 MS-DOS Russian
50 865 MS-DOS Nordic
51 864 Arabic
52 863 MS-DOS Canadian French
53 862 Hebrew
54 861 MS-DOS Icelandic
55 860 MS-DOS Portuguese
56 857 IBM Turkish
57 855 IBM Cyrillic; primarily Russian
58 852 Latin 2
59 775 Baltic
60 737 Greek; former 437 G
61 708 Arabic; ASMO 708
62 850 WE/Latin 1
63 437 US

+$#AK Networks
An application written for Microsoft® Windows® can use the network functions in the Microsoft® Win32® application programming interface (API) to implement networking capabilities without making allowances for a particular network provider or physical network implementation.
+$#A About Networks
Win32 network functions are network independent. Applications can use these functions to add and cancel network connections and to retrieve information about the current configuration of the network. In addition, the Win32 API implements a version of the NetBIOS network interface for applications that require it.
In addition to using the network functions, processes can also use the Win32 API implementations of mailslots and named pipes to communicate with one another. For more information about these subjects, see Mailslots_win32_Mailslots and Pipes_win32_Pipes.
The following figure shows the structure of a typical network.
{ewc msdncd, EWGraphic, group10691 0 /a "SDK_01.bmp"}
In the preceding figure, the hierarchy for Microsoft Advanced Server resources is given in detail as Network provider #1. Network resources from other providers have different hierarchical systems. An application does not need information about the hierarchy before it begins to work with a network. It can proceed from the network root (that is, the topmost container resource) and retrieve information about the network's resources as the information is required.
Network resources that contain other resources are called containers. Container resources are in boxes in the preceding figure.
Resources that do not contain other resources are objects. Sharepoint #1 and Sharepoint #2 in the figure are objects. A sharepoint is an object that is accessible across the network, such as a printer or a shared directory.
+$#A Network-Independent Functions
Windows version 3.1 included the following network-independent network functions:
WNetAddConnection_win32_WNetAddConnectionWNetCancelConnection_win32_WNetCancelConnectionWNetGetConnection_win32_WNetGetConnection
The Win32 API supports these functions and has added the following network and network enumeration functions:
WNetAddConnection2_win32_WNetAddConnection2 WNetEnumResource_win32_WNetEnumResource
WNetCancelConnection2_win32_WNetCancelConnection2 WNetGetLastError_win32_WNetGetLastError
WNetCloseEnum_win32_WNetCloseEnum WNetGetUser_win32_WNetGetUser
WNetConnectionDialog_win32_WNetConnectionDialog WNetOpenEnum_win32_WNetOpenEnum
WNetDisconnectDialog_win32_WNetDisconnectDialog

Many of these functions use a NETRESOURCE_win32_NETRESOURCE_str structure, which is also part of the Win32 API. WNetAddConnection_win32_WNetAddConnection and WNetCancelConnection_win32_WNetCancelConnection are supported for compatibility with earlier versions of Windows. However, new applications typically use WNetAddConnection2_win32_WNetAddConnection2 and WNetCancelConnection2_win32_WNetCancelConnection2 instead.
An application can use the network functions to browse, add, or cancel network connections anywhere in the hierarchy. By starting at the network root, the resources from all network providers can be enumerated.
A persistent connection is a network connection that Windows automatically restores when the user logs on. Using WNetAddConnection2 and WNetCancelConnection2, an application can control whether a network connection is persistent from one session to the next.
+$#A The Netbios Function
The Netbios_win32_Netbios function is provided primarily for applications that were written for the NetBIOS interface and need to be ported to Windows. Applications not requiring compatibility with NetBIOS can usually use other interfaces, such as mailslots and named pipes, to accomplish tasks similar to those supported by Netbios.
Netbios in the Win32 API takes one parameter: a pointer to a structure describing a network control block (NCB). The NCB_win32_NCB_str structure contains information about the network environment and a pointer to a buffer that is used for messages or for further data about the network.
+$#A Windows NetBios Enhancements
The Win32 implementation of the Netbios_win32_Netbios function includes the following enhancements that are not part of the NetBIOS 3.0 specification:
· POST routines can be called from the C programming language by means of the ncb_post member of the NCB_win32_NCB_str structure.
· Each asynchronous network control block can supply an event (in the ncb_event member of the NCB structure) set to the signaled state when the NCB completes.
· An application can enumerate all available LAN adapters by using the NCBENUM command in the ncb_command member of the NCB structure.

+$#A Differences Between Windows and NetBIOS
In addition to the enhancements listed in the preceding section, the following differences exist between the Win32 implementation of the Netbios_win32_Netbios function and the NetBIOS 3.0 specification:
· The system does not monitor the number of outstanding NCB commands specified when the NCBRESET command is issued. The memory quota limits for Windows are used to limit the total resources an application can use.
· Asynchronous commands on invalid sessions are rejected when they are submitted.
· The numbers of the network names (the ncb_num member of the NCB_win32_NCB_str structure) are assigned on a per-process basis.
· Requests for the status of the local adapter retrieve only the names that were added by the process making the request.
· The value 1 for the ncb_lana_num member of the NCB_win32_NCB_str structure is not exclusive when the NCBRESET command is issued. All MS-DOS and 16-bit Windows-based applications also share access to name number 1.

+$#A Using Networks
The Windows network functions enable your application to query and control network connections directly or to give direct control of the network connections to the user. To use these functions, you must link to the multiple-provider router library (MPR.LIB). This section discusses how to use the network functions in your application.
· Using the connections dialog box_win32_Using_the_Connections_Dialog_Box
· Enumerating network resources_win32_Enumerating_Network_Resources
· Adding a network connection_win32_Adding_a_Network_Connection
· Retrieving the connection name_win32_Retrieving_the_Connection_Name
· Retrieving the user name_win32_Retrieving_the_User_Name
· Canceling a network connection_win32_Canceling_a_Network_Connection
· Retrieving network errors_win32_Retrieving_Network_Errors

+$#A Using the Connections Dialog Box
The WNetConnectionDialog_win32_WNetConnectionDialog function creates a dialog box that allows the user to browse and connect to network resources. Conversely, the WNetDisconnectDialog_win32_WNetDisconnectDialog function creates a dialog box that allows the user to disconnect from them.
The following example shows how to use the WNetConnectionDialog function to create a dialog box that displays disk resources.
DWORD dwResult;

dwResult = WNetConnectionDialog(hwnd,
RESOURCETYPE_DISK);

if(dwResult != NO_ERROR) {

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetConnectionDialog");
return FALSE;
}

+$#A Enumerating Network Resources
To enumerate a network container resource, your application should pass the address of a NETRESOURCE_win32_NETRESOURCE_str structure to the WNetOpenEnum_win32_WNetOpenEnum function. WNetOpenEnum creates a handle to the resource described by the NETRESOURCE structure. The application then passes this handle to the WNetEnumResource_win32_WNetEnumResource function, which returns information about the resource in the form of an array of NETRESOURCE structures. When the handle is no longer needed, the application can close it by calling the WNetCloseEnum_win32_WNetCloseEnum function.
Your application can continue enumerating any container resource described in the array of NETRESOURCE structures retrieved by WNetEnumResource. If the dwUsage member of the NETRESOURCE structure is RESOURCEUSAGE_CONTAINER, the application can pass the address of that structure to WNetOpenEnum to open the container and continue the enumeration. If dwUsage is RESOURCEUSAGE_CONNECTABLE, the application can pass the structure's address to the WNetAddConnection2_win32_WNetAddConnection2 function.
The following example illustrates an application-defined function (EnumerateFunc) that enumerates all the resources on a network. When calling this function, specify NULL for the pointer to the NETRESOURCE structure. When WNetOpenEnum receives the NULL pointer, it retrieves a handle to the root of the network. Whenever a NETRESOURCE structure retrieved by WNetEnumResource is RESOURCEUSAGE_CONTAINER, the EnumerateFunc function calls itself and uses a pointer to that structure in its call to WNetOpenEnum.
BOOL FAR PASCAL EnumerateFunc(hwnd, hdc, lpnr)
HWND hwnd;
HDC hdc;
LPNETRESOURCE lpnr;
{
DWORD dwResult, dwResultEnum;
HANDLE hEnum;
DWORD cbBuffer = 16384; /* 16K is reasonable size */
DWORD cEntries = 0xFFFFFFFF; /* enumerate all possible entries */
LPNETRESOURCE lpnrLocal; /* pointer to enumerated structures */
DWORD i;

dwResult = WNetOpenEnum(RESOURCE_GLOBALNET,
RESOURCETYPE_ANY,
0, /* enumerate all resources */
lpnr, /* NULL first time this function is called */
&hEnum); /* handle to resource */

if (dwResult != NO_ERROR) {

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetOpenEnum");
return FALSE;
}

do {

/* Allocate memory for NETRESOURCE structures. */

lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);

dwResultEnum = WNetEnumResource(hEnum, /* resource handle */
&cEntries, /* defined locally as 0xFFFFFFFF */
lpnrLocal, /* LPNETRESOURCE */
&cbBuffer); /* buffer size */

if (dwResultEnum == NO_ERROR) {
for(i = 0; i < cEntries; i++) {

/*
* Following is an application-defined function for
* displaying contents of NETRESOURCE structures.
*/

DisplayStruct(hdc, &lpnrLocal[i]);

/*
* If this NETRESOURCE is a container, call the function
* recursively.
*/

if(RESOURCEUSAGE_CONTAINER ==
(lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER))
if(!EnumerateFunc(hwnd, hdc, &lpnrLocal[i]))
TextOut(hdc, 10, 10,
"EnumerateFunc returned FALSE.", 29);

}
}

else if (dwResultEnum != ERROR_NO_MORE_ITEMS) {
NetErrorHandler(hwnd, dwResultEnum, (LPSTR)"WNetEnumResource");
break;
}
}
while(dwResultEnum != ERROR_NO_MORE_ITEMS);

GlobalFree((HGLOBAL) lpnrLocal);

dwResult = WNetCloseEnum(hEnum);

if(dwResult != NO_ERROR) {
NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCloseEnum");
return FALSE;
}

return TRUE;
}

+$#A Adding a Network Connection
To make a connection to a network resource described by a NETRESOURCE_win32_NETRESOURCE_str structure, an application can call the WNetAddConnection2_win32_WNetAddConnection2 function, as shown in the following example.
DWORD dwResult;
NETRESOURCE nr;

dwResult = WNetAddConnection2(&nr, /* NETRESOURCE from enumeration */
(LPSTR) NULL, /* no password */
(LPSTR) NULL, /* logged-in user */
CONNECT_UPDATE_PROFILE); /* update profile with connect info */

if (dwResult == ERROR_ALREADY_ASSIGNED) {
TextOut(hdc, 10, 10, "Already connected to specified resource.", 40);
return FALSE;
}

else if (dwResult == ERROR_DEVICE_ALREADY_REMEMBERED) {
TextOut(hdc, 10, 10,
"Attempted reassignment of remembered device.", 44);
return FALSE;
}
else if(dwResult != NO_ERROR) {

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetAddConnection2");
return FALSE;
}

TextOut(hdc, 10, 10, "Connected to specified resource.", 32);
return TRUE;

The WNetAddConnection_win32_WNetAddConnection function is supported for compatibility with earlier versions of Windows. For new applications, use WNetAddConnection2_win32_WNetAddConnection2.
+$#A Retrieving the Connection Name
To retrieve the name of the network resource associated with a local device, an application can use the WNetGetConnection_win32_WNetGetConnection function, as shown in the following example.
CHAR szDeviceName[80];
DWORD dwResult, cchBuff = sizeof(szDeviceName);

dwResult = WNetGetConnection("z:",
(LPSTR) szDeviceName,
&cchBuff);

switch (dwResult) {
case NO_ERROR:
TextOut(hdc, 10, 10, (LPSTR) szDeviceName,
lstrlen((LPSTR) szDeviceName));
break;

case ERROR_NOT_CONNECTED:
TextOut(hdc, 10, 10, "Device z: not connected.", 24);

case ERROR_CONNECTION_UNAVAIL:

/* A connection is remembered but not connected. */

TextOut(hdc, 10, 10, "Connection unavailable.", 23);

default:

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetGetConnection");
return FALSE;
}

+$#A Retrieving the User Name
To retrieve the name of the user associated either with a local device connected to a network resource or with the name of a network, an application can call the WNetGetUser_win32_WNetGetUser function. The following example uses the device name to retrieve the name of the user.
CHAR szUserName[80];
DWORD dwResult, cchBuff = 80;

dwResult = WNetGetUser("z:",
(LPSTR) szUserName,
&cchBuff);

if(dwResult == NO_ERROR)
TextOut(hdc, 10, 10,
(LPSTR) szUserName,
lstrlen((LPSTR) szUserName));

else {

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetGetUser");
return FALSE;
}

+$#A Canceling a Network Connection
To cancel a connection to a network resource, an application can call the WNetCancelConnection2_win32_WNetCancelConnection2 function, as shown in the following example.
DWORD dwResult;

dwResult = WNetCancelConnection2("z:",
CONNECT_UPDATE_PROFILE, /* remove connection from profile */
FALSE); /* fail if open files or jobs */

if (dwResult == ERROR_NOT_CONNECTED) {
TextOut(hdc, 10, 10, "Drive z: not connected.", 23);
return FALSE;
}

else if(dwResult != NO_ERROR) {

/*
* An application-defined error handler is demonstrated in the
* section titled "Retrieving Network Errors."
*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCancelConnection2");
return FALSE;
}

TextOut(hdc, 10, 10, "Connection closed for z:.", 25);

The WNetCancelConnection_win32_WNetCancelConnection function is supported for compatibility with earlier versions of Windows. For new applications, use WNetCancelConnection2_win32_WNetCancelConnection2.
+$#A Retrieving Network Errors
When one of the Windows network functions returns WN_EXTENDED_ERROR, an application can call the WNetGetLastError_win32_WNetGetLastError function to get more information about the error that occurred. This information is usually specific to the network provider.
The following example illustrates an application-defined error-handling function (NetErrorHandler) that takes three arguments: a window handle, the error code returned by one of the Windows network functions, and the name of the function that produced the error. If the error code is WN_EXTENDED_ERROR, NetErrorHandler calls WNetGetLastError to get extended error information.
BOOL WINAPI NetErrorHandler(hwnd, dwErrorCode, lpszFunction)
HWND hwnd;
DWORD dwErrorCode;
LPSTR lpszFunction;
{
DWORD dwWNetResult, dwLastError;
CHAR szError[256];
CHAR szCaption[256];
CHAR szDescription[256];
CHAR szProvider[256];

/* The following code performs standard error-handling. */

if (dwErrorCode != ERROR_EXTENDED_ERROR) {
wsprintf((LPSTR) szError, "%s failed; \nResult is %ld",
lpszFunction, dwErrorCode);
wsprintf((LPSTR) szCaption, "%s error", lpszFunction);
MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);
return TRUE;
}

/*
* The following code performs error-handling when the
* ERROR_EXTENDED_ERROR return value indicates that WNetGetLastError
* can retrieve additional information.
*/

else {
dwWNetResult = WNetGetLastError(&dwLastError,
(LPSTR) szDescription, /* buffer for error description */
sizeof(szDescription),
(LPSTR) szProvider, /* buffer for provider name */
sizeof(szProvider));

if(dwWNetResult != NO_ERROR) {
wsprintf((LPSTR) szError,
"WNetGetLastError failed; error %ld", dwWNetResult);
MessageBox(hwnd, (LPSTR) szError,
"WNetGetLastError", MB_OK);
return FALSE;
}

wsprintf((LPSTR) szError,
"%s failed with code %ld;\n%s",
(LPSTR) szProvider, dwLastError, (LPSTR) szDescription);
wsprintf((LPSTR) szCaption, "%s error", lpszFunction);
MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);
return TRUE;
}
}

+$#A Network Functions
Following are the functions used with networks.
Netbios_win32_NetbiosWNetAddConnection_win32_WNetAddConnectionWNetAddConnection2_win32_WNetAddConnection2WNetAddConnection3_win32_WNetAddConnection3WNetCancelConnection_win32_WNetCancelConnectionWNetCancelConnection2_win32_WNetCancelConnection2WNetCloseEnum_win32_WNetCloseEnumWNetConnectionDialog_win32_WNetConnectionDialogWNetDisconnectDialog_win32_WNetDisconnectDialogWNetEnumResource_win32_WNetEnumResourceWNetGetConnection_win32_WNetGetConnectionWNetGetLastError_win32_WNetGetLastErrorWNetGetUniversalName_win32_WNetGetUniversalNameWNetGetUser_win32_WNetGetUserWNetOpenEnum_win32_WNetOpenEnum

+$#AK LAN Manager Functions
#Overview of Windows Networking Functions
For OS/2®-based servers the Microsoft® LAN Manager functions provided much of the functionality required for a network operating system; this functionality was missing from the local operating system. The Microsoft® Windows NT™ operating system will provide this missing functionality in the base. However, in order to make the migration of existing 16-bit applications as easy as possible, it is necessary to still define 32-bit equivalents of some of the LAN Manager functions.
The Windows networking functions specified in this online help are designed to provide some of the API functionality that was available in LAN Manager 2.x. They are not the base Windows NT networking functions. Windows NT takes some of the functionality that was previously supplied by the networking software, and moves this into the base functions (such as error and audit logging, printing). Windows NT also provides a network-independent set of network functions (the WNet functions) that allow network functions to work across different network vendors' products. If a base function or WNet function exists that could be used by your application, you should convert from the Windows networking function to the public Windows NT equivalent. There are at least three reasons to make the change now:
1. The WNet functions are network independent, while the Windows networking functions only work on LAN Manager networks.
2. Some of the Windows networking functions specified in this document may not be supported in future releases of Windows NT if they have been superseded by base functions or WNet functions. (Of course Microsoft does not plan to remove specific Windows networking functions unless equivalent or better functionality is available from other functions.)
3. The Windows networking functions may not be supported on some other future Microsoft platforms, since they are not part of the Microsoft® Win32® application programming interface (API).

In this documentation, equivalent Win32 functions will be listed that could be used in place of the Windows networking function. If at all possible, this is the function that your application should use.
This documentation assumes that you are familiar with the existing set of 16-bit LAN Manager functions. The 32-bit widening of the API data structures is specified here, but the description of the functionality for each field is not covered unless the functionality has changed from that specified in the LAN Manager 2.x Programmer's Reference.
+$#A API Buffers
The RPC run times will allocate the buffers required to remote the APIs. This is a requirement for both efficiency and interoperability. Using the RPC run times to allocate the API transmission buffers results in the two significant differences between a LAN Manager 2.x API and windows networking function.
· For a set type function (data to the server) the function caller specifies a buffer containing the information structure relevant to the function level but does not specify the buffer length.
· For a get type function (data returned from the server) the caller does not pre-allocate a buffer for the return information. The caller passes a LPBYTE * to the function on input. On successful return the buffer pointer will contain a pointer to a buffer containing the return information. When the caller has finished processing the returned information NetApiBufferFree must be called. This simplifies the calling code, because the caller does not need to guess at the size of the buffer required and will not need to resize and reissue the function as was the case with the LAN Manager 2.x functions.

+$#A API Data Alignment
All data structures specified for the Windows networking functions will be 32-bit word aligned. The base size for an API structure element will be a DWORD.
+$#A Enumeration Buffer Lengths
Enumeration functions will take an advisory maximum data length parameter, prefmaxlen, which allows a control on the number of bytes returned from an enumeration call. The prefered length is specified in units of 8-bit bytes. The actual function may return more than the prefered maximum length.
A Windows networking function enumeration call will not return partial entries.
+$#A Parameter Error Reporting
Add and SetInfo functions will return an index for a parameter in error. The caller may pass a NULL pointer for the parm_err parameter indicating that the field should not be set by the function. For remoted functions to downlevel servers this field will be returned as PARM_ERROR_UNKNOWN by the RpcXlate conversion layer.
+$#A Parmnum for SetInfo
To reduce the number of RPC interfaces we need for each SetInfo function, there will be no Parmnum parameter for the 32-bit SetInfo functions. Instead, the fields that can be set in the information structures will be set using additional infolevels. Although this will increase the number of information levels for a specified function, it will be easier to program to them. In an attempt to keep the mapping to and from downlevel calls simple, the new information levels will be easily mapped to the old parmnum values. By subtracting a specific number like PARMNUM_BASE_INFOLEVEL (whose value will be 1000).
+$#A Parmnum for GetInfo
There will be no addition of a parmnum field for the GetInfo function.
+$#A Embedded Strings
Windows networking function info structures will not contain embedded strings. This improves the alignability of the info structures and allows for OEM flexibility in the core functions. This does not change the feel of the LAN Manager 2.x functions that support string pointers. Code porting merely requires a pointer assignment rather than a string copy.
Any API information field that is returned in an enumeration call that can be subsequently used as a key for a GetInfo call is guaranteed to be present in the enumeration buffer. If the variable length information string that would specify the key field value will not fit then the entire fixed length structure for the entry is not returned. Other variable-length fields will be returned as a NULL pointer for the case where the string will not fit as with LAN Manager 2.x.
+$#A Enumeration Resume Handles
Enumeration resume handles will be identifiers for the actual resume key contained in the instance data for the function. This is required for security, interoperabilty and to simplify the caller code for the function.
If a NULL is passed for the pointer to the resume handle, then no handle is stored and the ennumeration search cannot be continued. This is useful in cases where the application does not want to enumerate all the items.
If an error is returned from an enumeration call, the resume handle must be treated as invalid and not used for any subsequent enumeration calls. For example, the enumeration should be restarted from the beginning.
+$#A User-Specific Information
The LAN Manager 2.x functions assumed that there would only be one user per computer. Therefore, some functions have that assumption built in, such as the NetSession functions. Since this assumption is not valid on Windows NT, the functions have had to be modified to take a user name as an additional parameter in order to be able to uniquely identify the information being queried or set.
+$#A Enumeration Functions
The enumeration functions all return the number of entries read, the total entries that could have been returned if the buffer was big enough, a buffer with the entries, and a return code. The following table details what an application can expect for the various parameters (M is less than N):
Condition entriesread totalentries buffer returncode
No entries 0 0 NULL SUCCESS
>= 1 entry N N NOT NULL SUCCESS
>= 1 entry M N NOT NULL ERROR_MORE_DATA
Failure ? ? ? Error Code

The caller must first examine the returned status before proceeding to examine any of the other parameters for data.
Note that "totalentries" is the total number of entries that would have been returned if the return buffer was big enough. For example, the total number of entries that were available for entries, using the current resume handle, if any, when the function was called. The one exception to this occurs in the NetUserEnum function when receiving data from a downlevel (OS/2 LANMAN) computer. In this case, the total entries always reflects the total number of entries in the database, no matter where it is in the resume sequence.
+$#A SetInfo Functions
All the SetInfo functions take a structure that contains the new information to be set. If any of the pointer fields in the structure are NULL pointers, then the corresponding field is not changed by the call. This is consistent with LAN Manager 2.x functionality.
In addition, the information levels that allow GUEST access to query information do not allow callers with GUEST access to set the fields. For example, a caller with GUEST that calls the SetInfo function will probably get an error back from the SetInfo call.
+$#A Windows Networking Functions
Function Status
Windows networking functions will use the LAN Manager 2.x error reporting convention. For example, a successful function call returns 0, a nonzero return code indicates an error in the range of the LAN Manager 2.x API/OS2 errors.
An extended error range will be added for Windows NT specific errors.
Since the Windows networking functions will use RPC, the API error definition will also be extended to include RPC error codes.
+$#A RPC Buffer Allocation Errors
Since the RPC run time will allocate memory for send and receive buffers, the function should expect to see RPC allocation errors. In the event of an RPC allocation error, a resumable API handle is invalidated. This is a requirement since resumable functions are not rewindable.
+$#A Obsolete Information Fields
Many of the information fields in the core information structures will be obsolete in the Windows NT service implementation. These fields will remain in the information structure for compatibility with 16-bit versions of Windows and will be returned with an intelligent default on Windows NT systems.
+$#A NLS Support
There are only Unicode versions of the Windows networking functions. If you use these functions, you MUST define Unicode in your source file before including any of the LM include files.
+$#A Windows Networking Function Definitions
The Windows networking functions are 'stdcall' calling convention functions that return a DWORD API status:
#define NET_API_STATUS DWORD
#define NET_API_FUNCTION __stdcall

+$#A Buffer Manipulation Functions
For remotable functions that return information to the caller, the RPC run time will allocate the buffer containing the return information. When the caller has finished processing the returned information, NetApiBufferFree must be called. Additional buffer manipulation functions are also supported.
The buffer manipulation functions are:
NetApiBufferAllocate_win32_NetApiBufferAllocate
NetApiBufferFree_win32_NetApiBufferFree
NetApiBufferReallocate_win32_NetApiBufferReallocate
NetApiBufferSize_win32_NetApiBufferSize
+$#A Service Functions

Note A complete set of service functions are provided in the Win32 API. These should be used in place of the NetService functions, unless you have a requirement to control services on a downlevel server. For more details, see Services_win32_Services.

Service functions control services. A service is an application that an administrator can control using the Service Controller interfaces.
Services allow administrators to control applications on the network and maintain the integrity of users' data. On a typical network, applications are shared by many users. If an administrator terminates an application running on a server, a user who has not finished working with that application can lose important data. When an application is implemented as a service, the service controller checks the status before changing the state of the service. Windows NT Networking provides several standard services, such as the Workstation, Server, and Messenger services.
A service can be started using the service functions. At startup time, the service defines whether it can be stopped, paused, and continued.
NetServiceControl_win32_NetServiceControl controls the operations of network services, and it can provide time hints to controlling applications. NetServiceEnum_win32_NetServiceEnum retrieves information about all started services. NetServiceGetInfo_win32_NetServiceGetInfo retrieves information about a particular started service. NetServiceInstall_win32_NetServiceInstall starts a network service. NetServiceStatus sets status and code information for a network service.
The service functions provide service information at three levels:
typedef struct _SERVICE_INFO_0 { LPWSTR svci0_name; /* service name */} SERVICE_INFO_0, *PSERVICE_INFO_0, *LPSERVICE_INFO_0;

typedef struct _SERVICE_INFO_1 { LPWSTR svci1_name; DWORD svci1_status; DWORD svci1_code; DWORD svci1_pid;} SERVICE_INFO_1, *PSERVICE_INFO_1, *LPSERVICE_INFO_1;

typedef struct _SERVICE_INFO_2 { LPWSTR svci2_name; DWORD svci2_status; DWORD svci2_code; DWORD svci2_pid; LPWSTR svci2_text;
DWORD svci2_specific_error;} SERVICE_INFO_2, *PSERVICE_INFO_2, *LPSERVICE_INFO_2;

The service functions are:
NetServiceControl_win32_NetServiceControl
NetServiceEnum_win32_NetServiceEnum
NetServiceGetInfo_win32_NetServiceGetInfo
NetServiceInstall_win32_NetServiceInstall
+$#A Server Functions
Server functions perform administrative tasks on a local or remote server. Any user or application with admin group membership on a local or remote server can perform administrative tasks on that server to control its operation, user access, and resource sharing. The low-level parameters that affect a server's operation can be examined and modified by calling NetServerGetInfo_win32_NetServerGetInfo and NetServerSetInfo_win32_NetServerSetInfo.
The LAN Manager 2.x server API included several fields that logically belong to other LAN Manager services and core Windows NT components. For this reason the server information levels available in LAN Manager 2.x are no longer available in Windows networking functions. The server specific information is available in 3 levels starting at base level 100.
+$#A Server System Information
Level 100 available with guest access:
typedef struct _SERVER_INFO_100 { DWORD sv100_platform_id; LPWSTR sv100_name;} SERVER_INFO_100, *PSERVER_INFO_100, *LPSERVER_INFO_100;

Level 101 available with User access:
typedef struct _SERVER_INFO_101 { DWORD sv101_platform_id; LPWSTR sv101_name; DWORD sv101_version_major; DWORD sv101_version_minor; DWORD sv101_type; LPWSTR sv101_comment;} SERVER_INFO_101, *PSERVER_INFO_101, *LPSERVER_INFO_101;

Level 102 available with Admin access:
typedef struct SERVER_INFO_102 { DWORD sv102_platform_id; LPWSTR sv102_name; DWORD sv102_version_major; DWORD sv102_version_minor; DWORD sv102_type; LPWSTR sv102_comment; DWORD sv102_users; DWORD sv102_disc; BOOL sv102_hidden; DWORD sv102_announce; DWORD sv102_anndelta; LPWSTR sv102_userpath;} SERVER_INFO_102, *PSERVER_INFO_102, *LPSERVER_INFO_102;

Level 400-499 OS/2 Server information.
Level 402 available with Admin access:
typedef struct _SERVER_INFO_402 { DWORD sv402_ulist_mtime; DWORD sv402_glist_mtime; DWORD sv402_alist_mtime; LPWSTR sv402_alerts; DWORD sv402_security; DWORD sv402_numadmin; DWORD sv402_lanmask; LPWSTR sv402_guestacct; DWORD sv402_chdevs; DWORD sv402_chdevq; DWORD sv402_chdevjobs; DWORD sv402_connections; DWORD sv402_shares; DWORD sv402_openfiles; DWORD sv402_sessopens; DWORD sv402_sessvcs; DWORD sv402_sessreqs; DWORD sv402_opensearch; DWORD sv402_activelocks; DWORD sv402_numreqbuf; DWORD sv402_sizreqbuf; DWORD sv402_numbigbuf; DWORD sv402_numfiletasks; DWORD sv402_alertsched; DWORD sv402_erroralert; DWORD sv402_logonalert; DWORD sv402_accessalert; DWORD sv402_diskalert; DWORD sv402_netioalert; DWORD sv402_maxauditsz; LPWSTR sv402_srvheuristics;} SERVER_INFO_402, *PSERVER_INFO_402, *LPSERVER_INFO_402;

Level 403 available with Admin access:
typedef struct _SERVER_INFO_403 { DWORD sv403_ulist_mtime; DWORD sv403_glist_mtime; DWORD sv403_alist_mtime; LPWSTR sv403_alerts; DWORD sv403_security; DWORD sv403_numadmin; DWORD sv403_lanmask; LPWSTR sv403_guestacct; DWORD sv403_chdevs; DWORD sv403_chdevq; DWORD sv403_chdevjobs; DWORD sv403_connections; DWORD sv403_shares; DWORD sv403_openfiles; DWORD sv403_sessopens; DWORD sv403_sessvcs; DWORD sv403_sessreqs; DWORD sv403_opensearch; DWORD sv403_activelocks; DWORD sv403_numreqbuf; DWORD sv403_sizreqbuf; DWORD sv403_numbigbuf; DWORD sv403_numfiletasks; DWORD sv403_alertsched; DWORD sv403_erroralert; DWORD sv403_logonalert; DWORD sv403_accessalert; DWORD sv403_diskalert; DWORD sv403_netioalert; DWORD sv403_maxauditsz; LPWSTR sv403_srvheuristics; DWORD sv403_auditedevents; DWORD sv403_autoprofile; LPWSTR sv403_autopath;} SERVER_INFO_403, *PSERVER_INFO_403, *LPSERVER_INFO_403;

Level 500-599 Windows NT Server information.
Level 502 available with Admin access:
typedef struct _SERVER_INFO_502 { DWORD sv502_sessopens; DWORD sv502_sessvcs; DWORD sv502_opensearch; DWORD sv502_sizreqbuf; DWORD sv502_initworkitems; DWORD sv502_maxworkitems; DWORD sv502_rawworkitems; DWORD sv502_irpstacksize; DWORD sv502_maxrawbuflen; DWORD sv502_sessusers; DWORD sv502_sessconns; DWORD sv502_maxpagedmemoryusage; DWORD sv502_maxnonpagedmemoryusage; BOOL sv502_enablesoftcompat; BOOL sv502_enableforcedlogoff; BOOL sv502_timesource; BOOL sv502_acceptdownlevelapis; BOOL sv502_lmannounce;} SERVER_INFO_502, *PSERVER_INFO_502, *LPSERVER_INFO_502;

Level 503 available with Admin access:
typedef struct _SERVER_INFO_503 { DWORD sv503_sessopens; DWORD sv503_sessvcs; DWORD sv503_opensearch; DWORD sv503_sizreqbuf; DWORD sv503_initworkitems; DWORD sv503_maxworkitems; DWORD sv503_rawworkitems; DWORD sv503_irpstacksize; DWORD sv503_maxrawbuflen; DWORD sv503_sessusers; DWORD sv503_sessconns; DWORD sv503_maxpagedmemoryusage; DWORD sv503_maxnonpagedmemoryusage; BOOL sv503_enablesoftcompat; BOOL sv503_enableforcedlogoff; BOOL sv503_timesource; BOOL sv503_acceptdownlevelapis; BOOL sv503_lmannounce; LPTSTR sv503_domain; DWORD sv503_maxcopyreadlen; DWORD sv503_maxcopywritelen; DWORD sv503_minkeepsearch; DWORD sv503_maxkeepsearch; DWORD sv503_minkeepcomplsearch; DWORD sv503_maxkeepcomplsearch; DWORD sv503_threadcountadd; DWORD sv503_numblockthreads; DWORD sv503_scavtimeout; DWORD sv503_minrcvqueue; DWORD sv503_minfreeworkitems; DWORD sv503_xactmemsize; DWORD sv503_threadpriority; DWORD sv503_maxmpxct; DWORD sv503_oplockbreakwait; DWORD sv503_oplockbreakresponsewait; BOOL sv503_enableoplocks; BOOL sv503_enableoplockforceclose; BOOL sv503_enablefcbopens; BOOL sv503_enableraw; BOOL sv503_enabledpc; BOOL sv503_enablemdlio; BOOL sv503_enablefastio;} SERVER_INFO_503, *PSERVER_INFO_503, *LPSERVER_INFO_503;

The following information levels are only valid for NetServerSetInfo_win32_NetServerSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _SERVER_INFO_1005 { DWORD sv1005_comment;} SERVER_INFO_1005, *PSERVER_INFO_1005, *LPSERVER_INFO_1005;

typedef struct _SERVER_INFO_1107 { DWORD sv1107_users;} SERVER_INFO_1107, *PSERVER_INFO_1107, *LPSERVER_INFO_1107;

typedef struct _SERVER_INFO_1010 { DWORD sv1010_disc;} SERVER_INFO_1010, *PSERVER_INFO_1010, *LPSERVER_INFO_1010;

typedef struct _SERVER_INFO_1016 { BOOL sv1016_hidden;} SERVER_INFO_1016, *PSERVER_INFO_1016, *LPSERVER_INFO_1016;

typedef struct _SERVER_INFO_1017 { DWORD sv1017_announce;} SERVER_INFO_1017, *PSERVER_INFO_1017, *LPSERVER_INFO_1017;

typedef struct _SERVER_INFO_1018 { DWORD sv1018_anndelta;} SERVER_INFO_1018, *PSERVER_INFO_1018, *LPSERVER_INFO_1018;

typedef struct _SERVER_INFO_1037 { DWORD sv1037_alertsched;} SERVER_INFO_1037, *PSERVER_INFO_1037, *LPSERVER_INFO_1037;

typedef struct _SERVER_INFO_1038 { DWORD sv1038_erroralert;} SERVER_INFO_1038, *PSERVER_INFO_1038, *LPSERVER_INFO_1038;

typedef struct _SERVER_INFO_1039 { DWORD sv1039_logonalert;} SERVER_INFO_1039, *PSERVER_INFO_1039, *LPSERVER_INFO_1039;

typedef struct _SERVER_INFO_1040 { DWORD sv1040_accessalert;} SERVER_INFO_1040, *PSERVER_INFO_1040, *LPSERVER_INFO_1040;

typedef struct _SERVER_INFO_1041 { DWORD sv1041_diskalert;} SERVER_INFO_1041, *PSERVER_INFO_1041, *LPSERVER_INFO_1041;

typedef struct _SERVER_INFO_1042 { DWORD sv1042_netioalert;} SERVER_INFO_1042, *PSERVER_INFO_1042, *LPSERVER_INFO_1042;

typedef struct _SERVER_INFO_1043 { DWORD sv1043_maxauditsz;} SERVER_INFO_1043, *PSERVER_INFO_1043, *LPSERVER_INFO_1043;

The following do not do anything on downlevel systems (for example, LAN Manager 2.x) since they cannot be modified on the fly on those systems:
typedef struct _SERVER_INFO_1009 { DWORD sv1009_users;} SERVER_INFO_1009, *PSERVER_INFO_1009, *LPSERVER_INFO_1009;

typedef struct _SERVER_INFO_1021 { LPWSTR sv1021_userpath;} SERVER_INFO_1021, *PSERVER_INFO_1021, *LPSERVER_INFO_1021;

typedef struct _SERVER_INFO_1022 { DWORD sv1022_chdevs;} SERVER_INFO_1022, *PSERVER_INFO_1022, *LPSERVER_INFO_1022;

typedef struct _SERVER_INFO_1028 { DWORD sv1028_sessopens;} SERVER_INFO_1028, *PSERVER_INFO_1028, *LPSERVER_INFO_1028;

typedef struct _SERVER_INFO_1029 { DWORD sv1029_opensearch;} SERVER_INFO_1029, *PSERVER_INFO_1029, *LPSERVER_INFO_1029;

The following are not supported on downlevel systems (for example, LAN Manager 2.x) since these are Windows NT - only fields:
typedef struct _SERVER_INFO_1001 { DWORD sv1001_maxworkitems;} SERVER_INFO_1001, *PSERVER_INFO_1001, *LPSERVER_INFO_1001;

typedef struct _SERVER_INFO_1002 { DWORD sv1002_maxrawbuflen;} SERVER_INFO_1002, *PSERVER_INFO_1002, *LPSERVER_INFO_1002;

typedef struct _SERVER_INFO_1003 { DWORD sv1003_sessusers;} SERVER_INFO_1003, *PSERVER_INFO_1003, *LPSERVER_INFO_1003;

typedef struct _SERVER_INFO_1004 { DWORD sv1004_sesscons;} SERVER_INFO_1004, *PSERVER_INFO_1004, *LPSERVER_INFO_1004;

typedef struct _SERVER_INFO_1006 { DWORD sv1006_enablesoftcompat;} SERVER_INFO_1006, *PSERVER_INFO_1006, *LPSERVER_INFO_1006;

For NetServerSetInfo, parmnum values refer to the fields in the _SERVER_INFO structures as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in server_info struct
SV_NAME_PARMNUM sv_name
SV_VERSION_MAJOR_PARMNUM sv_version_major
SV_VERSION_MINOR_PARMNUM sv_version_minor
SV_TYPE_PARMNUM sv_type
SV_COMMENT_PARMNUM sv_comment
SV_USERS_PARMNUM sv_users
SV_DISC_PARMNUM sv_disc
SV_HIDDEN_PARMNUM sv_hidden
SV_ANNOUNCE_PARMNUM sv_announce
SV_ANNDELTA_PARMNUM sv_anndelta
SV_USERPATH_PARMNUM sv_userpath
SV_ULIST_MTIME_PARMNUM sv_ulist_mtime
SV_GLIST_MTIME_PARMNUM sv_glist_mtime
SV_ALIST_MTIME_PARMNUM sv_alist_mtime
SV_ALERTS_PARMNUM sv_alerts
SV_SECURITY_PARMNUM sv_security
SV_NUMADMIN_PARMNUM sv_numadmin
SV_LANMASK_PARMNUM sv_lanmask
SV_GUESTACC_PARMNUM sv_guestacc
SV_CHDEVS_PARMNUM sv_chdevs
SV_CHDEVQ_PARMNUM sv_chdevq
SV_CHDEVJOBS_PARMNUM sv_chdevjobs
SV_CONNECTIONS_PARMNUM sv_connections
SV_SHARES_PARMNUM sv_shares
SV_OPENFILES_PARMNUM sv_openfiles
SV_SESSOPENS_PARMNUM sv_sessopens
SV_SESSVCS_PARMNUM sv_sessvcs
SV_SESSREQS_PARMNUM sv_sessreqs
SV_OPENSEARCH_PARMNUM sv_opensearch
SV_ACTIVELOCKS_PARMNUM sv_activelocks
SV_NUMREQBUF_PARMNUM sv_numreqbuf
SV_SIZREQBUF_PARMNUM sv_sizreqbuf
SV_NUMBIGBUF_PARMNUM sv_numbigbuf
SV_NUMFILETASKS_PARMNUM sv_numfiletasks
SV_ALERTSCHED_PARMNUM sv_alertsched
SV_ERRORALERT_PARMNUM sv_erroralert
SV_LOGONALERT_PARMNUM sv_logonalert
SV_ACCESSALERT_PARMNUM sv_accessalert
SV_DISKALERT_PARMNUM sv_diskalert
SV_NETIOALERT_PARMNUM sv_netioalert
SV_MAXAUDITSZ_PARMNUM sv_maxauditsz
SV_SRVHEURISTICS_PARMNUM sv_srvheuristics
SV_AUDITEDEVENTS_PARMNUM sv_auditedevents
SV_AUTOPROFILE_PARMNUM sv_autoprofile
SV_MAXWORKITEMS_PARMNUM sv_maxworkitems
SV_RAWWORKITEMS_PARMNUM sv_rawworkitems
SV_IRPSTACKSIZE_PARMNUM sv_irpstacksize
SV_SESSUSERS_PARMNUM sv_sessusers
SV_SESSCONNS_PARMNUM sv_sessconns
SV_MAXNONPAGEDMEMORYUSAGE_PARMNUM sv_maxnonpagedmemoryusage
SV_MAXPAGEDMEMORYUSAGE_PARMNUM sv_maxpagedmmeoryusage
SV_ENABLEOFTCOMPAT_PARMNUM sv_enablesoftcompat

The server functions are:
NetServerGetInfo_win32_NetServerGetInfo
NetServerSetInfo_win32_NetServerSetInfo.
+$#A Workstation and Workstation User Functions
Workstation functions perform administrative tasks on a local or remote workstation. Any user or application with admin group membership on a local or remote server can perform administrative tasks on that workstation to control its operation, user access, and resource sharing. The low-level parameters that affect a workstation's operation can be examined and modified by calling NetWkstaGetInfo_win32_NetWkstaGetInfo and NetWkstaSetInfo_win32_NetWkstaSetInfo.
The workstation API data structures are restructured from those of LAN Man 2.x to allow the information to be grouped by type and security accesses. The LAN Manager 2.x workstation information format is discontinued due to the following problems:
· The base level (0 and 1) were not grouped by accessibility such that a non superset level (level 10) was required to allow guest access to the information.
· Platform specific implementation information was included in the base levels such that every platform had to return all information including a default for non-relevant fields. This grew the size of the information structures unneccessarily, making the function cumbersome to use.

The workstation functions allow access to two discrete groups of workstation information:
· System information.
· Platform specific information (Windows NT, OS/2, MS-DOS, and so on)

The Workstation User functions allow access to user-specific information. The user-specific information is separated from the workstation information because there can be more than one user on a workstation.
Within each group the fields are categorized by security access such that the guest accessible fields are a subset of the user accessible fields that are a subset of the admin accessible fields.
The system information structure contains a platform base number that identifies the levels and format of the platform specific information structures.
The workstation function information structures are:
System Information -
Level 100 available with guest access -
typedef struct _WKSTA_INFO_100 { DWORD wki100_platform_id; LPWSTR wki100_computername; LPWSTR wki100_langroup; DWORD wki100_ver_major; DWORD wki100_ver_minor;} WKSTA_INFO_100, *PWKSTA_INFO_100, *LPWKSTA_INFO_100;

Level 101 available with User access:
typedef struct _WKSTA_INFO_101 { DWORD wki101_platform_id; LPWSTR wki101_computername; LPWSTR wki101_langroup; DWORD wki101_ver_major; DWORD wki101_ver_minor; LPWSTR wki101_lanroot;} WKSTA_INFO_101, *PWKSTA_INFO_101, *LPWKSTA_INFO_101;

Level 102 available with Admin access:
typedef struct _WKSTA_INFO_102 { DWORD wki102_platform_id; LPWSTR wki102_computername; LPWSTR wki102_langroup; DWORD wki102_ver_major; DWORD wki102_ver_minor; LPWSTR wki102_lanroot; DWORD wki102_logged_on_users;} WKSTA_INFO_102, *PWKSTA_INFO_102, *LPWKSTA_INFO_102;

Platform Specific Information
Level 300-399 MS-DOS Workstation information.
Level 300 available with guest access:
Level 302 available with Admin access:
typedef struct _WKSTA_INFO_302 { DWORD wki302_char_wait; DWORD wki302_collection_time; DWORD wki302_maximum_collection_count; DWORD wki302_keep_conn; DWORD wki302_keep_search; DWORD wki302_max_cmds; DWORD wki302_num_work_buf; DWORD wki302_siz_work_buf; DWORD wki302_max_wrk_cache; DWORD wki302_sess_timeout; DWORD wki302_siz_error; DWORD wki302_num_alerts; DWORD wki302_num_services; DWORD wki302_errlog_sz; DWORD wki302_print_buf_time; DWORD wki302_num_char_buf; DWORD wki302_siz_char_buf; LPWSTR wki302_wrk_heuristics; DWORD wki302_mailslots; DWORD wki302_num_dgram_buf;} WKSTA_INFO_302, *PWKSTA_INFO_302, *LPWKSTA_INFO_302;

Level 400-499 OS/2 Workstation information.
Level 402 available with Admin access:
typedef struct _WKSTA_INFO_402 { DWORD wki402_char_wait; DWORD wki402_collection_time; DWORD wki402_maximum_collection_count; DWORD wki402_keep_conn; DWORD wki402_keep_search; DWORD wki402_max_cmds; DWORD wki402_num_work_buf; DWORD wki402_siz_work_buf; DWORD wki402_max_wrk_cache; DWORD wki402_sess_timeout; DWORD wki402_siz_error; DWORD wki402_num_alerts; DWORD wki402_num_services; DWORD wki402_errlog_sz; DWORD wki402_print_buf_time; DWORD wki402_num_char_buf; DWORD wki402_siz_char_buf; LPWSTR wki402_wrk_heuristics; DWORD wki402_mailslots; DWORD wki402_num_dgram_buf; DWORD wki402_max_threads;} WKSTA_INFO_402, *PWKSTA_INFO_402, *LPWKSTA_INFO_402;

Level 500-599 Windows NT Workstation information.
Level 502 available with Admin access:
typedef struct _WKSTA_INFO_502 { DWORD wki502_char_wait; DWORD wki502_collection_time; DWORD wki502_maximum_collection_count; DWORD wki502_keep_conn; DWORD wki502_max_cmds; DWORD wki502_sess_timeout; DWORD wki502_siz_char_buf; DWORD wki502_max_threads; DWORD wki502_lock_quota; DWORD wki502_lock_increment; DWORD wki502_lock_maximum; DWORD wki502_pipe_increment; DWORD wki502_pipe_maximum; DWORD wki502_cache_file_timeout; DWORD wki502_dormant_file_limit; DWORD wki502_read_ahead_throughput; DWORD wki502_num_mailslot_buffers; DWORD wki502_num_srv_announce_buffers; DWORD wki502_dgreceiver_threads; BOOL wki502_use_opportunistic_locking; BOOL wki502_use_unlock_behind; BOOL wki502_use_close_behind; BOOL wki502_buf_named_pipes; BOOL wki502_use_lock_read_unlock; BOOL wki502_utilize_nt_caching; BOOL wki502_use_raw_read; BOOL wki502_use_raw_write; BOOL wki502_use_write_raw_data; BOOL wki502_use_encryption; BOOL wki502_buf_files_deny_write; BOOL wki502_buf_read_only_files; BOOL wki502_force_core_create_mode; BOOL wki502_use_512_byte_max_transfer;} WKSTA_INFO_502, *PWKSTA_INFO_502, *LPWKSTA_INFO_502;

This format of workstation information allows for a more logical future extension. When new information is to be added to any set of guest,user and admin information a new set of levels is created to include the current infromation plus the new information.
The following information levels are only valid for NetWkstaSetInfo_win32_NetWkstaSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _WKSTA_INFO_1010 { DWORD wki1010_char_wait;} WKSTA_INFO_1010, *PWKSTA_INFO_1010, *LPWKSTA_INFO_1010;

typedef struct _WKSTA_INFO_1011 { DWORD wki1011_collection_time;} WKSTA_INFO_1011, *PWKSTA_INFO_1011, *LPWKSTA_INFO_1011;

typedef struct _WKSTA_INFO_1012 { DWORD wki1012_maximum_collection_count;} WKSTA_INFO_1012, *PWKSTA_INFO_1012, *LPWKSTA_INFO_1012;

typedef struct _WKSTA_INFO_1027 { DWORD wki1027_errlog_sz;} WKSTA_INFO_1027, *PWKSTA_INFO_1027, *LPWKSTA_INFO_1027;

typedef struct _WKSTA_INFO_1028 { DWORD wki1028_print_buf_time;} WKSTA_INFO_1028, *PWKSTA_INFO_1028, *LPWKSTA_INFO_1028;

typedef struct _WKSTA_INFO_1032 { DWORD wki1032_wrk_heuristics;} WKSTA_INFO_1032, *PWKSTA_INFO_1032, *LPWKSTA_INFO_1032;

typedef struct _WKSTA_INFO_1035 { LPWSTR wki1035_oth_domains;} WKSTA_INFO_1035, *PWKSTA_INFO_1035, *LPWKSTA_INFO_1035;

The following do nothing on downlevel systems (for example, LAN Manager 2.x) since these fields cannot be set on them:
typedef struct _WKSTA_INFO_1013 { DWORD wki1013_keep_conn;} WKSTA_INFO_1013, *PWKSTA_INFO_1013, *LPWKSTA_INFO_1013;

typedef struct _WKSTA_INFO_1018 { DWORD wki1018_sess_timeout;} WKSTA_INFO_1018, *PWKSTA_INFO_1018, *LPWKSTA_INFO_1018;

typedef struct _WKSTA_INFO_1023 { DWORD wki1023_siz_char_buf;} WKSTA_INFO_1023, *PWKSTA_INFO_1023, *LPWKSTA_INFO_1023;

typedef struct _WKSTA_INFO_1033 { DWORD wki1033_max_threads;} WKSTA_INFO_1033, *PWKSTA_INFO_1033, *LPWKSTA_INFO_1033;

The following are not supported on downlevel systems (for example, LAN Manager 2.x):
typedef struct _WKSTA_INFO_1041 { DWORD wki1041_lock_quota;} WKSTA_INFO_1041, *PWKSTA_INFO_1041, *LPWKSTA_INFO_1041;

typedef struct _WKSTA_INFO_1042 { DWORD wki1042_lock_increment;} WKSTA_INFO_1042, *PWKSTA_INFO_1042, *LPWKSTA_INFO_1042;

typedef struct _WKSTA_INFO_1043 { DWORD wki1043_lock_maximum;} WKSTA_INFO_1043, *PWKSTA_INFO_1043, *LPWKSTA_INFO_1043;

typedef struct _WKSTA_INFO_1044 { DWORD wki1044_pipe_increment;} WKSTA_INFO_1044, *PWKSTA_INFO_1044, *LPWKSTA_INFO_1044;

typedef struct _WKSTA_INFO_1045 { DWORD wki1045_pipe_maximum;} WKSTA_INFO_1045, *PWKSTA_INFO_1045, *LPWKSTA_INFO_1045;

typedef struct _WKSTA_INFO_1046 { DWORD wki1046_dormant_file_limit;} WKSTA_INFO_1046, *PWKSTA_INFO_1046, *LPWKSTA_INFO_1046;

typedef struct _WKSTA_INFO_1047 { DWORD wki1047_cache_file_timeout;} WKSTA_INFO_1047, *PWKSTA_INFO_1047, *LPWKSTA_INFO_1047;

typedef struct _WKSTA_INFO_1048 { BOOL wki1048_use_opportunity_locking;} WKSTA_INFO_1048, *PWKSTA_INFO_1048, *LPWKSTA_INFO_1048;

typedef struct _WKSTA_INFO_1049 { BOOL wki1049_use_unlock_behind;} WKSTA_INFO_1049, *PWKSTA_INFO_1049, *LPWKSTA_INFO_1049;

typedef struct _WKSTA_INFO_1050 { BOOL wki1050_use_close_behind;} WKSTA_INFO_1050, *PWKSTA_INFO_1050, *LPWKSTA_INFO_1050;

typedef struct _WKSTA_INFO_1051 { BOOL wki1051_buf_named_pipes;} WKSTA_INFO_1051, *PWKSTA_INFO_1051, *LPWKSTA_INFO_1051;

typedef struct _WKSTA_INFO_1052 { BOOL wki1052_use_lock_read_unlock;} WKSTA_INFO_1052, *PWKSTA_INFO_1052, *LPWKSTA_INFO_1052;

typedef struct _WKSTA_INFO_1053 { BOOL wki1053_utilize_nt_caching;} WKSTA_INFO_1053, *PWKSTA_INFO_1053, *LPWKSTA_INFO_1053;

typedef struct _WKSTA_INFO_1054 { BOOL wki1054_use_raw_read;} WKSTA_INFO_1054, *PWKSTA_INFO_1054, *LPWKSTA_INFO_1054;

typedef struct _WKSTA_INFO_1055 { BOOL wki1055_use_raw_write;} WKSTA_INFO_1055, *PWKSTA_INFO_1055, *LPWKSTA_INFO_1055;

typedef struct _WKSTA_INFO_1056 { BOOL wki1056_use_write_raw_data;} WKSTA_INFO_1056, *PWKSTA_INFO_1056, *LPWKSTA_INFO_1056;

typedef struct _WKSTA_INFO_1057 { BOOL wki1057_use_encryption;} WKSTA_INFO_1057, *PWKSTA_INFO_1057, *LPWKSTA_INFO_1057;

typedef struct _WKSTA_INFO_1058 { BOOL wki1058_buf_files_deny_write;} WKSTA_INFO_1058, *PWKSTA_INFO_1058, *LPWKSTA_INFO_1058;

typedef struct _WKSTA_INFO_1059 { BOOL wki1059_buf_read_only_files;} WKSTA_INFO_1059, *PWKSTA_INFO_1059, *LPWKSTA_INFO_1059;

typedef struct _WKSTA_INFO_1060 { BOOL wki1060_force_core_create_mode;} WKSTA_INFO_1060, *PWKSTA_INFO_1060, *LPWKSTA_INFO_1060;

typedef struct _WKSTA_INFO_1061 { BOOL wki1061_use_512_byte_max_transfer;} WKSTA_INFO_1061, *PWKSTA_INFO_1061, *LPWKSTA_INFO_1061;

typedef struct _WKSTA_INFO_1062 { DWORD wki1062_read_ahead_throughput;} WKSTA_INFO_1062, *PWKSTA_INFO_1062, *LPWKSTA_INFO_1062;

The Workstation User function information structures are:
Level 0 and 1 available with User access:
typedef struct _WKSTA_USER_INFO_0 { LPWSTR wkui0_username;} WKSTA_USER_INFO_0, *PWKSTA_USER_INFO_0, *LPWKSTA_USER_INFO_0;

typedef struct _WKSTA_USER_INFO_1 { LPWSTR wkui1_username; LPWSTR wkui1_logon_domain; LPWSTR wkui1_logon_server;
LPWSTR wkui1_oth_domains;} WKSTA_USER_INFO_1, *PWKSTA_USER_INFO_1, *LPWKSTA_USER_INFO_1;

typedef struct _WKSTA_USER_INFO_1101 { LPTSTR wkui1101_oth_domains;} WKSTA_USER_INFO_1101, *PWKSTA_USER_INFO_1101, *LPWKSTA_USER_INFO_1101;

For NetWkstaSetInfo, parmnum values refer to the fields in the workstation info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in wksta_info struct
WKSTA_PLATFORM_ID_PARMNUM wki_platform_id
WKSTA_COMPUTERNAME_PARMNUM wki_computername
WKSTA_LANGROUP_PARMNUM wki_langroup
WKSTA_OTH_DOMAINS_PARMNUM wki_oth_domains
WKSTA_VER_MAJOR_PARMNUM wki_ver_major
WKSTA_VER_MINOR_PARMNUM wki_ver_minor
WKSTA_LOGGED_ON_USERS_PARMNUM wki_logged_on_users
WKSTA_LANROOT_PARMNUM wki_lanroot
WKSTA_LOGON_DOMAIN_PARMNUM wki_logon_domain
WKSTA_LOGON_SERVER_PARMNUM wki_logon_server
WKSTA_CHARWAIT_PARMNUM wki_char_wait
WKSTA_CHARTIME_PARMNUM wki_collection_time
WKSTA_CHARCOUNT_PARMNUM wki_maximum_collection_count
WKSTA_KEEPCONN_PARMNUM wki_keep_conn
WKSTA_KEEPSEARCH_PARMNUM wki_keep_search
WKSTA_MAXCMDS_PARMNUM wki_max_cmds
WKSTA_NUMWORKBUF_PARMNUM wki_num_work_buf
WKSTA_MAXWRKCACHE_PARMNUM wki_max_wrk_cache
WKSTA_SESSTIMEOUT_PARMNUM wki_sess_timeout
WKSTA_SIZERROR_PARMNUM wki_siz_error
WKSTA_NUMALERTS_PRAMNUM wki_num_alerts
WKSTA_NUMSERVICES_PARMNUM wki_num_services
WKSTA_ERRLOGSZ_PARMNUM wki_errlog_sz
WKSTA_PRINTBUFTIME_PARMNUM wki_print_buf_time
WKSTA_NUMCHARBUF_PARMNUM wki_num_char_buf
WKSTA_SIZCHARBUF_PARMNUM wki_siz_char_buf
WKSTA_WRKHEURISTICS_PARMNUM wki_wrk_heuristics
WKSTA_MAILSLOTS_PRAMNUM wki_mailslots
WKSTA_MAXTHREADS_PARMNUM wki_max_threads
WKSTA_SIZWORKBUF_PARMNUM wki_siz_work_buf
WKSTA_DORMANTTIMEOUT_PARMNUM wki_dormant_timeout
WKSTA_LOCKQUOTA_PARMNUM wki_lock_quota
WKSTA_LOCKINCREMENT_PARMNUM wki_lock_increment
WKSTA_LOCKMAXIMUM_PARMNUM wki_lock_maximum
WKSTA_PIPEINCREMENT_PRAMNUM wki_pipe_increment
WKSTA_PIPEMAXIMUM_PARMNUM wki_pipe_maximum
WKSTA_RAWREADTHRESHOLD_PARMNUM wki_raw_read_threshold
WKSTA_USEOPLOCKING_PARMNUM wki_use_opportunistic_locking
WKSTA_USEOPBATCH_PARMNUM wki_use_op_batch
WKSTA_USEUNLOCKBEHIND_PARMNUM wki_use_unlock_behind
WKSTA_USECLOSEBEHIND_PARMNUM wki_use_close_behind
WKSTA_BUFNAMEDPIPES_PARMNUM wksta_buf_named_pipes
WKSTA_USELOCKANDREADANDUNLOCK _PARMNUM wki_use_lock_and_read_and _unlock
WKSTA_UTILIZENTCACHING_PARMNUM wki_utilize_nt_caching
WKSTA_USERAWREAD_PARMNUM wki_use_raw_read
WKSTA_USEWRITERAWWITHDATA_PARMNUM wki_use_write_raw_with_data
WKSTA_USEENCRYPTION_PARMNUM wki_use_encryption
WKSTA_BUFFILESWITHDENYWRITE _PARMNUM wki_buf_files_with_deny_write
WKSTA_BUFREADONLYFILES_PRAMNUM wki_buf_read_only_files
WKSTA_FORCECORECREATEMODE_PARMNUM wki_force_core_create_mode
WKSTA_USE512BYTESMAXTRANSFER _PARMNUM wki_use_512_bytes_max_transfer

The workstation functions are:
NetWkstaGetInfo_win32_NetWkstaGetInfo
NetWkstaSetInfo_win32_NetWkstaSetInfo
The Workstation User functions are:
NetWkstaUserEnum_win32_NetWkstaUserEnum
NetWkstaUserGetInfo_win32_NetWkstaUserGetInfo
NetWkstaUserSetInfo_win32_NetWkstaUserSetInfo

+$#A Use Functions

Note The WNetAddConnection2_win32_WNetAddConnection2 and WNetCancelConnection2_win32_WNetCancelConnection2 functions should be used instead of the NetUse functions.

Use functions examine or control connections (uses) between workstations and servers. Connections are distinguished from sessions: a session is established the first time a workstation makes a connection to a shared resource on the server; all further connections between the workstation and the server are part of this same session until the session ends. Two types of connections can be made: device-name connections (which can only be explicit) and universal-naming convention (UNC) connections (which can be explicit or implicit).
Connections are made on a per-user basis. A connection made by a user is deleted when that user logs off. For this reason the NetUse functions are local only, since a connection set up by a remote user would not be accessible to any other users, even the user that was interactively logged onto that computer.
NetUseAdd_win32_NetUseAdd creates a device-name connection or an explicit UNC connection. Implicit UNC connections are made by the function responsible for the connection.
NetUseAdd establishes an explicit connection between the local computer and a resource shared on a server by redirecting a local device name to the share name of a remote server resource (\\servername\sharename). Once a device-name connection is made, users or applications can use the remote resource by specifying the local device name. To establish an implicit UNC connection, an application passes the share name of a resource to any function that accepts UNC paths. The function accepts the UNC name and makes a connection to the specified share name. All further requests on this connection require the full share name.
NetUseDel_win32_NetUseDel ends a connection to a shared resource. NetUseEnum_win32_NetUseEnum enumerates all current connections between the local computer and resources on remote servers. NetUseGetInfo_win32_NetUseGetInfo returns information about a connection to a shared resource.
The use functions are available at three information levels.
typedef struct _USE_INFO_0 { LPWSTR ui0_local; LPWSTR ui0_remote;} USE_INFO_0, *PUSE_INFO_0, *LPUSE_INFO_0;

typedef struct _USE_INFO_1 { LPWSTR ui1_local; LPWSTR ui1_remote; LPWSTR ui1_password; DWORD ui1_status; DWORD ui1_asg_type; DWORD ui1_refcount; DWORD ui1_usecount;} USE_INFO_1, *PUSE_INFO_1, *LPUSE_INFO_1;

Information level 2 is not available if the function is remoted to a downlevel system ¾ ERROR_NOT_SUPPORTED will be returned in that case.
typedef struct _USE_INFO_2 { LPWSTR ui2_local; LPWSTR ui2_remote; LPWSTR ui2_password; DWORD ui2_status; DWORD ui2_asg_type; DWORD ui2_refcount; DWORD ui2_usecount; LPWSTR ui2_username;
LPWSTR ui2_domainname;} USE_INFO_2, *PUSE_INFO_2, *LPUSE_INFO_2;

The use functions are:
NetUseAdd_win32_NetUseAdd
NetUseDel_win32_NetUseDel
NetUseEnum_win32_NetUseEnum
NetUseGetInfo_win32_NetUseGetInfo
+$#A Share Functions
Share functions control shared resources. A shared resource is a local resource on a server (for example, a disk directory, print device, or named pipe) that can be accessed by users and applications on the network.
NetShareAdd_win32_NetShareAdd allows a user or application to share a resource of a specific type using the specified share name. NetShareAdd requires the share name and local device name to share the resource. A user or application must have an account on the server to access the resource.
You may also specify a Security Descriptor to be associated with a share, which specifies which users will be allowed to access files through this share, and with what type of access.
LAN Manager defines three types of special share names for interprocess communication (IPC) and remote administration of the server:
· IPC$, reserved for interprocess communication.
· ADMIN$, reserved for remote administration.
· A$, B$, C$ (and other local disk names followed by a dollar sign), assigned to local disk devices.

Share functions are available at four information levels.
typedef struct _SHARE_INFO_0 { LPWSTR shi0_netname;} SHARE_INFO_0, *PSHARE_INFO_0, *LPSHARE_INFO_0;

typedef struct _SHARE_INFO_1 { LPWSTR shi1_netname; DWORD shi1_type; LPWSTR shi1_remark;} SHARE_INFO_1, *PSHARE_INFO_1, *LPSHARE_INFO_1;

typedef struct _SHARE_INFO_2 { LPWSTR shi2_netname; DWORD shi2_type; LPWSTR shi2_remark; DWORD shi2_permissions; DWORD shi2_max_uses; DWORD shi2_current_uses; LPWSTR shi2_path; LPWSTR shi2_passwd;} SHARE_INFO_2, *PSHARE_INFO_2, *LPSHARE_INFO_2;

typedef struct _SHARE_INFO_502 { LPTSTR shi502_netname; DWORD shi502_type; LPTSTR shi502_remark; DWORD shi502_permissions; DWORD shi502_max_uses; DWORD shi502_current_uses; LPTSTR shi502_path; LPTSTR shi502_passwd; DWORD shi502_reserved; PSECURITY_DESCRIPTOR shi502_security_descriptor;} SHARE_INFO_502, *PSHARE_INFO_502, *LPSHARE_INFO_502;

The following information levels are valid only for NetShareSetInfo_win32_NetShareSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _SHARE_INFO_1004 { LPWSTR shi1004_remark;} SHARE_INFO_1004, *PSHARE_INFO_1004, *LPSHARE_INFO_1004;

typedef struct _SHARE_INFO_1006 { DWORD shi1006_max_uses;} SHARE_INFO_1006, *PSHARE_INFO_1006, *LPSHARE_INFO_1006;

The following is only supported on Windows NT.
typedef struct _SHARE_INFO_1501 { DWORD shi1501_reserved; PSECURITY_DESCRIPTOR shi1501_security_descriptor;} SHARE_INFO_1501, *PSHARE_INFO_1501, *LPSHARE_INFO_1501;

For NetShareSetInfo_win32_NetShareSetInfo, parmnum values refer to the fields in the share_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in share_info struct
SHARE_NETNAME_PARMNUM shi_netname
SHARE_TYPE_PARMNUM shi_type
SHARE_REMARK_PARMNUM shi_remark
SHARE_PERMISSIONS_PARMNUM shi_permissions
SHARE_MAX_USES_PARMNUM shi_max_uses
SHARE_CURRENT_USES_PARMNUM shi_current_uses
SHARE_PATH_PARMNUM shi_path
SHARE_PASSWD_PARMNUM shi_passwd
SHARE_FILE_SD_PARMNUM shi_security_descriptor

The share functions are:
NetShareAdd_win32_NetShareAdd
NetShareCheck_win32_NetShareCheck
NetShareDel_win32_NetShareDel
NetShareEnum_win32_NetShareEnum
NetShareGetInfo_win32_NetShareGetInfo
NetShareSetInfo_win32_NetShareSetInfo
+$#A Session Functions
Session functions control network sessions established between workstations and servers. They require that the Server service be started on the specified server. A session is a link between a workstation and a server. It is established the first time a workstation makes a connection with a shared resource on the server. Until the session ends, all further connections between the workstation and the server are part of this same session. To end a session, an application on the server end of a connection calls NetSessionDel_win32_NetSessionDel. This deletes all current connections between the workstation and the server. NetSessionEnum_win32_NetSessionEnum returns information about all sessions established for a server. NetSessionGetInfo_win32_NetSessionGetInfo returns information about a particular session.
Per-user information is managed by the NetSession functions through the use of the username parameter. Since there can be multiple users per session, this parameter is necessary in order to access the user-specific information for the session.
Session functions are available at four information levels.
typedef struct _SESSION_INFO_0 { LPWSTR sesi0_cname;} SESSION_INFO_0, *PSESSION_INFO_0, *LPSESSION_INFO_0;

typedef struct _SESSION_INFO_1 { LPWSTR sesi1_cname; LPWSTR sesi1_username; DWORD sesi1_num_opens; DWORD sesi1_time; DWORD sesi1_idle_time; DWORD sesi1__flags;} SESSION_INFO_1, *PSESSION_INFO_1, *LPSESSION_INFO_1;

typedef struct _SESSION_INFO_2 { LPWSTR sesi2_cname; LPWSTR sesi2_username; DWORD sesi2_num_opens; DWORD sesi2_time; DWORD sesi2_idle_time; DWORD sesi2_flags; LPWSTR sesi2_cltype_name;} SESSION_INFO_2, *PSESSION_INFO_2, *LPSESSION_INFO_2;

typedef struct _SESSION_INFO_10 { LPWSTR sesi10_cname; LPWSTR sesi10_username; DWORD sesi10_time; DWORD sesi10_idle_time;} SESSION_INFO_10, *PSESSION_INFO_10, *LPSESSION_INFO_10;

sesi_username cannot be NULL. sesi1_flags and sesi2_flags can take the following bits:
SESS_GUEST 0x00000001SESS_NONENCRYPTION 0x00000002

The session functions are:
NetSessionDel_win32_NetSessionDel
NetSessionEnum_win32_NetSessionEnum
NetSessionGetInfo_win32_NetSessionGetInfo
+$#A File Functions
File functions provide a way to monitor and close the file, device, and pipe resources open on a server. NetFileClose_win32_NetFileClose forces a server resource closed. This function can be used when a system error prevents normal closure. NetFileEnum_win32_NetFileEnum returns information about resources open on a server. A file can be opened one or more times by one or more applications. Each file opening is uniquely identified. NetFileEnum returns an entry for each file opening. NetFileGetInfo_win32_NetFileGetInfo returns information about one particular opening of a resource.
File functions are available at information levels 2 and 3 only. Levels 0 and 1 are not supported.
typedef struct _FILE_INFO_2 { DWORD fi2_id;} FILE_INFO_2, *PFILE_INFO_2, *LPFILE_INFO_2;

typedef struct _FILE_INFO_3 { DWORD fi3_id; DWORD fi3_permissions; DWORD fi3_num_locks; LPWSTR fi3_pathname; LPWSTR fi3_username;} FILE_INFO_3, *PFILE_INFO_3, *LPFILE_INFO_3;

The file functions are:
NetFileClose_win32_NetFileClose
NetFileEnum_win32_NetFileEnum
NetFileGetInfo_win32_NetFileGetInfo
+$#A Message Functions
Message functions send and receive messages and manipulate message aliases. A message is a buffer of text data sent to a user or application on the network. To receive a message, a user or application must register a message alias in a computer's table of message names. This can be done by using NetMessageNameAdd_win32_NetMessageNameAdd. A message name table contains a list of registered message aliases (users and applications) permitted to receive messages.
The aliases registered in the message name table are case insensitive. NetMessageNameDel_win32_NetMessageNameDel deletes a specific message alias from the message name table. NetMessageNameEnum_win32_NetMessageNameEnum lists all the aliases stored in the message name table. NetMessageNameGetInfo_win32_NetMessageNameGetInfo retrieves information about a particular message alias in the message name table.
To send a message, an application calls NetMessageBufferSend_win32_NetMessageBufferSend. Applications can also send broadcast messages to all users in a domain or to all computers on a network using NetMessageBufferSend.
Message functions are available at two information levels.
MSG_INFO_1 exists only for compatability. The Windows NT messenger will not forward names or allow names to be forwarded to it.
typedef struct _MSG_INFO_0 { LPWSTR msgi0_name;} MSG_INFO_0, *PMSG_INFO_0, *LPMSG_INFO_0;

typedef struct _MSG_INFO_1 { LPWSTR msgi1_name; DWORD msgi1_forward_flag; LPWSTR msgi1_forward;} MSG_INFO_1, *PMSG_INFO_1, *LPMSG_INFO_1;

The message functions are:
NetMessageBufferSend_win32_NetMessageBufferSend
NetMessageNameAdd_win32_NetMessageNameAdd
NetMessageNameDel_win32_NetMessageNameDel
NetMessageNameEnum_win32_NetMessageNameEnum
NetMessageNameGetInfo_win32_NetMessageNameGetInfo
+$#A Remote Utility Function
The remote utility function enables applications to access the time-of-day information on a remote server.
NetRemoteTOD_win32_NetRemoteTOD returns time-of-day information from a remote server.
+$#A Security Account Functions
User Functions
User functions control a user's account in the security database. Each user or application that accesses resources must have an account in the security database. The Windows NT Security system (SAM) uses this user account to verify that the user or application has permission to use a resource. When a user or an application requests access to a resource, the security system checks for an appropriate user account or group account to permit the access.
NetUserEnum_win32_NetUserEnum can be used to list all user accounts in a domain. An application can verify the groups to which a user belongs by calling NetUserGetGroups_win32_NetUserGetGroups, which returns a list of global group names. The NetUserGetLocalGroups_win32_NetUserGetLocalGroups function does the same for local groups. When a user account is no longer needed, use NetUserDel_win32_NetUserDel to delete the account from the server. Once the account is removed, the user can no longer access the server, except by using the guest account. Because the user's password is confidential, it is not returned by NetUserEnum or NetUserGetInfo_win32_NetUserGetInfo. The password is initially assigned when NetUserAdd_win32_NetUserAdd is called. NetUserSetInfo_win32_NetUserSetInfo sets the password and other elements of a user account.
User account information is available at five levels:
typedef struct _USER_INFO_0 { LPWSTR usri0_name;} USER_INFO_0, *PUSER_INFO_0, *LPUSER_INFO_0;

typedef struct _USER_INFO_1 { LPWSTR usri1_name; LPWSTR usri1_password; DWORD usri1_password_age; DWORD usri1_priv; LPWSTR usri1_home_dir; LPWSTR usri1_comment; DWORD usri1_flags; LPWSTR usri1_script_path;} USER_INFO_1, *PUSER_INFO_1, *LPUSER_INFO_1;

typedef struct _USER_INFO_2 { LPWSTR usri2_name; LPWSTR usri2_password; DWORD usri2_password_age; DWORD usri2_priv; LPWSTR usri2_home_dir; LPWSTR usri2_comment; DWORD usri2_flags; LPWSTR usri2_script_path; DWORD usri2_auth_flags; LPWSTR usri2_full_name; LPWSTR usri2_usr_comment; LPWSTR usri2_parms; LPWSTR usri2_workstations; DWORD usri2_last_logon; DWORD usri2_last_logoff; DWORD usri2_acct_expires; DWORD usri2_max_storage; DWORD usri2_units_per_week; LPBYTE usri2_logon_hours; DWORD usri2_bad_pw_count; DWORD usri2_num_logons; LPWSTR usri2_logon_server; DWORD usri2_country_code; DWORD usri2_code_page;} USER_INFO_2, *PUSER_INFO_2, *LPUSER_INFO_2;

typedef struct _USER_INFO_3 { LPWSTR usri3_name; LPWSTR usri3_password; DWORD usri3_password_age; DWORD usri3_priv; LPWSTR usri3_home_dir; LPWSTR usri3_comment; DWORD usri3_flags; LPWSTR usri3_script_path; DWORD usri3_auth_flags; LPWSTR usri3_full_name; LPWSTR usri3_usr_comment; LPWSTR usri3_parms; LPWSTR usri3_workstations; DWORD usri3_last_logon; DWORD usri3_last_logoff; DWORD usri3_acct_expires; DWORD usri3_max_storage; DWORD usri3_units_per_week; PBYTE usri3_logon_hours; DWORD usri3_bad_pw_count; DWORD usri3_num_logons; LPWSTR usri3_logon_server; DWORD usri3_country_code; DWORD usri3_code_page; DWORD usri3_user_id; DWORD usri3_primary_group_id; LPWSTR usri3_profile; LPWSTR usri3_home_dir_drive; DWORD usri3_password_expired;}USER_INFO_3, *PUSER_INFO_3, *LPUSER_INFO_3;

typedef struct _USER_INFO_10 { LPWSTR usri10_name; LPWSTR usri10_comment; LPWSTR usri10_usr_comment; LPWSTR usri10_full_name;} USER_INFO_10, *PUSER_INFO_10, *LPUSER_INFO_10;

typedef struct _USER_INFO_11 { LPWSTR usri11_name; LPWSTR usri11_comment; LPWSTR usri11_usr_comment; LPWSTR usri11_full_name; DWORD usri11_priv; DWORD usri11_auth_flags; DWORD usri11_password_age; LPWSTR usri11_home_dir; LPWSTR usri11_parms; DWORD usri11_last_logon; DWORD usri11_last_logoff; DWORD usri11_bad_pw_count; DWORD usri11_num_logons; LPWSTR usri11_logon_server; DWORD usri11_country_code; LPWSTR usri11_workstations; DWORD usri11_max_storage; DWORD usri11_units_per_week; LPBYTE usri11_logon_hours; DWORD usri11_code_page;} USER_INFO_11, *PUSER_INFO_11, *LPUSER_INFO_11;

typedef struct _USER_INFO_20 { LPWSTR usri20_name; LPWSTR usri20_full_name; LPWSTR usri20_comment; DWORD usri20_flags; DWORD usri20_user_id;}USER_INFO_20, *PUSER_INFO_20, *LPUSER_INFO_20;

The following information levels are valid only for NetUserSetInfo_win32_NetUserSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _USER_INFO_1003 { LPWSTR usri1003_password;} USER_INFO_1003, *PUSER_INFO_1003, *LPUSER_INFO_1003;

typedef struct _USER_INFO_1005 { DWORD usri1005_priv;} USER_INFO_1005, *PUSER_INFO_1005, *LPUSER_INFO_1005;

typedef struct _USER_INFO_1006 { LPWSTR usri1006_home_dir;} USER_INFO_1006, *PUSER_INFO_1006, *LPUSER_INFO_1006;

typedef struct _USER_INFO_1007 { LPWSTR usri1007_comment;} USER_INFO_1007, *PUSER_INFO_1007, *LPUSER_INFO_1007;

typedef struct _USER_INFO_1008 { DWORD usri1008_flags;} USER_INFO_1008, *PUSER_INFO_1008, *LPUSER_INFO_1008;

typedef struct _USER_INFO_1009 { LPWSTR usri1009_script_path;} USER_INFO_1009, *PUSER_INFO_1009, *LPUSER_INFO_1009;

typedef struct _USER_INFO_1010 { DWORD usri1010_auth_flags;} USER_INFO_1010, *PUSER_INFO_1010, *LPUSER_INFO_1010;

typedef struct _USER_INFO_1011 { LPWSTR usri1011_full_name;} USER_INFO_1011, *PUSER_INFO_1011, *LPUSER_INFO_1011;

typedef struct _USER_INFO_1012 { LPWSTR usri1012_usr_comment;} USER_INFO_1012, *PUSER_INFO_1012, *LPUSER_INFO_1012;

typedef struct _USER_INFO_1013 { LPWSTR usri1013_parms;} USER_INFO_1013, *PUSER_INFO_1013, *LPUSER_INFO_1013;

typedef struct _USER_INFO_1014 { LPWSTR usri1014_workstations;} USER_INFO_1014, *PUSER_INFO_1014, *LPUSER_INFO_1014;

typedef struct _USER_INFO_1017 { DWORD usri1017_acct_expires;} USER_INFO_1017, *PUSER_INFO_1017, *LPUSER_INFO_1017;

typedef struct _USER_INFO_1018 { DWORD usri1018_max_storage;} USER_INFO_1018, *PUSER_INFO_1018, *LPUSER_INFO_1018;

typedef struct _USER_INFO_1020 {
DWORD usri1020_units_per_week; LPBYTE usri1020_logon_hours;} USER_INFO_1020, *PUSER_INFO_1020, *LPUSER_INFO_1020;

typedef struct _USER_INFO_1023 { LPWSTR usri1023_logon_server;} USER_INFO_1023, *PUSER_INFO_1023, *LPUSER_INFO_1023;

typedef struct _USER_INFO_1024 { DWORD usri1024_country_code;} USER_INFO_1024, *PUSER_INFO_1024, *LPUSER_INFO_1024;

typedef struct _USER_INFO_1025 { DWORD usri1025_code_page;} USER_INFO_1025, *PUSER_INFO_1025, *LPUSER_INFO_1025;

typedef struct _USER_INFO_1051 {
DWORD usri1051_primary_group_id;
} USER_INFO_1051, *PUSER_INFO_1051, *LPUSER_INFO_1051;

typedef struct _USER_INFO_1052 {
LPWSTR usri1052_profile;
} USER_INFO_1052, *PUSER_INFO_1052, *LPUSER_INFO_1052;

typedef struct _USER_INFO_1053 {
LPWSTR usri1053_home_dir_drive;
} USER_INFO_1053, *PUSER_INFO_1053, *LPUSER_INFO_1053;

For NetUserSetInfo, parmnum values refer to the fields in the user_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in user_info struct
USER_NAME_PARMNUM usri_name
USER_PASSWORD_PARMNUM usri_password
USER_PASSWORD_AGE_PARMNUM usri_password_age
USER_PRIV_PARMNUM usri_priv
USER_HOME_DIR_PARMNUM usri_home_dir
USER_COMMENT_PARMNUM usri_comment
USER_FLAGS_PARMNUM usri_flags
USER_SCRIPT_PATH_PARMNUM usri_script_path
USER_AUTH_FLAGS_PARMNUM usri_auth_flags
USER_FULL_NAME_PARMNUM usri_full_name
USER_USR_COMMENT_PARMNUM usri_usr_comment
USER_PARMS_PARMNUM usri_parms
USER_WORKSTATIONS_PARMNUM usri_workstations
USER_LAST_LOGON_PARMNUM usri_last_logon
USER_LAST_LOGOFF_PARMNUM usri_last_logoff
USER_ACCT_EXPIRES_PARMNUM usri_acct_expires
USER_MAX_STORAGE_PARMNUM usri_max_storage
USER_UNITS_PER_WEEK_PARMNUM usri_units_per_week
USER_LOGON_HOURS_PARMNUM usri_logon_hours
USER_PAD_PW_COUNT_PARMNUM usri_bad_pw_count
USER_NUM_LOGONS_PARMNUM usri_num_logons
USER_LOGON_SERVER_PARMNUM usri_logon_server
USER_COUNTRY_CODE_PARMNUM usri_country_code
USER_CODE_PAGE_PARMNUM usri_code_page
USER_PRIMARY_GROUP_PARMNUM usri_primary_group_id
USER_PROFILE usri_profile
USER_HOME_DIR_DRIVE_PARMNUM usri_home_dir_drive

The user functions are:
NetUserAdd_win32_NetUserAdd
NetUserDel_win32_NetUserDel
NetUserEnum_win32_NetUserEnum
NetUserGetGroups_win32_NetUserGetGroups
NetUserGetInfo_win32_NetUserGetInfo
NetUserGetLocalGroups_win32_NetUserGetLocalGroups
NetUserSetGroups_win32_NetUserSetGroups
NetUserSetInfo_win32_NetUserSetInfo
+$#A User Modal Functions
The user modal functions control the system-wide parameters that affect the Windows NT Security system behavior.
User modal information is available at three levels:
typedef struct _USER_MODALS_INFO_0 { DWORD usrmod0_min_passwd_len; DWORD usrmod0_max_passwd_age; DWORD usrmod0_min_passwd_age; DWORD usrmod0_force_logoff; DWORD usrmod0_password_hist_len;} USER_MODALS_INFO_0, *PUSER_MODALS_INFO_0, LPUSER_MODALS_INFO_0;

typedef struct _USER_MODALS_INFO_1 { DWORD usrmod1_role; LPWSTR usrmod1_primary;} USER_MODALS_INFO_1, *PUSER_MODALS_INFO_1, *LPUSER_MODALS_INFO_1;

typedef struct _USER_MODALS_INFO_2 { LPWSTR usrmod2_domain_name; PSID usrmod2_domain_id;}USER_MODALS_INFO_2, *PUSER_MODALS_INFO_2, *LPUSER_MODALS_INFO_2;

The following information levels are valid only for NetUserModalsSet_win32_NetUserModalsSet and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well. Note that the mapping from the old parmnum to the information level does not apply to the role and primary fields.
typedef struct _USER_MODALS_INFO_1001 { DWORD usrmod1001_min_passwd_len;} USER_MODALS_INFO_1001, *PUSER_MODALS_INFO_1001, *LPUSER_MODALS_INFO_1001;

typedef struct _USER_MODALS_INFO_1002 { DWORD usrmod1002_max_passwd_age;} USER_MODALS_INFO_1002, *PUSER_MODALS_INFO_1002, *LPUSER_MODALS_INFO_1002;

typedef struct _USER_MODALS_INFO_1003 { DWORD usrmod1003_min_passwd_age;} USER_MODALS_INFO_1003, *PUSER_MODALS_INFO_1003, *LPUSER_MODALS_INFO_1003;

typedef struct _USER_MODALS_INFO_1004 { DWORD usrmod1004_force_logoff;} USER_MODALS_INFO_1004, *PUSER_MODALS_INFO_1004, *LPUSER_MODALS_INFO_1004;

typedef struct _USER_MODALS_INFO_1005 { DWORD usrmod1005_password_hist_len;} USER_MODALS_INFO_1005, *PUSER_MODALS_INFO_1005, *LPUSER_MODALS_INFO_1005;

typedef struct _USER_MODALS_INFO_1006 { DWORD usrmod1006_role;} USER_MODALS_INFO_1006, *PUSER_MODALS_INFO_1006, *LPUSER_MODALS_INFO_1006;

typedef struct _USER_MODALS_INFO_1007 { LPWSTR usrmod1007_primary;} USER_MODALS_INFO_1007, *PUSER_MODALS_INFO_1007, *LPUSER_MODALS_INFO_1007;

For NetUserModalsSet_win32_NetUserModalsSet, parmnum values refer to the fields in the modals_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in modals_info struct
MODALS_MIN_PASSWD_LEN_PARMNUM usrmod_min_passwd_len
MODALS_MAX_PASSWD_AGE_PARMNUM usrmod_max_passwd_age
MODALS_MIN_PASSWD_AGE_PARMNUM usrmod_min_passwd_age
MODALS_FORCE_LOGOFF_PARMNUM usrmod_force_logoff
MODALS_PASSWD_HIST_LIST_PARMNUM usrmod_passwd_hist_list
MODALS_ROLE_PARMNUM usrmod_role
MODALS_PRIMARY_PARMNUM usrmod_primary
MODALS_DOMAIN_NAME_PARMNUM usrmod_domain_name
MODALS_DOMAIN_ID_PARMNUM usrmod_domain_id

The user modal functions are:
NetUserModalsGet_win32_NetUserModalsGet
NetUserModalsSet_win32_NetUserModalsSet
+$#A Global Group functions
The global group functions control global groups of users in a way that can be used across domains. A global group is a set of users who share common permissions in the security database. Group functions create or delete global groups, and review or adjust the membership of the global groups. The global group has a group name that specifies the user names of global group members.
To create a global group, an application calls NetGroupAdd_win32_NetGroupAdd, supplying a group name. Initially, the global group has no members. To assign members to the global group, call NetGroupSetUsers_win32_NetGroupSetUsers. To add a user to an existing global group, call NetGroupAddUser_win32_NetGroupAddUser. To set general information about the global group, call NetGroupSetInfo_win32_NetGroupSetInfo.
NetGroupDelUser_win32_NetGroupDelUser deletes a specified user name from a global group, and NetGroupDel_win32_NetGroupDel disbands a global group. NetGroupDel works whether or not the global group has any members.
Three group functions retrieve information about the global groups on a server: NetGroupEnum_win32_NetGroupEnum produces a list of all global groups; NetGroupGetUsers_win32_NetGroupGetUsers lists all members of a specified global group; and NetGroupGetInfo_win32_NetGroupGetInfo returns general information about the global group.Each user account automatically belongs to one of the special global groups Domain Users or None, according to the user's Security Requirements. Membership of these global groups is indirectly controlled by the NetUserAdd_win32_NetUserAdd, NetUserDel_win32_NetUserDel, and NetUserSetInfo_win32_NetUserSetInfo functions. Group functions control groups of users.
Group account information is available at two levels:
typedef struct _GROUP_INFO_0 { LPWSTR grpi0_name;} GROUP_INFO_0, *PGROUP_INFO_0, *LPGROUP_INFO_0;

typedef struct _GROUP_INFO_1 { LPWSTR grpi1_name; LPWSTR grpi1_comment;} GROUP_INFO_1, *PGROUP_INFO_1, *LPGROUP_INFO_1;

typedef struct _GROUP_INFO_2 { LPWSTR grpi2_name; LPWSTR grpi2_comment; DWORD grpi2_group_id; DWORD grpi2_attributes;}GROUP_INFO_2, *PGROUP_INFO_2;

The groups to which a user belongs may be obtained at one information level.
typedef struct _GROUP_USERS_INFO_0 { LPWSTR grui0_name;} GROUP_USERS_INFO_0, *PGROUP_USERS_INFO_0, *LPGROUP_USERS_INFO_0;

typedef struct _GROUP_USERS_INFO_1 { LPWSTR grui1_name; DWORD grui1_attributes;} GROUP_USERS_INFO_1, *PGROUP_USERS_INFO_1, *LPGROUP_USERS_INFO_1;

The following information levels are valid only for NetGroupSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _GROUPINFO_1002 { LPWSTR grpi1002_comment;} GROUP_USERS_INFO_1002, *PGROUP_USERS_INFO_1002, *LPGROUP_USERS_INFO_1002;

typedef struct _GROUP_INFO_1005 { DWORD grpi1005_attributes;} GROUP_INFO_1005, *PGROUP_INFO_1005, *LPGROUP_INFO_1005;

For NetGroupSetInfo, parmnum values refer to the fields in the group_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in group_info struct
GROUP_NAME_PARMNUM grpi_name
GROUP_COMMENT_PARMNUM grpi_comment
GROUP_ATTRIBUTES_PARMNUM grpi_attributes

The group functions are:
NetGroupAdd_win32_NetGroupAdd
NetGroupAddUser_win32_NetGroupAddUser
NetGroupDel_win32_NetGroupDel
NetGroupDelUser_win32_NetGroupDelUser
NetGroupEnum_win32_NetGroupEnum
NetGroupGetInfo_win32_NetGroupGetInfo
NetGroupGetUsers_win32_NetGroupGetUsers
NetGroupSetInfo_win32_NetGroupSetInfo
NetGroupSetUsers_win32_NetGroupSetUsers
+$#A Local Group Functions
A local group is a set of users who share common permissions in the security database. A local group can have members that are either users or global groups (global groups can contain only users). The local group functions control members of local groups in a way that can be used only by the systems within a "cluster". A "cluster" is the individual workstation if the system is a Windows NT system, but it contains all the Windows NT Servers of a domain if the system is a Windows NT Server. Thus, a local group defined on a workstation can be used only on that workstation, but a local group defined on a Windows NT Server can be used by any other Windows NT Server within the same domain. The local Group functions create or delete local groups, and review or adjust the memberships of local groups.
A member can be added to a local group by specifying the security identifier (SID) of the member. The Win32 LookupAccountName_win32_LookupAccountName function can be used to translate a member account name to a SID.
To create a local group, an application calls NetLocalGroupAdd_win32_NetLocalGroupAdd, supplying a local group name. Initially, the local group has no members. To assign members to the local group, call NetLocalGroupSetMembers_win32_NetLocalGroupSetMembers. To add a member to an existing local group, call NetLocalGroupAddMember_win32_NetLocalGroupAddMember. To set general information about the local group, call NetLocalGroupSetInfo_win32_NetLocalGroupSetInfo.
NetLocalGroupDelMember_win32_NetLocalGroupDelMember deletes a specified member from a local group, and NetLocalGroupDel_win32_NetLocalGroupDel disbands a local group, deleting all existing members of the local group first.
Three local group functions retrieve information about the local groups on a server: NetLocalGroupEnum_win32_NetLocalGroupEnum produces a list of all local groups; NetLocalGroupGetMembers_win32_NetLocalGroupGetMembers lists all members of a specified local group; and NetLocalGroupGetInfo_win32_NetLocalGroupGetInfo returns general information about the local group.
Group account information is available at three levels:
typedef struct _LOCALGROUP_INFO_0 { LPWSTR lgrpi0_name;} LOCALGROUP_INFO_0, *PLOCALGROUP_INFO_0, LPLOCALGROUP_INFO_0;

typedef struct _LOCALGROUP_INFO_1 { LPWSTR lgrpi1_name; LPWSTR lgrpi1_comment;} LOCALGROUP_INFO_1, *PLOCALGROUP_INFO_1, LPLOCALGROUP_INFO_1;

typedef struct _LOCALGROUP_INFO_1002 { LPWSTR lgrpi1002_comment;} LOCALGROUP_INFO_1002, *PLOCALGROUP_INFO_1002, LPLOCALGROUP_INFO_1002;

The local group to which a member belongs may be obtained at two information level.
typedef struct _LOCALGROUP_MEMBERS_INFO_0 { PSID lgrmi0_sid;} LOCALGROUP_MEMBERS_INFO_0, *PLOCALGROUP_MEMBERS_INFO_0, *LPLOCALGROUP_MEMBERS_INFO_0;

typedef struct _LOCALGROUP_MEMBERS_INFO_0 { PSID lgrmi0_sid;} LOCALGROUP_MEMBERS_INFO_0, *PLOCALGROUP_MEMBERS_INFO_0, *LPLOCALGROUP_MEMBERS_INFO_0;

typedef struct _LOCALGROUP_MEMBERS_INFO_1 { PSID lgrmi1_sid; SID_NAME_USE lgrmi1_sidusage; LPWSTR lgrmi1_name;} LOCALGROUP_MEMBERS_INFO_1, *PLOCALGROUP_MEMBERS_INFO_1, *LPLOCALGROUP_MEMBERS_INFO_1;

typedef struct _LOCALGROUP_USERS_INFO_0 { LPWSTR lgrui0_name;} LOCALGROUP_USERS_INFO_0, *PLOCALGROUP_USERS_INFO_0, *LPLOCALGROUP_USERS_INFO_0;

For NetLocalGroupSetInfo_win32_NetLocalGroupSetInfo, parmnum values refer to the fields in the group_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in group_info struct
LOCALGROUP_NAME_PARMNUM lgrpi_name
LOCALGROUP_COMMENT_PARMNUM lgrpi_comment

The local group functions are:
NetLocalGroupAdd_win32_NetLocalGroupAdd
NetLocalGroupAddMember_win32_NetLocalGroupAddMember
NetLocalGroupDel_win32_NetLocalGroupDel
NetLocalGroupDelMember_win32_NetLocalGroupDelMember
NetLocalGroupEnum_win32_NetLocalGroupEnum
NetLocalGroupGetInfo_win32_NetLocalGroupGetInfo
NetLocalGroupGetMembers_win32_NetLocalGroupGetMembers
NetLocalGroupSetInfo_win32_NetLocalGroupSetInfo
NetLocalGroupSetMembers_win32_NetLocalGroupSetMembers
+$#A Access Functions

Note A full set of access setting functions exist in Win32. These should be used in place of the NetAccess functions. The NetAccess functions work only when remoted to a downlevel system.

Access permissions functions examine or modify user or group access permissions for specified resources. An access control list (ACL) contains the name of a resource, an audit attribute field, and a list of access control entries. An access control entry (ACE) is a user name or group name and its corresponding access permissions.
The audit attribute field defines what type of events will be audited for that resource. It is possible to audit various types of events, depending on whether the resource is a file or a directory. It is possible to audit events such as opening, writing, and deleting a file; creating or deleting a directory; and changing the ACL of the resource.
NetAccessAdd_win32_NetAccessAdd creates an ACL for a resource and sets user name or group name access permissions. NetAccessDel_win32_NetAccessDel deletes the ACL for a resource. NetAccessGetInfo_win32_NetAccessGetInfo returns the ACL for a particular resource. NetAccessEnum_win32_NetAccessEnum returns information about all ACLs.
Only users or applications with admin group membership or special permission for the resource can define or examine access permissions. Users have special permissions for a resource when they are granted ACCESS_PERM permission for that resource; this is also known as P permission.
NetAccessCheck_win32_NetAccessCheck verifies whether a user has permission to perform a specified operation on a particular resource. If access permission is needed, you can use NetAccessSetInfo_win32_NetAccessSetInfo to change the ACL. NetAccessGetUserPerms_win32_NetAccessGetUserPerms returns a specified user's or group's permission for a specified resource.
Access permission information is available at two levels:
typedef struct _ACCESS_INFO_0 { LPWSTR acc0_resource_name;} ACCESS_INFO_0, *PACCESS_INFO_0, *LPACCESS_INFO_0;

typedef struct _ACCESS_INFO_1 { LPWSTR acc1_resource_name; DWORD acc1_attr; DWORD acc1_count;} ACCESS_INFO_1, *PACCESS_INFO_1, *LPACCESS_INFO_1;

Access list information is available at one levels:
typedef struct _ACCESS_LIST { LPWSTR acl_ugname; DWORD acl_access;} ACCESS_LIST, *PACCESS_LIST, *LPACCESS_LIST;

The following information levels are valid only for NetAccessSetInfo_win32_NetAccessSetInfo and replace the older way of passing in a Parmnum to set a specific field.
The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:
typedef struct _ACCESS_INFO_1002 { DWORD acc1002_attr;} ACCESS_INFO_1002, *PACCESS_INFO_1002, *LPACCESS_INFO_1002;

For NetAccessSetInfo_win32_NetAccessSetInfo, parmnum values refer to the fields in the access_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.
parmnum value Field in access_info struct
ACCESS_RESOURCE_NAME_PARMNUM acc_resource_name
ACCESS_ATTR_PARMNUM acc_attr
ACCESS_COUNT_PARMNUM acc_count
ACCESS_ACCESS_LIST_PARMNUM array following acc1_count

The following are defined for the bits in the acc1_attr field of _ACCESS_INFO_1:
Value Constant
ACCESS_AUDIT 0x001
ACCESS_SUCCESS_OPEN 0x010
ACCESS_SUCCESS_WRITE 0x020
ACCESS_SUCCESS_DELETE 0x040
ACCESS_SUCCESS_ACL 0x080
ACCESS_SUCCESS_MASK 0x0F0
ACCESS_FAIL_OPEN 0x100
ACCESS_FAIL_WRITE 0x200
ACCESS_FAIL_DELETE 0x400
ACCESS_FAIL_ACL 0x800
ACCESS_FAIL_MASK 0xF00
ACCESS_FAIL_SHIFT 0x004

The access permission functions are:
NetAccessAdd_win32_NetAccessAdd
NetAccessCheck_win32_NetAccessCheck
NetAccessDel_win32_NetAccessDel
NetAccessEnum_win32_NetAccessEnum
NetAccessGetInfo_win32_NetAccessGetInfo
NetAccessGetUserPerms_win32_NetAccessGetUserPerms
NetAccessSetInfo_win32_NetAccessSetInfo
+$#A Domain Functions
Domain functions retrieve information about a domain. They require that the workstation service be started.
The domain functions are:
NetGetDCName_win32_NetGetDCName
NetServerTransportAdd_win32_NetServerTransportAdd
NetServerTransportDel_win32_NetServerTransportDel
NetServerTransportEnum_win32_NetServerTransportEnum
NetWkstaTransportAdd_win32_NetWkstaTransportAdd
NetWkstaTransportDel_win32_NetWkstaTransportDel
NetWkstaTransportEnum_win32_NetWkstaTransportEnum
+$#A Transport Functions
Transport functions handle binding and unbinding of transports to and from the server and redirector, and also enumerate the transports used by a component. The server transport functions deal with transports managed by the server, and the workstation transport functions deal with transports managed by the redirector.
NetServerTransportAdd_win32_NetServerTransportAdd allows the user to bind the transport to the server. NetServerTransportDel_win32_NetServerTransportDel allows the user to unbind the server from the transport. NetServerTransportEnum_win32_NetServerTransportEnum enumerates the transports that are managed by the server.
The NetWkstaTransportAdd/Del/Enum functions perform equivalent operations for the workstation.
Server transport functions are available at one information level.
typedef struct _SERVER_TRANSPORT_INFO_0 { DWORD svti0_number_of_vcs; LPWSTR svti0_transport_name; LPBYTE svti0_transport_address; DWORD svti0_transportaddresslength; LPWSTR svti0_networkaddress;} SERVER_TRANSPORT_INFO_0, *PSERVER_TRANSPORT_INFO_0, *LPSERVER_TRANSPORT_INFO_0;

Workstation transport functions are available at one information level.
typedef struct _WKSTA_TRANSPORT_INFO_0 { DWORD wkti0_quality_of_service; DWORD wkti0_number_of_vcs; LPWSTR wkti0_transport_name; LPWSTR wkti0_transport_address;} WKSTA_TRANSPORT_INFO_0, *PWKSTA_TRANSPORT_INFO_0, *LPWKSTA_TRANSPORT_INFO_0;

The transport functions are:
NetServerTransportAdd_win32_NetServerTransportAdd
NetServerTransportDel_win32_NetServerTransportDel
NetServerTransportEnum_win32_NetServerTransportEnum
+$#A Alert F u n c t i o n s
A l e r t f u n c t i o n s n o t i fy network service programs and applications of network events. An event is a particular instance of a process or state of hardware as defined by an application. The alert functions allow applications to indicate when predefined events occur.
NetAlertRaise_win32_NetAlertRaise is used to indicate that an event has occurred.
The fixed-length header contains the standard alert data structure. The STD_ALERT data structure has the following format:
typedef struct _STD_ALERT { DWORD alrt_timestamp; WCHAR alrt_eventname[EVLEN+1]; WCHAR alrt_servicename[SNLEN+1];} STD_ALERT, *PSTD_ALERT, *LPSTD_ALERT;

The ADMIN_OTHER_INFO data structure has the following format:
typedef struct _ADMIN_OTHER_INFO { DWORD alrtad_errcode; DWORD alrtad_numstrings;} ADMIN_OTHER_INFO, *PADMIN_OTHER_INFO, *LPADMIN_OTHER_INFO;/* Followed by consecutive Unicode strings; the count is in the alrtad_numstrings element. */ WCHAR mergestrings[];

The ERRLOG_OTHER_INFO data structure has the following format:
typedef struct _ERRLOG_OTHER_INFO { DWORD alrter_errcode; DWORD alrter_offset;} ERRLOG_OTHER_INFO, *PERRLOG_OTHER_INFO, *LPERRLOG_OTHER_INFO;

The PRINT_OTHER_INFO data structure has the following format:
typedef struct _PRINT_OTHER_INFO { DWORD alrtpr_jobid; DWORD alrtpr_status; DWORD alrtpr_submitted; DWORD alrtpr_size;} PRINT_OTHER_INFO, *PPRINT_OTHER_INFO, *LPPRINT_OTHER_INFO;/* Followed by consecutive Unicode strings. */ WCHAR computername[]; WCHAR username[]; WCHAR queuename[]; WCHAR destname[]; WCHAR status_string[];

The USER_OTHER_INFO data structure has the following format:
typedef struct _USER_OTHER_INFO { DWORD alrtus_errcode; DWORD alrtus_numstrings;} USER_OTHER_INFO, *PUSER_OTHER_INFO, *LPUSER_OTHER_INFO;/* * Followed by a number of consecutive Unicode strings; * the count is in the alrtus_numstrings element. */ */ WCHAR mergestrings[];/* Further followed by two more consecutive Unicode strings. */ WCHAR username[]; WCHAR computername[];

The alert functions are:
NetAlertRaise_win32_NetAlertRaise
NetAlertRaiseEx_win32_NetAlertRaiseEx
+$#A Error Logging Functions

Note Use the error logging functions only for remoting to downlevel. For Windows NT use the Event Logging functions.

Windows NT uses an integrated Event Logging mechanism for reporting both errors and audits. The NetError and NetAudit functions are provided to access downlevel LAN Manager logs. They will report ERROR_NOT_SUPPORTED if called to an Windows NT system.
The error log is a file that stores error messages (in binary format). It contains information about LAN Manager software internal errors, MS OS/2 and MS-DOS internal errors, and network service errors.
NetErrorLogRead_win32_NetErrorLogRead reads entries from the error log; and NetErrorLogClear_win32_NetErrorLogClear clears the error log and, optionally, saves the entries in a backup file.
NetErrorLogRead uses the ERROR_LOG data structure to read entries from and write entries to the error log. An entry consists of a fixed-length data structure. The data structure can be followed by Unicode strings (el_text) that describe the error message, and a block of raw data (el_data) related to the cause of the error. Because of the variable lengths and structures of the el_data and el_text portions of the entry, only the fixed-length data structure is defined in the ERROR_LOG data structure.
The error log entry has the following format:
typedef struct _ERROR_LOG { DWORD el_len; DWORD el_reserved; DWORD el_time; DWORD el_error; LPWSTR el_name LPWSTR el_text;
LPBYTE el_data;
DWORD el_data_size; DWORD el_nstrings;} ERROR_LOG, *PERROR_LOG, *LPERROR_LOG;/* * Variable-length raw data specific to the error. el_len will be preceded by pad bytes
* as required for proper alignment. There may also be pad bytes after the end of the
* fixed length structure, before el_data. Use the pointer in the fixed length structure
* to reference this data. */ BYTE el_data[]; /* Raw data */ DWORD el_len2;

The error log handle is defined as follows:
typedef struct _HLOG { DWORD time; DWORD last_flags; DWORD offset; DWORD rec_offset;} HLOG, *PHLOG, *LPHLOG;

The error log functions are:
NetErrorLogClear_win32_NetErrorLogClear
NetErrorLogRead_win32_NetErrorLogRead
+$#A Configuration Functions

Note The configuration functions are only for downlevel support. Use the registry functions to retrieve configuration information on Windows NT.

Configuration functions retrieve network configuration information.
Configuration functions are available at one information level.
typedef struct _CONFIG_INFO_0 { LPWSTR cfgi0_key; LPWSTR cfgi0_data;} CONFIG_INFO_0, *PCONFIG_INFO_0, *LPCONFIG_INFO_0;

The configuration functions are:
NetConfigGet_win32_NetConfigGet
NetConfigGetAll_win32_NetConfigGetAll
NetConfigSet_win32_NetConfigSet
+$#A Statistics Functions
LAN Manager accumulates a set of operating statistics for workstations and servers from the time that the Workstation or Server service is started. NetStatisticsGet_win32_NetStatisticsGet is called to get those statistics. The statistics function, NetStatisticsGet, retrieves the operating statistics for workstations and servers. Since Windows NT and downlevel workstations collect a different set of statistics, the caller must know whether the server is Windows NT or downlevel (which can be discovered using the NetServerGetInfo_win32_NetServerGetInfo function) and interpret the returned buffer accordingly.
NetStatisticsGet returns a STAT_WORKSTATION_0 data structure when workstation statistics are requested; it returns a STAT_SERVER_0 data structure when server statistics are requested.
The downlevel STAT_WORKSTATION_0 data structure has the following format:
typedef struct _STAT_WORKSTATION_0 { DWORD stw0_start; DWORD stw0_numNCB_r; DWORD stw0_numNCB_s; DWORD stw0_numNCB_a; DWORD stw0_fiNCB_r; DWORD stw0_fiNCB_s; DWORD stw0_fiNCB_a; DWORD stw0_fcNCB_r; DWORD stw0_fcNCB_s; DWORD stw0_fcNCB_a; DWORD stw0_sesstart; DWORD stw0_sessfailcon; DWORD stw0_sessbroke; DWORD stw0_uses; DWORD stw0_usefail; DWORD stw0_autorec; DWORD stw0_bytessent_r_hi; DWORD stw0_bytessent_r_lo; DWORD stw0_bytesrcvd_r_hi; DWORD stw0_bytesrcvd_r_lo; DWORD stw0_bytessent_s_hi; DWORD stw0_bytessent_s_lo; DWORD stw0_bytesrcvd_s_hi; DWORD stw0_bytesrcvd_s_lo; DWORD stw0_bytessent_a_hi; DWORD stw0_bytessent_a_lo; DWORD stw0_bytesrcvd_a_hi; DWORD stw0_bytesrcvd_a_lo; DWORD stw0_reqbufneed; DWORD stw0_bigbufneed;} STAT_WORKSTATION_0, *PSTAT_WORKSTATION_0, *LPSTAT_WORKSTATION_0;

The Windows NT STAT_WORKSTATION_0 data structure has the following format:
typedef struct _STAT_WORKSTATION_0 { LARGE_INTEGER StatisticsStartTime; LARGE_INTEGER BytesReceived; LARGE_INTEGER SmbsReceived; LARGE_INTEGER PagingReadBytesRequested; LARGE_INTEGER NonPagingReadBytesRequested; LARGE_INTEGER CacheReadBytesRequested; LARGE_INTEGER NetworkReadBytesRequested; LARGE_INTEGER BytesTransmitted; LARGE_INTEGER SmbsTransmitted; LARGE_INTEGER PagingWriteBytesRequested; LARGE_INTEGER NonPagingWriteBytesRequested; LARGE_INTEGER CacheWriteBytesRequested; LARGE_INTEGER NetworkWriteBytesRequested; DWORD InitiallyFailedOperations; DWORD FailedCompletionOperations; DWORD ReadOperations; DWORD RandomReadOperations; DWORD ReadSmbs; DWORD LargeReadSmbs; DWORD SmallReadSmbs; DWORD WriteOperations; DWORD RandomWriteOperations; DWORD WriteSmbs; DWORD LargeWriteSmbs; DWORD SmallWriteSmbs; DWORD RawReadsDenied; DWORD RawWritesDenied; DWORD NetworkErrors; // Connection /Session counts DWORD Sessions; DWORD FailedSessions; DWORD Reconnects; DWORD CoreConnects; DWORD Lanman20Connects; DWORD Lanman21Connects; DWORD LanmanNtConnects; DWORD ServerDisconnects; DWORD HungSessions; DWORD UseCount; DWORD FailedUseCount; // // Queue Leng ths (updates protected by RdrMpxTableSpinLock NOT // RdrStatisticsSpinlock) // DWORD CurrentCommands;} STAT_WORKSTATION_0, *PSTAT_WORKSTATION_0, *LPSTAT_WORKSTATION_0;

The STAT_SERVER_0 data structure has the following format:
typedef struct _STAT_SERVER_0 { DWORD sts0_start; DWORD sts0_fopens; DWORD sts0_devopens; DWORD sts0_jobsqueued; DWORD sts0_sopens; DWORD sts 0_stimedout; DWORD sts0_serrorout; DWORD sts0_pwerrors; DWORD sts0_permerrors; DWORD sts0_syserrors; DWORD sts0_bytessent_low; DWORD sts0_bytessent_high; DWORD sts0_bytesrcvd_low; DWORD sts0_bytesrcvd_high; DWORD sts0_avresponse; DWORD sts0_reqbufneed; DWORD sts0_bigbufneed;} STAT_SERVER_0, *PSTAT_SERVER_0, *LPSTAT_SERVER_0;

The statistics function is:
NetStatisticsGet_win32_NetStatisticsGet
+$#A Auditing Functions

Note The auditing functions are for downlevel only. Windows NT automatically records audits on failed accesses when AccessCheckAndAudit is used. To read audits, use the ReadEventLog_win32_ReadEventLog function.

Windows NT uses an integrated event logging mechanism for reporting both errors and audits. The NetError and NetAudit functions are provided to access downlevel LAN Manager logs. They will report ERROR_NOT_SUPPORTED if called to an Windows NT system.
Auditing functions control the audit log. Auditing functions monitor operations on the specified server. If auditing is enabled, each monitored operation generates an audit entry. For example, when a user establishes a connection to the server, a single audit entry is generated.
Audit entries are stored in a binary file called an audit trail or audit log. All Auditing functions perform their operations on this file. LAN Manager defines many types of audit entries.
NetAuditRead_win32_NetAuditRead reads the audit log. NetAuditClear_win32_NetAuditClear clears the audit log.
Data Structures
All audit entries include a fixed-length header used in conjunction with variable-length data specific to the entry type. Because of the variable lengths and structures of the ae_data element of the audit entry (it is possible for ae_data to be zero bytes), only the fixed header is defined in the audit_entry data structure.
The variable-length portion of the audit entry can contain an offset to a variable-length Unicode string. The offset values are DWORDs. To determine the value of the pointer to this string, add the offset value to the address of the ae_data data structure.
The following example illustrates this procedure. Assume that pAE points to a buffer that contains a complete audit entry and that the ae_type element of the audit_entry data structure contains the value AE_CONNSTOP, which specifies the predefined AE_CONNSTOP data structure. To point the variable pszComputerName to the Unicode string that contains the name of the client whose connection was stopped, an application would perform the following algorithm:
PAUDIT_ENTRY pAE; /* Fixed part of audit entry */LPAE_CONNSTOP pAEvar; /* Variable-length structure */LPWSTR pszComputerName; /* Pointer to var-length string*//* Calculate the offset to the variable-length structure. */pAEvar = (_LPAE_CONNSTOP) (((LPBYTE) pAE) + pAE->ae_data_offset);/* Calculate the offset to the computername. */pszComputerName = ((LPBYTE) pAEvar) + pAEvar->ae_cp_compname;

Fixed-Length Header
The audit_entry data structure has the following format:
typedef struct _AUDIT_ENTRY { DWORD ae_len; DWORD ae_reserved; DWORD ae_time; DWORD ae_type; DWORD ae_data_offset; /* Offset from beginning address of audit_entry */
DWORD ae_data_size;} AUDIT_ENTRY, *PAUDIT_ENTRY, *LPAUDIT_ENTRY;/* Variable-length data specific to type of audit entry. There may be pad bytes after ae_data_size for alignment purposes.
*/ BYTE ae_data[];/* Terminating length indicator. Preceded by pad bytes for proper alignment.*/ DWORD ae_len2;

where
ae_len and ae_len2
Specify the length of the audit entry. Both have the same value. The ae_len element is included at the beginning and at the end of the audit entry to enable both backward and forward scanning of the log.
ae_reserved
Reserved.
ae_time
Specifies when the audit entry was generated. The value is stored as the number of seconds elapsed since 00:00:00, January 1, 1970.
ae_type
Specifies the type of audit entry. Type values from 0x0000 through 0x07FF are reserved. OEMs and other applications programmers can reserve values from 0x0800 through 0xFFFF.
ae_data_offset
Specifies the byte offset from the beginning of the audit entry to the beginning of the variable-length portion (ae_data) of the audit entry.
ae_data
Specifies the variable-length portion of the audit entry; it differs depending on the type of entry specified by ae_type. The information begins at ae_data_offset bytes from the top of the audit entry. For information about the structure of each entry type defined by LAN Manager, see Variable-Length Data_win32_Variable_Length_Data.

+$#A Variable-Length Data
The following data structures are specific to the audit entry type The structures follow the audit_entry header, but they are not necessarily contiguous.
The _AE_SRVSTATUS data structure is associated with an audit entry of type AE_SRVSTATUS.
typedef struct _AE_SRVSTATUS { DWORD ae_sv_status;}AE_SRVSTATUS , * PAE_SRVSTATUS ;

The _AE_SESSLOGON data structure is associated with an audit entry of type AE_SESSLOGON.
typedef struct _AE_SESSLOGON { DWORD ae_so_compname; DWORD ae_so_username; DWORD ae_so_privilege;}AE_SESSLOGON , * PAE_SESSLOGON ;

The _AE_SESSLOGOFF data structure is associated with an audit entry of type AE_SESSLOGOFF.
typedef struct AE_SESSLOGOFF { DWORD ae_sf_compname; DWORD ae_sf_username; DWORD ae_sf_reason;}AE_SESSLOGOFF , * PAE_SESSLOGOFF;

The _AE_SESSPWERR data structure is associated with an audit entry of type AE_SESSPWERR.
typedef struct _AE_SESSPWERR { DWORD ae_sp_compname; DWORD ae_sp_username;}AE_SESSPWERR , * PAE_SESSPWERR ;

The _AE_CONNSTART data structure is associated with an audit entry of type AE_CONNSTART.
typedef struct _AE_CONNSTART { DWORD ae_ct_compname; DWORD ae_ct_username; DWORD ae_ct_netname; DWORD ae_ct_connid;}AE_CONNSTART , * PAE_CONNSTART ;

The _AE_CONNSTOP data structure is associated with an audit entry of type AE_CONNSTOP.
typedef struct _AE_CONNSTOP { DWORD ae_cp_compname; DWORD ae_cp_username; DWORD ae_cp_netname; DWORD ae_cp_connid; DWORD ae_cp_reason;}AE_CONNSTOP , * PAE_CONNSTOP ;

The _AE_CONNREJ data structure is associated with an audit entry of type AE_CONNREJ.
typedef struct _AE_CONNREJ { DWORD ae_cr_compname; DWORD ae_cr_username; DWORD ae_cr_netname; DWORD ae_cr_reason;}AE_CONNREJ , * PAE_CONNREJ ;

The _AE_RESACCESS data structure is associated with an audit entry of type AE_RESACCESS.
typedef struct _AE_RESACCESS { DWORD ae_ra_compname; DWORD ae_ra_username; DWORD ae_ra_resname; DWORD ae_ra_operation; DWORD ae_ra_returncode; DWORD ae_ra_restype; DWORD ae_ra_fileid;}AE_RESACCESS , * PAE_RESACCESS ;

The _AD_RESACCESSREJ data structure is associated with an audit entry of type AE_RESACCESSREJ.
typedef struct _AE_RESACCESSREJ { DWORD ae_rr_compname; DWORD ae_rr_username; DWORD ae_rr_resname; DWORD ae_rr_operation;}AE_RESACCESSREJ , * PAE_RESACCESSREJ ;

The _AD_CLOSEFILE data structure is associated with an audit entry of type AE_CLOSEFILE.
typedef struct _AE_CLOSEFILE { DWORD ae_cf_compname; DWORD ae_cf_username; DWORD ae_cf_resname; DWORD ae_cf_fileid; DWORD ae_cf_duration DWORD ae_cf_reason;}AE_CLOSEFILE , * PAE_CLOSEFILE ;

The _AE_SERVICESTAT data structure is associated with an audit entry of type AE_SERVICESTAT.
typedef struct _AE_SERVICESTAT { DWORD ae_ss_compname; DWORD ae_ss_username; DWORD ae_ss_svcname; DWORD ae_ss_status; DWORD ae_ss_code; DWORD ae_ss_text; DWORD ae_ss_returnval;}AE_SERVICESTAT , * PAE_SERVICESTAT ;

The _AE_ACLMOD data structure is associated with audit entries of type AE_ACLMOD and AE_ACLMODFAIL.
struct _AE_ACLMOD { DWORD ae_am_compname; DWORD ae_am_username; DWORD ae_am_resname; DWORD ae_am_action; DWORD ae_am_datalen;}AE_ACLMOD, * PAE_ACLMOD;

The _AE_UASMOD data structure is associated with an audit entry of type AE_UASMOD.
typedef struct _AE_UASMOD { DWORD ae_um_compname; DWORD ae_um_username; DWORD ae_um_resname; DWORD ae_um_rectype; DWORD ae_um_action; DWORD ae_um_datalen;}AE_UASMOD , * PAE_UASMOD ;

The _AE_NETLOGON data structure is associated with an audit entry of type AE_NETLOGON.
typedef struct _AE_NETLOGON { DWORD ae_no_compname; DWORD ae_no_username; DWORD ae_no_privilege; DWORD ae_no_authflags;}AE_NETLOGON , * PAE_NETLOGON ;

The _AE_NETLOGOFF data structure is associated with an audit entry of type AE_NETLOGOFF.
typedef struct _AE_NETLOGOFF { DWORD ae_nf_compname; DWORD ae_nf_username; DWORD ae_nf_reserved1; DWORD ae_nf_reserved2;}AE_NETLOGOFF , * PAE_NETLOGOFF ;

The _AE_ACCLIM data structure is associated with an audit entry of type AE_ACCLIMITEXCD.
typedef struct _AE_ACCLIM { DWORD ae_al_compname; DWORD ae_al_username; DWORD ae_al_resname; DWORD ae_al_limit;}AE_ACCLIM , * PAE_ACCLIM ;

The _AE_LOCKOUT data structure is associated with an audit entry of type AE_LOCKOUT.
typedef struct AE_LOCKOUT { DWORD ae_lk_compname; DWORD ae_lk_username; DWORD ae_lk_action; DWORD ae_lk_bad_pw_count;}AE_LOCKOUT , PAE_LOCKOUT ;

The _AE_GENERIC data structure is associated with an audit entry of type AE_GENERIC.
typedef struct _AE_GENERIC { DWORD ae_ge_msgfile; DWORD ae_ge_msgnum; DWORD ae_ge_params; DWORD ae_ge_param1; DWORD ae_ge_param2; DWORD ae_ge_param3; DWORD ae_ge_param4; DWORD ae_ge_param5; DWORD ae_ge_param6; DWORD ae_ge_param7; DWORD ae_ge_param8; DWORD ae_ge_param9;}AE_GENERIC , * PAE_GENERIC ;

The audit log handle is defined as follows:
typedef struct _HLOG { DWORD time; DWORD last_flags; DWORD offset; DWORD rec_offset;} HLOG, *PHLOG, *LPHLOG;

The auditing functions are:
NetAuditClear_win32_NetAuditClear
NetAuditRead_win32_NetAuditRead
+$#A Replicator Functions
The Windows NT replicator functions control how the Windows NT Replicator service updates selective directories from an export server to one or more clients. In addition to providing compatible LAN Manager 2.x functionality in a well-defined manner, this new set of LAN Manager functions allow for specific API (operation) security checking.
The change from the file system based control on LAN Manager 2.x to replicator function control on Windows NT has the following implications:
· Applications can no longer delete a directory in the import path of a client to stop receiving updates from its master.
· Applications can no longer use the REPL.INI file in each replicated directory on a master to control the method of replication.
· Applications can no longer lock or unlock a directory on a master from being replicated by creating or deleting the USERLOCK.* file(s).
· Applications can no longer lock or unlock a directory on a client from receiving updates from its master by creating or deleting the USERLOCK.* file(s).
· Applications that depend on the LAN Manager 2.x behavior of ignoring locks for file integrity trees will need to be modified. (Windows NT policy differs from LAN Manager 2.x policy; under Windows NT the locks are always respected.)

Each of the intended operations listed preceding can be specified to the Windows NT replicator service through an appropriate function.
Any user or application that belongs to the admin or server operator group on a local or remote export server can modify the parameters that control the replication master.
There are three categories of replicator functions:
· Replicator configuration functions_win32_Replicator_Configuration_Functions
· Replicator export directory functions_win32_Replicator_Export_Directory_Functions
· Replicator import directory functions_win32_Replicator_Import_Directory_Functions

+$#A Replicator Configuration Functions
The configuration parameters of the replicator service can be examined using NetReplGetInfo_win32_NetReplGetInfo. They can be modified using NetReplSetInfo_win32_NetReplSetInfo.
The replicator service configuration parameters are:
role
Role of the Replicator service, which is either REPL_ROLE_IMPORT (client), REPL_ROLE_EXPORT (master), or REPL_ROLE_BOTH. On a Windows NT Server, any of these values are allowed. Under Windows NT systems, only REPL_ROLE_IMPORT is allowed.
exportpath
Fully qualified path to the master tree in which directories are created and replicated from. This path must include the drive letter.
exportlist
List of computer and domain names to send update announcements to. If this list is not provided, then update announcements will be sent to the server's domain. The list entries are separated by semicolons; computer names in the list must not have leading backslashes.
importpath
Fully qualified path to the client tree in which directories are created to receive the replicas of the master. This path must include the drive letter.
importlist
List of computers and domain names to receive updates from. If this list is not provided, then update announcements are received from the server's domain. The list entries are separated by semicolons; computer names in the list must not have leading backslashes.
logonusername
User account name belonging to the replicator group with which the client logs on to read files from its master(s). This field is ignored by the Windows NT replicator, but is provided for possible future use.
interval
Time in minutes within which the master checks for changes in all the replicated directories. This field may be ignored by the Windows NT replicator, but is provided for possible future use.
pulse
Time in number of intervals within which the master notifies its clients of the current replica version when no updates are necessary. This field may be ignored by the Windows NT replicator, but is provided for possible future use.
guardtime
Time in minutes that a REPL_INTEGRITY_TREE level integrity directory must be stable before a client is allowed to update from it. (See the explanation of integrity in Replicator Export Directory Functions_win32_Replicator_Export_Directory_Functions). The default guard time is 2 minutes.
random
A value in seconds sent by a master to its clients so that the clients can use the 0 random range to generate a random time to wait on before updating from the master.

The configuration parameters (except importpath and exportpath) values can all be dynamically set.
These functions can be called whether the replicator service is running or not. If the replicator service is already running, any modification (except to importpath and exportpath) to the replication configuration takes effect immediately, and is persistent after the replicator service has been stopped. If the replicator service is not started, the parameters are stored as persistent information and will take effect when the replicator service starts up. The service must be stopped to set the importpath and exportpath values.
The replicator configuration information structures are:
typedef struct _REPL_INFO_0 { DWORD rp0_role; LPWSTR rp0_exportpath; LPWSTR rp0_exportlist; LPWSTR rp0_importpath; LPWSTR rp0_importlist; LPWSTR rp0_logonusername; DWORD rp0_interval; DWORD rp0_pulse; DWORD rp0_guardtime; DWORD rp0_random;} REPL_INFO_0, *PREPL_INFO_0, *LPREPL_INFO_0;
typedef struct _REPL_INFO_1000 { DWORD rp1000_interval;} REPL_INFO_1000, *PREPL_INFO_1000, *LPREPL_INFO_1000;
typedef struct _REPL_INFO_1001 { DWORD rp1001_pulse;} REPL_INFO_1001, *PREPL_INFO_1001, *LPREPL_INFO_1001;
typedef struct _REPL_INFO_1002 { DWORD rp1002_guardtime;} REPL_INFO_1002, *PREPL_INFO_1002, *LPREPL_INFO_1002;
typedef struct _REPL_INFO_1003 { DWORD rp1003_random;} REPL_INFO_1003, *PREPL_INFO_1003, *LPREPL_INFO_1003;

+$#A Replicator Export Directory Functions
The replicator export directory functions control top-level directories under the export path on the master. A user can create a new directory under the export path and the Replicator service will automatically replicate that directory. Or, a directory under the export path can be registered using NetReplExportDirAdd_win32_NetReplExportDirAdd. When adding a directory to be replicated using these functions, the replication controls (integrity and extent) are specified via the NetReplExportDirAdd function. If the directory is created in the file system and no replicator functions are called, then the directory is treated as having file integrity and tree extent.
Integrity determines when a master updates a client. When integrity is REPL_INTEGRITY_FILE, the client gets a replica of a file within the directory when it is not in use (being changed or replicated). When integrity is set to REPL_INTEGRITY_TREE, every file and directory within the replicated directory must be stable for the amount of time specified by the guardtime parameter before the client is updated. Extent determines whether the entire tree within the directory is replicated (REPL_EXTENT_TREE) or only the files in the first-level directory is replicated (REPL_EXTENT_FILE).
The replication controls of each replicated directory can be examined using NetReplExportDirGetInfo_win32_NetReplExportDirGetInfo, and dynamically modified using NetReplExportDirSetInfo_win32_NetReplExportDirSetInfo. These control fields used to be specified in the REPL.INI file within each replicated directory on LAN Manager 2.x, and they were not dynamically settable. On Windows NT, the REPL.INI file is not used and will be ignored in the replication process.
NetReplExportDirEnum_win32_NetReplExportDirEnum returns a list of directories that are currently replicated. NetReplExportDirDel_win32_NetReplExportDirDel deregisters a directory so that it is no longer replicated.
The lock status information is returned in two fields: lockcount and locktime. Lockcount indicates the number of outstanding locks on a directory. Locktime is the time (in seconds since 1970, GMT) when the directory was first locked, or is 0 if the directory is not locked at the present time.
NetReplExportDirLock_win32_NetReplExportDirLock locks a directory so that it is not replicated, by incrementing a lock reference count for the directory. A lock on a directory can be unlocked using NetReplExportDirUnlock_win32_NetReplExportDirUnlock. The replication does not resume unless all outstanding locks on that directory are released, and the lock reference count is returned to 0. (The locktime field is automatically set to 0 when lockcount is 0.)
The replicator export directory functions can be called whether the Replicator service is running or not. If the Replicator service is running as a master, any modification to the directory controls takes effect immediately, and is persistent after the Replicator service has been stopped. If the Replicator service is not started, the controls for the directory is stored as persistent information and will take effect when the Replicator service starts up.
The replicator export directory functions are available at the following information levels:
typedef struct _REPL_EDIR_INFO_0 { LPWSTR rped0_dirname;} REPL_EDIR_INFO_0, *PREPL_EDIR_INFO_0, *LPREPL_EDIR_INFO_0;
typedef struct _REPL_EDIR_INFO_1 { LPWSTR rped1_dirname; DWORD rped1_integrity; DWORD rped1_extent;} REPL_EDIR_INFO_1, *PREPL_EDIR_INFO_1, *LPREPL_EDIR_INFO_1;
typedef struct _REPL_EDIR_INFO_2 { LPWSTR rped2_dirname; DWORD rped2_integrity; DWORD rped2_extent; DWORD rped2_lockcount; DWORD rped2_locktime;} REPL_EDIR_INFO_2, *PREPL_EDIR_INFO_2, *LPREPL_EDIR_INFO_2;
typedef struct _REPL_EDIR_INFO_1000 { DWORD rped1000_integrity;} REPL_EDIR_INFO_1000, *PREPL_EDIR_INFO_1000, *LPREPL_EDIR_INFO_1000;
typedef struct _REPL_EDIR_INFO_1001 { DWORD rped1001_extent;} REPL_EDIR_INFO_1001, *PREPL_EDIR_INFO_1001, *LPREPL_EDIR_INFO_1001;

The replicator export directory functions are:
NetReplExportDirAdd_win32_NetReplExportDirAdd
NetReplExportDirDel_win32_NetReplExportDirDel
NetReplExportDirEnum_win32_NetReplExportDirEnum
NetReplExportDirGetInfo_win32_NetReplExportDirGetInfo
NetReplExportDirSetInfo_win32_NetReplExportDirSetInfo
NetReplExportDirLock_win32_NetReplExportDirLock
NetReplExportDirUnlock_win32_NetReplExportDirUnlock
+$#A Replicator Import Directory Functions
The replicator import directory functions designate the top-level directories under the import path to receive updates on. They also return status information about a replicated directory on the client. On LAN Manager 2.x, a user must create a directory under the import path and the Replicator service automatically replicates to it. On Windows NT, import directories are automatically added if they are exported by an export server from which importer is importing. Another way to register a directory in advance of it being exported, is to use the NetReplImportDirAdd_win32_NetReplImportDirAdd functions. This function does not create the directory itself. This is useful if you wish to modify some of the properties of the import directory (for example, to lock it) prior to it first beginning to import this directory.
NetReplImportDirDel_win32_NetReplImportDirDel deregisters a directory. This is used to clean up a directory that is no longer being exported. It will not stop replication if there is an active exporter, since it will be re-registered the next time the exporter tells the importer what directories it is exporting. If you wish to prevent importing of an actively exported directory, use the NetReplImportDirLock_win32_NetReplImportDirLock function.
NetReplImportDirEnum_win32_NetReplImportDirEnum lists all the directories that are replicated to a client, and NetReplImportDirGetInfo_win32_NetReplImportDirGetInfo returns the status of a specified directory.
The status information of a directory consists of the replication state, the UNC computer name of the master (mastername), and the time (in seconds since 1970, GMT) when the directory was last updated (last_update_time). If the state is REPL_STATE_OK, the directory currently has a master, and is receiving regular update notices from it. If the state is REPL_STATE_NO_MASTER, the directory is not supported by any master, and it is normally empty. If the state is REPL_STATE_NO_SYNC, the directory has a master, but the master has not sent any update notices within the interval time period. This may be due to a communication failure, the master crashing, the directory being locked, files in the client directory being opened at update time, or an unstable REPL_INTEGRITY_TREE integrity directory on the master. If the client Replicator service is not started the state is REPL_STATE_NEVER_REPLICATED, mastername is a NULL string, and last_update_time is 0.NetReplImportDirLock_win32_NetReplImportDirLock locks a directory so that it does not receive updates, by incrementing a lock reference count for the directory. A lock on a directory can be unlocked using NetReplImportDirUnlock_win32_NetReplImportDirUnlock. The directory is not updated unless all outstanding locks on that directory are released, and the lock reference count is returned to 0.
The lock status information is returned in two fields: lockcount and locktime. Lockcount indicates the number of outstanding locks on a directory. Locktime is the time (in seconds since 1970, GMT) when the directory was first locked. (locktime is set to 0 whenever lockcount goes to 0.)
The replicator import directory functions can be called whether the Replicator service is running or not. If the Replicator service is running as a client, directory adds or deletes take effect immediately, and is persistent after the Replicator service has been stopped. If the Replicator service is not started, any added directory will receive updates when the Replicator service starts up (if there exists a master that exports the directory).
The replicator import directory function are available at the following information levels:
typedef struct _REPL_IDIR_INFO_0 { LPWSTR rpid0_dirname;} REPL_IDIR_INFO_0, *PREPL_IDIR_INFO_0, *LPREPL_IDIR_INFO_0;
typedef struct _REPL_IDIR_INFO_1 { LPWSTR rpid1_dirname; DWORD rpid1_state; LPWSTR rpid1_mastername; DWORD rpid1_last_update_time; DWORD rpid1_lockcount; DWORD rpid1_locktime;} REPL_IDIR_INFO_1, *PREPL_IDIR_INFO_1, *LPREPL_IDIR_INFO_1;

The replicator import directory functions are:
NetReplImportDirAdd_win32_NetReplImportDirAdd
NetReplImportDirDel_win32_NetReplImportDirDel
NetReplImportDirEnum_win32_NetReplImportDirEnum
NetReplImportDirGetInfo_win32_NetReplImportDirGetInfo
NetReplImportDirLock_win32_NetReplImportDirLock
+$#A Schedule Service Functions
Schedule service functions are used to submit and manage jobs to be executed at a given computer at a given time or times in future. Jobs can be commands and programs. Jobs can be managed at remote and local computers provided schedule service is running at a given computer.
Schedule service functions were designed to support old AT command functionality, but they extend this functionality so that new AT command may submit and manage jobs at remote as well as local computers. Schedule service functions are also known as "Job" and "AT command" functions.
Schedule service functions are job submittal (NetScheduleJobAdd_win32_NetScheduleJobAdd), job cancellation (NetScheduleJobDel_win32_NetScheduleJobDel), job enumeration (NetScheduleJobEnum_win32_NetScheduleJobEnum) and retrieving information about a particular job (NetScheduleJobGetInfo_win32_NetScheduleJobGetInfo). In order for these functions to succeed, a caller must have administrator's privilege at a computer where Schedule service is running.
Data Structures
AT_INFO data structure is used by NetScheduleJobAdd( ) to specify a job to be added, and by NetScheduleJobGetInfo( ) to return information about an already submitted job. AT_ENUM data structure is used by NetScheduleJobEnum( ) to enumerate and return information about an entire queue of already submitted jobs.
typedef struct _AT_INFO { DWORD JobTime; DWORD DaysOfMonth; UCHAR DaysOfWeek; UCHAR Flags; LPWSTR Command;} AT_INFO, *PAT_INFO, *LPAT_INFO;

typedef struct _AT_ENUM { DWORD JobId; DWORD JobTime; DWORD DaysOfMonth; UCHAR DaysOfWeek; UCHAR Flags; LPWSTR Command;} AT_ENUM, *PAT_ENUM, *LPAT_ENUM;

JobId
Job identifier of an already submitted job.
JobTime
Time of day when a job is scheduled to run. Time is a local time at a computer where Schedule service is running. Time is measured from midnight and is expressed in milliseconds.
DaysOfMonth
Bitmask for days of month when a job is scheduled to run. The bitmask is NULL if a job was scheduled to run only once at the first occurrence of JobTime. For each bit that is set in the bitmask a job will run at JobTime on a corresponding day of the month. Bit 0 corresponds to first day of the month, etc.
DaysOfWeek
Bitmask for days of week when this job is scheduled to run. The bitmask is NULL if a job was scheduled to run only once at the first occurrence of JobTime. For each bit that is set in the bitmask a job will run at JobTime on a corresponding day of the week. Bit 0 corresponds to Monday (first day of the week), etc.
Flags
Bitmask describing job properties. For job submission (NetScheduleJobAdd_win32_NetScheduleJobAdd) possible values are:
JOB_RUN_PERIODICALLY
If this flag bit is set then job runs on every day where corresponding bits in DaysOfMonth or DaysOfWeek are set. If this flag bit is clear then job runs only once for each bit that was set in DaysOfMonth and/or DaysOfWeek at the time of job submission.
JOB_ADD_CURRENT_DATE
When this flag bit is set then job will also execute at the first occurrence of JobTime at the computer where job is submitted to. In other words, setting this flag bit is equivalent to setting the corresponding day bit in DaysOfMonth bitmask.
For job information retrieval (NetScheduleJobEnum_win32_NetScheduleJobEnum and NetScheduleJobGetInfo_win32_NetScheduleJobGetInfo) possible values are:
JOB_RUN_PERIODICALLY
This flag bit is equal to the original value of this flag bit when a job was submitted.
JOB_EXEC_ERROR
This flag bit is set whenever Schedule service failed to execute successfully this job the last time this job was supposed to run.
JOB_RUNS_TODAY
This flag bit is set if JobId is larger than the current time of day at the computer where this job is queued at.
Command
Points to a Unicode string that contains the name of command, batch program or binary file to execute.

The schedule service functions are:
NetScheduleJobAdd_win32_NetScheduleJobAdd
NetScheduleJobDel_win32_NetScheduleJobDel
NetScheduleJobEnum_win32_NetScheduleJobEnum
NetScheduleJobGetInfo_win32_NetScheduleJobGetInfo
+$#A LAN Manager Functions Not Supported in Windows NT
The following section lists the 16-bit LAN Manager functions that are no longer supported in the Win32 API. In general, the rule we have followed is to not support anything that was marked as obsolete in LAN Manager 2.x. In addition, there are some functions that no longer make sense in Windows NT, and those have been removed as well. Another set of functions has been folded into the Windows NT base or Win32 API.
+$#A Functions with No 32-bit Net Equivalents
· NetAlertStop, NetAlertStart
· NetAuditWrite
· NetBiosClose, NetBiosEnum, NetBiosGetInfo, NetBiosOpen, NetBiosSubmit
· NetCharDev and NetCharDevQ
· NetErrorLogWrite
· NetLogonEnum
· NetHandleGetInfo levels 2 and 3
· NetMessageFileSend, NetMessageLogFileGet, NetMessageLogFileSet, NetMessageNameFwd, NetMessageNameUnFwd
· NetRemoteCopy, NetRemoteExec, NetRemoteMove
· NetServerAdminCommand
· NetWkstaSetUID

+$# Functions That Only Have Support for Remoting to Downlevel
· NetAccess functions
· NetAudit functions
· NetConfig functions
· NetError functions

+$#A Requests from 16-bit LAN Manager Clients
Windows NT provides support for most remote functions called from downlevel clients. However, the following calls are not supported when remoted from a downlevel client to a Windows NT server. In some of the following, there is a more current "2" version of the function that is supported (such as NetServerEnum_win32_NetServerEnum).
· NetFileEnum_win32_NetFileEnum
· NetFileGetInfo_win32_NetFileGetInfo
· NetFileClose_win32_NetFileClose
· NetServerAdminCommand
· NetAuditOpen
· NetAuditClear_win32_NetAuditClear
· NetErrorLogOpen
· NetErrorLogClear_win32_NetErrorLogClear
· NetMessageNameFwd
· NetMessageNameUnFwd
· NetMessageFileSend
· NetMessageLogFileSet
· NetMessageLogFileGet
· NetUserAdd_win32_NetUserAdd
· NetUserSetInfo_win32_NetUserSetInfo
· NetWkstaSetUID
· NetUseEnum_win32_NetUseEnum
· NetUseAdd_win32_NetUseAdd
· NetUseDel_win32_NetUseDel
· NetUseGetInfo_win32_NetUseGetInfo
· NetProfileSave
· NetProfileLoad
· NetStatisticsGet_win32_NetStatisticsGet
· NetStatisticsClear
· NetNetBiosEnum
· NetNetBiosGetInfo
· NetServerEnum_win32_NetServerEnum
· NetConfigGet2
· NetConfigGetAll2
· NetHandleGetInfo
· NetHandleSetInfo
· NetAuditRead_win32_NetAuditRead
· NetUserValidate2
· NetAccessCheck_win32_NetAccessCheck
· NetAlertRaise_win32_NetAlertRaise
· NetAlertStart
· NetAlertStop
· NetAuditWrite
· NetServiceStatus
· DosPrintDriverEnum
· DosPrintQProcessorEnum
· DosPrintPortEnum
· DosPrintDest
· NetConfigSet_win32_NetConfigSet

+$#A Calling 16-bit LAN Manager Servers
When an RPC-based function fails to connect to the appropriate interface, the client-side stub may attempt to initiate a down-level function request to the server selected. For most of the Windows networking functions specified in this document, and any API where the functionality and data formats are changed only for 32-bit usage, the conversion is straightforward. For components that offer new functionality the caller of the function should generally be aware of the destination type. When the new function offers a superset of the functionality of the down-level station the same function is used for both destinations, but the new function fields must have either a reserved value of an associated field to inform the conversion layer the field may be ignored if going downlevel. This is required so that a function caller is not misled as to the action performed when the function was called.

+$#AK Network DDE
The Micrsoft® Win32® application programming interface (API) provides network dynamic data exchange (network DDE) services.
+$#A About Network DDE
Dynamic data exchange lets a process establish data conversations with other processes executing on the same computer. Network DDE lets a process establish data conversations with other processes executing on other computers over a network.
+$#AK Network DDE Reference
The following functions and structures are associated with network dynamic data exchange.
+$#A Network DDE Functions
Use these functions to implement network DDE functionality:
NDdeGetErrorString_win32_NDdeGetErrorStringNDdeGetShareSecurity_win32_NDdeGetShareSecurityNDdeGetTrustedShare_win32_NDdeGetTrustedShareNDdeIsValidAppTopicList_win32_NDdeIsValidAppTopicListNDdeIsValidShareName_win32_NDdeIsValidShareNameNDdeSetShareSecurity_win32_NDdeSetShareSecurityNDdeSetTrustedShare_win32_NDdeSetTrustedShareNDdeShareAdd_win32_NDdeShareAddNDdeShareDel_win32_NDdeShareDelNDdeShareEnum_win32_NDdeShareEnumNDdeShareGetInfo_win32_NDdeShareGetInfoNDdeShareSetInfo_win32_NDdeShareSetInfoNDdeTrustedShareEnum_win32_NDdeTrustedShareEnum
+$#A Network DDE Structures
Use this structure to implement network DDE functionality:
NDDESHAREINFO_win32_NDDESHAREINFO_str

+$#AK Windows Sockets
+$#A Introduction to Windows Sockets
+$#A What is Windows Sockets?
The Windows Sockets documentation defines a network programming interface for Microsoft® Windows® based on the "socket" paradigm popularized in the Berkeley Software Distribution (BSD) from the University of California at Berkeley. It encompasses both familiar Berkeley socket style routines and a set of Windows-specific extensions designed to allow the programmer to take advantage of the message-driven nature of Windows.
The Windows Sockets documentation is intended to provide a single application programming interface (API) to which application developers can program and multiple network software vendors can conform. Furthermore, in the context of a particular version of Microsoft Windows, it defines a binary interface (ABI) such that an application written using the Windows Sockets API can work with a conformant protocol implementation from any network software vendor. This specification thus defines the library calls and associated semantics to which an application developer can program and which a network software vendor can implement.
Network software that conforms to this Windows Sockets specification will be considered "Windows Sockets compliant." Suppliers of interfaces that are Windows Sockets compliant shall be referred to as "Windows Sockets Suppliers." To be Windows Sockets compliant, a vendor must implement 100% of this Windows Sockets specification.
Applications capable of operating with any Windows Sockets-compliant protocol implementation will be considered to have a "Windows Sockets Interface" and will be referred to as "Windows Sockets Applications."
This version of the Windows Sockets specification defines and documents the use of the API in conjunction with the Internet Protocol Suite (IPS, generally referred to as TCP/IP). Specifically, all Windows Sockets implementations support both stream (TCP) and datagram (UDP) sockets.
While the use of this API with alternative protocol stacks is not precluded (and is expected to be the subject of future revisions of the specification), such usage is beyond the scope of this version of the specification.
+$#A Berkeley Sockets
The Windows Sockets Specification has been built on the Berkeley Sockets programming model, which is the de facto standard for TCP/IP networking. It is intended to provide a high degree of familiarity for programmers who are used to programming with sockets in UNIX and other environments, and to simplify the task of porting existing sockets-based source code. The Windows Sockets API is consistent with release 4.3 of the Berkeley Software Distribution (4.3BSD).
Portions of the Windows Sockets specification are derived from material that is Copyright © 1982-1986 by the Regents of the University of California. All rights are reserved. The Berkeley Software License Agreement specifies the terms and conditions for redistribution.
+$#A Microsoft Windows and Windows-Specific Extensions
This API is intended to be usable within all implementations and versions of Microsoft Windows from Microsoft Windows version 3.0 onwards. It thus provides for Windows Sockets implementations and Windows Sockets applications in both 16- and 32-bit operating environments.
Windows Sockets makes provisions for multithreaded Windows processes. A process contains one or more threads of execution. In the Windows 3.1 non-multithreaded world, a task corresponds to a process with a single thread. All references to threads in this document refer to actual "threads" in multithreaded Windows environments. In non-multithreaded environments (such as Windows 3.0), use of the term "thread" refers to a Windows process.
The Microsoft Windows extensions included in Windows Sockets are provided to allow application developers to create software that conforms to the Windows programming model. It is expected that this will facilitate the creation of robust and high-performance applications, and will improve the cooperative multitasking of applications within nonpreemptive versions of Windows. With the exception of WSAStartup_win32_WSAStartup and WSACleanup_win32_WSACleanup, their use is not mandatory.
+$#A The Status of This Specification
Windows Sockets is an independent specification that was created for the benefit of application developers and network vendors and, indirectly, computer users. Discussion of this documentation and suggested improvements continue and are welcomed. Such discussion occurs mainly via the Internet news group alt.winsock.
+$#A Programming with Sockets
+$#A Windows Sockets Stack Installation Checking
To detect the presence of one (or many) Windows Sockets implementations on a system, an application that has been linked with the Windows Sockets Import Library may simply call the WSAStartup_win32_WSAStartup function. A more sophisticated application can examine the $PATH environment variable and search for instances of Windows Sockets implementations (WINSOCK.DLL). For each instance, it can issue a LoadLibrary_win32_LoadLibrary call and use the WSAStartup function to discover implementation-specific data.
This version of the Windows Sockets specification does not attempt to address explicitly the issue of multiple concurrent Windows Sockets implementations. Nothing in the specification should be interpreted as restricting multiple Windows Sockets DLLs from being present and used concurrently by one or more Windows Sockets applications.
For further details of where to obtain Windows Sockets components, see Windows Sockets Components_win32_Windows_Sockets_Components.
+$#A Sockets
The material in this guide is derived from the document "An Advanced 4.3BSD Interprocess Communication Tutorial," by Samuel J. Leffler, Robert S. Fabry, William N. Joy, Phil Lapsley, Steve Miller, and Chris Torek.
+$#A Socket Basic Concepts
The basic building block for communication is the socket. A socket is an endpoint of communication to which a name can be bound. Each socket in use has a type and an associated process. Sockets exist within communication domains. A communication domain is an abstraction introduced to bundle common properties of threads communicating through sockets. Sockets typically exchange data only with sockets in the same domain (it may be possible to cross domain boundaries, but only if some translation process is performed). The Windows Sockets facilities support a single communication domain: the Internet domain, which is used by processes that communicate using the Internet Protocol Suite. (Future versions of this specification may include additional domains.)
Sockets are typed according to the communication properties visible to a user. Applications are presumed to communicate only between sockets of the same type, although there is nothing that prevents communication between sockets of different types should the underlying communication protocols support this.
Two types of sockets are currently available to a user. A stream socket provides for the bidirectional, reliable, sequenced, and unduplicated flow of data without record boundaries.
A datagram socket supports bidirectional flow of data that is not promised to be sequenced, reliable, or unduplicated. That is, a process receiving messages on a datagram socket may find messages duplicated, and possibly, in an order different from the order in which it was sent. An important characteristic of a datagram socket is that record boundaries in data are preserved. Datagram sockets closely model the facilities found in many contemporary packet-switched networks such as Ethernet.
+$#A Client-Server Model
The paradigm most commonly used in constructing distributed applications is the client-server model. In this scheme, client applications request services from a server application. This implies an asymmetry in establishing communication between the client and server.
The client and server require a well-known set of conventions before service may be rendered (and accepted). These conventions constitute a protocol that must be implemented at both ends of a connection. Depending on the situation, the protocol may be symmetric or asymmetric. In a symmetric protocol, either side may play the master or slave roles. In an asymmetric protocol, one side is immutably recognized as the master, with the other as the slave. An example of a symmetric protocol is the TELNET protocol used in the Internet for remote terminal emulation. An example of an asymmetric protocol is the Internet file transfer protocol, FTP. No matter whether the specific protocol used in obtaining a service is symmetric or asymmetric, when accessing a service there is a "client process" and a "server process."
A server application typically listens at a well-known address for service requests. That is, the server process remains dormant until a connection is requested by a client's connection to the server's address. At such a time, the server process "wakes up" and services the client, performing whatever appropriate actions the client requests of it. While connection-based services are the norm, some services are based on the use of datagram sockets.
+$#A Out-of-Band Data

Note The following discussion of out-of-band data, also referred to as TCP Urgent data, follows the model used in the Berkeley software distribution. Users and implementors should be aware of the fact that there are at present two conflicting interpretations of RFC 793 (in which the concept is introduced), and that the implementation of out-of-band data in the Berkeley Software Distribution does not conform to the Host Requirements laid down in RFC 1122. To minimize interoperability problems, application developers are advised not to use out-of-band data unless this is required in order to interoperate with an existing service. Windows Sockets suppliers are urged to document the out-of-band semantics (BSD or RFC 1122) that their product implements. It is beyond the scope of this specification to mandate a particular set of semantics for out-of-band data handling.

The stream socket abstraction includes the notion of "out of band" data. Out-of-band data is a logically independent transmission channel associated with each pair of connected stream sockets. Out-of-band data is delivered to the user independently of normal data. The abstraction defines that the out-of-band data facilities must support the reliable delivery of at least one out-of-band message at a time. This message may contain at least one byte of data, and at least one message may be pending delivery to the user at any one time. For communications protocols that support only in-band signaling (that is, the urgent data is delivered in sequence with the normal data), the system typically extracts the data from the normal data stream and stores it separately. This allows users to choose between receiving the urgent data in order and receiving it out of sequence without having to buffer all the intervening data. It is possible to "peek" at out-of-band data.
An application may prefer to process out-of-band data "in-line," as part of the normal data stream. This is achieved by setting the socket option SO_OOBINLINE (see setsockopt_win32_setsockopt). In this case, the application may wish to determine whether any of the unread data is "urgent" (the term usually applied to in-line out-of-band data). To facilitate this, the Windows Sockets implementation will maintain a logical "mark" in the data stream to indicate the point at which the out-of-band data was sent. An application can use the SIOCATMARK ioctlsocket_win32_ioctlsocket command to determine whether there is any unread data preceding the mark. For example, the application might use this to resynchronize with its peer by ensuring that all data up to the mark in the data stream is discarded when appropriate.
The WSAAsyncSelect_win32_WSAAsyncSelect function is particularly well suited to handling notification of the presence of out-of-band data.
+$#A Broadcasting
By using a datagram socket, it is possible to send broadcast packets on many networks supported by the system. The network itself must support broadcast: the system provides no simulation of broadcast in software. Broadcast messages can place a high load on a network, since they force every host on the network to service them. Consequently, the ability to send broadcast packets has been limited to sockets that are explicitly marked as allowing broadcasting. Broadcast is typically used for one of two reasons: it is needed to find a resource on a local network without prior knowledge of its address, or important functions such as routing require that information be sent to all accessible neighbors.
The destination address of the message to be broadcast depends on the network(s) on which the message is to be broadcast. The Internet domain supports a shorthand notation for broadcast on the local network, the address INADDR_BROADCAST. Received broadcast messages contain the sender's address and port, because datagram sockets must be bound before use.
Some types of network support the notion of different types of broadcast. For example, the IEEE 802.5 token ring architecture supports the use of link-level broadcast indicators, which control whether broadcasts are forwarded by bridges. The Windows Sockets specification does not provide any mechanism whereby an application can determine the type of underlying network, nor any way to control the semantics of broadcasting.
+$#A Byte Ordering
The Intel byte ordering is like that of the DEC VAX™, and therefore differs from the Internet and 68000-type processor byte ordering. Thus, care must be taken to ensure correct orientation.
Any reference to IP addresses or port numbers passed to or from a Windows Sockets function must be in network order. This includes the IP address and port fields of a struct sockaddr_in (but not the sin_family field).
Consider an application that typically contacts a server on the TCP port corresponding to the "time" service, but provides a mechanism for the user to specify that an alternative port is to be used. The port number returned by getservbyname_win32_getservbyname is already in network order, which is the format required when constructing an address, so no translation is required. However, if the user elects to use a different port, entered as an integer, the application must convert this from host order to network order (using the htons_win32_htons function) before using it to construct an address. Conversely, if the application is to display the number of the port within an address (returned using, for example, getpeername_win32_getpeername), the port number must be converted from network order to host order (using ntohs_win32_ntohs) before it can be displayed.
Since the Intel and Internet byte orders are different, the conversions described preceding are unavoidable. Application writers are cautioned that they should use the standard conversion functions provided as part of the Windows Sockets API rather than writing their own conversion code, since future implementations of Windows Sockets are likely to run on systems for which the host order is identical to the network byte order. Only applications that use the standard conversion functions are likely to be portable.
+$#A Socket Options
The socket options supported by Windows Sockets are listed in setsockopt_win32_setsockopt and getsockopt_win32_getsockopt. A Windows Sockets implementation must recognize all of these options, and (for getsockopt) return plausible values for each. The default value for each option is shown in the following table.
Value Type Meaning Default Note
SO_ACCEPTCONN BOOL Socket is in listen mode. FALSE unless a listen_win32_listen has been performed
SO_BROADCAST BOOL Socket is configured for the transmission of broadcast messages. FALSE
SO_DEBUG BOOL Debugging is enabled. FALSE (i)
SO_DONTLINGER BOOL If true, the SO_LINGER option is disabled. TRUE
SO_DONTROUTE BOOL Routing is disabled. FALSE (i)
SO_ERROR int Retrieve error status and clear. 0
SO_KEEPALIVE BOOL Keepalive messages are being sent. FALSE
SO_LINGER struct linger FAR * Returns the current linger options. l_onoff is 0
SO_OOBINLINE BOOL Out-of-band data is being received in the normal data stream. FALSE
SO_RCVBUF int Buffer size for recv operations. Implementation dependent (i)
SO_REUSEADDR BOOL The address to which this socket is bound can be used by others. FALSE
SO_SNDBUF int Buffer size for send operations. Implementation dependent (i)
SO_TYPE int The type of the socket (for example, SOCK_STREAM). As created using socket_win32_socket
TCP_NODELAY BOOL Disables the Nagle algorithm for send coalescing. Implementation dependent

Notes:
(i) An implementation may silently ignore this option on setsockopt_win32_setsockopt and return a constant value for getsockopt_win32_getsockopt, or it may accept a value for setsockopt and return the corresponding value in getsockopt without using the value in any way.
+$#A Database Files
The getXbyY and WSAAsyncGetXByY classes of routines are provided for retrieving network-specific information. (This specification uses the function name getXbyY to represent the set of routines gethostbyaddr_win32_gethostbyaddr, gethostbyname_win32_gethostbyname, and so on. Similarly WSAAsyncGetXByY represents WSAAsyncGetHostByAddr_win32_WSAAsyncGetHostByAddr, and so on.) The getXbyY routines were originally designed (in the first Berkeley UNIX releases) as mechanisms for looking up information in text databases. Although the information may be retrieved by the Windows Sockets implementation in different ways, a Windows Sockets application requests such information in a consistent manner through either the getXbyY or the WSAAsyncGetXByY class of routines.
+$#A Deviation from Berkeley Sockets
There are a few limited instances in which the Windows Sockets API has had to divert from strict adherence to the Berkeley conventions, usually because of difficulties of implementation in a Windows environment.
+$#A Socket Data Type and Error Values
A new data type, SOCKET, has been defined. The definition of this type was necessary for future enhancements to the Windows Sockets specification, such as being able to use sockets as file handles in Windows NT. Definition of this type also facilitates porting applications to a Win32 environment, because the type will automatically be promoted from 16 to 32 bits.
In UNIX, all handles, including socket handles, are small, non-negative integers, and some applications make assumptions that this will be true. Windows Sockets handles have no restrictions, other than that the value INVALID_SOCKET is not a valid socket. Socket handles may take any value in the range 0 to INVALID_SOCKET - 1.
Because the SOCKET type is unsigned, compiling existing source code from, for example, a UNIX environment may lead to compiler warnings about signed/unsigned data type mismatches.
This means, for example, that checking for errors when the socket_win32_socket and accept_win32_accept routines return should not be done by comparing the return value with - 1, or seeing if the value is negative (both approaches are common and legal in BSD). Instead, an application should use the manifest constant INVALID_SOCKET as defined in WINSOCK.H. For example:
TYPICAL BSD STYLE:
s = socket(...);

if (s == -1) /* or s < 0 */
{...}

PREFERRED STYLE:
s = socket(...);
if (s == INVALID_SOCKET)
{...}

+$#A select and FD_XXX
Because a SOCKET is no longer represented by the UNIX-style "small non-negative integer," the implementation of the select_win32_select function was changed in the Windows Sockets API. Each set of sockets is still represented by the fd_set type, but instead of being stored as a bitmask the set is implemented as an array of SOCKETs. To avoid potential problems, applications must adhere to the use of the FD_XXX macros to set, initialize, clear, and check the fd_set structures.
+$#A Error Codes - errno, h_errno & WSAGetLastError
Error codes set by the Windows Sockets implementation are not made available via the errno variable. Additionally, for the getXbyY class of functions, error codes are NOT made available via the h_errno variable. Instead, error codes are accessed by using the WSAGetLastError_win32_WSAGetLastError . This function is provided in Windows Sockets as a precursor (and eventually an alias) for the Win32 function GetLastError_win32_GetLastError@win32.hlp. This is intended to provide a reliable way for a thread in a multithreaded process to obtain per-thread error information.
For compatibility with BSD, an application may choose to include a line of the form:
#define errno WSAGetLastError

This will allow networking code that was written to use the global errno to work correctly in a single-threaded environment. There are, obviously, some drawbacks. If a source file includes code that inspects errno for both socket and nonsocket functions, this mechanism cannot be used. Furthermore, it is not possible for an application to assign a new value to errno. (In Windows Sockets the function WSASetLastError_win32_WSASetLastError can be used for this purpose.)
TYPICAL BSD STYLE:
r = recv(...);
if (r == -1
&& errno == EWOULDBLOCK)
{...}

PREFERRED STYLE:
r = recv(...);
if (r == -1 /* (but see below) */
&& WSAGetLastError == EWOULDBLOCK)
{...}

Although error constants consistent with 4.3 Berkeley Sockets are provided for compatibility purposes, applications should, where possible, use the "WSA" error code definitions. For example, a more accurate version of the preceding source code fragment is:
r = recv(...);
if (r == -1 /* (but see below) */
&& WSAGetLastError == WSAEWOULDBLOCK)
{...}

+$#A Pointers
All pointers used by applications with Windows Sockets should be FAR. To facilitate this, data type definitions such as LPHOSTENT are provided.
+$#A Renamed Functions
In two cases it was necessary to rename functions that are used in Berkeley Sockets to avoid clashes with other functions.
#close & closesocket
In Berkeley Sockets, sockets are represented by standard file descriptors, and so the close function can be used to close sockets as well as regular files. While nothing in Windows Sockets prevents an implementation from using regular file handles to identify sockets, nothing requires it either. Socket descriptors are not presumed to correspond to regular file handles, and file operations such as read, write, and close cannot be assumed to work correctly when applied to sockets. Sockets must be closed by using the closesocket_win32_closesocket function. Using the close function to close a socket is incorrect and the effects of doing so are undefined by this specification.
#ioctl & ioctlsocket
Various C-language run-time systems use the ioctl function for purposes unrelated to Windows Sockets. For this reason we have defined the function ioctlsocket_win32_ioctlsocket, which is used to handle socket functions that in the Berkeley Software Distribution are performed using ioctl and fcntl.
+$#A Blocking Routines and EINPROGRESS
Although blocking operations on sockets are supported under Windows Sockets, their use is strongly discouraged. Programmers who are constrained to use blocking mode ¾ for example, as part of an existing application that is to be ported ¾ should be aware of the semantics of blocking operations in Windows Sockets. For more details, see Blocking/Nonblocking and Data Volatility_win32_Blocking_Nonblocking_and_Data_Volatility.
+$#A Maximum Number of Sockets Supported
The maximum number of sockets supported by a particular Windows Sockets supplier is implementation specific. An application should make no assumptions about the availability of a certain number of sockets. This topic is addressed further in WSAStartup_win32_WSAStartup. However, the number of sockets supported by a particular implementation is independent of the issue of the maximum number of sockets an application can actually make use of.
The maximum number of sockets a Windows Sockets application can make use of is determined at compile time by the manifest constant FD_SETSIZE. This value is used in constructing the fd_set structures used in select_win32_select. The default value in WINSOCK.H is 64. If an application is designed to be capable of working with more than 64 sockets, the implementor should define the manifest FD_SETSIZE in every source file before including WINSOCK.H. One way of doing this is to include the definition within the compiler options in the makefile, for example adding -DFD_SETSIZE=128 as an option to the compiler command line for Microsoft C. It must be emphasized that defining FD_SETSIZE as a particular value has no effect on the actual number of sockets provided by a Windows Sockets implementation.
+$#A Include Files
For ease of portability of existing Berkeley sockets based source code, a number of standard Berkeley include files are supported. However, these Berkeley header files merely include the WINSOCK.H include file, and it is therefore sufficient (and recommended) that Windows Sockets application source files should simply include WINSOCK.H.
+$#A Return Values on Function Failure
The manifest constant SOCKET_ERROR is provided for checking function failure. Although use of this constant is not mandatory, it is recommended. The following example illustrates the use of the SOCKET_ERROR constant:
TYPICAL BSD STYLE:
r = recv(...);
if (r == -1 /* or r < 0 */
&& errno == EWOULDBLOCK)
{...}

PREFERRED STYLE:
r = recv(...);
if (r == SOCKET_ERROR && WSAGetLastError == WSAEWOULDBLOCK)
{...}

+$#A Raw Sockets
The Windows Sockets specification does not mandate that a Windows Sockets DLL support raw sockets, that is, sockets opened with SOCK_RAW. However, a Windows Sockets DLL is allowed and encouraged to supply raw socket support. A Windows Sockets-compliant application that needs to use raw sockets should attempt to open the socket with the socket_win32_socket call, and if it fails either attempt to use another socket type or indicate the failure to the user.
+$#A Windows Sockets in Multithreaded Versions of Windows
The Windows Sockets interface is designed to work for both single-threaded versions of Windows (such as Windows 3.1) and preemptive multithreaded versions of Windows (such as Windows NT). In a multithreaded environment the sockets interface is basically the same, but the author of a multithreaded application must be aware that it is the responsibility of the application, not the Windows Sockets implementation, to synchronize access to a socket between threads. This is the same rule that applies to other forms of I/O such as file I/O. Failure to synchronize calls on a socket leads to unpredictable results; for example if there are two simultaneous calls to send_win32_send, there is no guarantee as to the order in which the data will be sent.
Closing a socket in one thread that has an outstanding blocking call on the same socket in another thread will cause the blocking call to fail with WSAEINTR, just as if the operation were canceled. This also applies if there is a select_win32_select call outstanding and the application closes one of the sockets being selected.
There is no default blocking hook installed in preemptive multithreaded versions of Windows. This is because the computer will not be blocked if a single application is waiting for an operation to complete and hence not calling PeekMessage_win32_PeekMessage or GetMessage_win32_GetMessage, which cause the application to yield in nonpreemptive Windows. However, for backwards compatibility, the WSASetBlockingHook_win32_WSASetBlockingHook call is implemented in multithreaded versions of Windows, and applications whose behavior depends on the default blocking hook can install their own blocking hook that duplicates the default hook's semantics.
+$#AK Socket Functions
In each routine it is indicated that the header file WINSOCK.H must be included. The Berkeley-compatible header files that are supported are provided for compatibility purposes only, and each of them will simply include WINSOCK.H. The Windows header file WINDOWS.H is also needed, but WINSOCK.H will include it if necessary.
The Windows Sockets specification includes the following Berkeley-style socket functions:
accept_win32_accept* An incoming connection is acknowledged and associated with an immediately created socket. The original socket is returned to the listening state.
bind_win32_bind Assign a local name to an unnamed socket.
closesocket_win32_closesocket* Remove a socket from the per-process object reference table. Only blocks if SO_LINGER is set.
connect_win32_connect* Initiate a connection on the specified socket.
getpeername_win32_getpeername Retrieve the name of the peer connected to the specified socket.
getsockname_win32_getsockname Retrieve the current name for the specified socket
getsockopt_win32_getsockopt Retrieve options associated with the specified socket.
htonl_win32_htonl Convert a 32-bit quantity from host byte order to network byte order.
htons_win32_htons Convert a 16-bit quantity from host byte order to network byte order.
inet_addr_win32_inet_addr Converts a character string representing a number in the Internet standard "." notation to an Internet address value.
inet_ntoa_win32_inet_ntoa Converts an Internet address value to an ASCII string in "." notation (for example, "a.b.c.d").
ioctlsocket_win32_ioctlsocket Provide control for sockets.
listen_win32_listen Listen for incoming connections on a specified socket.
ntohl_win32_ntohl Convert a 32-bit quantity from network byte order to host byte order.
ntohs_win32_ntohs Convert a 16-bit quantity from network byte order to host byte order.
recv_win32_recv* Receive data from a connected socket.
recvfrom_win32_recvfrom* Receive data from either a connected or unconnected socket.
select_win32_select* Perform synchronous I/O multiplexing.
send_win32_send* Send data to a connected socket.
sendto_win32_sendto* Send data to either a connected or unconnected socket.
setsockopt_win32_setsockopt Store options associated with the specified socket.
shutdown_win32_shutdown Shut down part of a full-duplex connection.
socket_win32_socket Create an endpoint for communication and return a socket.
* The function can block if acting on a blocking socket.

+$#A Blocking/Nonblocking and Data Volatility
One major issue in porting applications from a Berkeley sockets environment to a Windows environment involves "blocking"; that is, invoking a function that does not return until the associated operation is completed. The problem arises if the operation takes an arbitrarily long time to complete: an obvious example is a recv_win32_recv that may block until data has been received from the peer system. The default behavior within the Berkeley sockets model is for a socket to operate in a blocking mode unless the programmer explicitly requests that operations be treated as nonblocking.

Note It is strongly recommended that programmers use the nonblocking (asynchronous) operations if at all possible, because they work significantly better within the nonpreemptive Windows environment. Use blocking operations only if absolutely necessary, and carefully read and understand this topic if you must use blocking operations.

Even on a blocking socket, some operations (for example, bind_win32_bind, getsockopt_win32_getsockopt, getpeername_win32_getpeername) can be completed immediately. For such operations there is no difference between blocking and nonblocking operation. Other operations (for example, recv_win32_recv) may be completed immediately or may take an arbitrary time to complete, depending on various transport conditions. When applied to a blocking socket, these operations are referred to as blocking operations. All functions that can block are listed with an asterisk in the tables in Socket Functions_win32_Socket_Functions and Socket Database Functions_win32_Socket_Database_Functions.
Within a Windows Sockets implementation, a blocking operation that cannot be completed immediately is handled as follows. The DLL initiates the operation, and then enters a loop in which it dispatches any Windows messages (yielding the processor to another thread if necessary) and then checks for the completion of the Windows Sockets function. If the function has completed, or if WSACancelBlockingCall_win32_WSACancelBlockingCall has been invoked, the blocking function completes with an appropriate result. Refer to WSASetBlockingHook_win32_WSASetBlockingHook for a complete description of this mechanism, including pseudocode for the various functions.
If a Windows message is received for a process for which a blocking operation is in progress, there is a risk that the application will attempt to issue another Windows Sockets call. Because of the difficulty of managing this condition safely, the Windows Sockets specification does not support such application behavior. Two functions are provided to assist you in this situation. WSAIsBlocking_win32_WSAIsBlocking may be called to determine whether or not a blocking Windows Sockets call is in progress. WSACancelBlockingCall_win32_WSACancelBlockingCall may be called to cancel an in-progress blocking call, if any. Any other Windows Sockets function that is called in this situation will fail with the error WSAEINPROGRESS. It should be emphasized that this restriction applies to both blocking and nonblocking operations.
Although this mechanism is sufficient for simple applications, it cannot support the complex message-dispatching requirements of more advanced applications (for example, those using the MDI model). For such applications, Windows Sockets includes the function WSASetBlockingHook_win32_WSASetBlockingHook, which allows the programmer to define a special function that will be called instead of the default message dispatch function previously described.
The Windows Sockets DLL calls the blocking hook only if all of the following are true: the function is one that is defined as being able to block, the specified socket is a blocking socket, and the request cannot be completed immediately. (A socket is set to blocking by default, but the IOCTL FIONBIO and WSAAsyncSelect_win32_WSAAsyncSelect both set a socket to nonblocking mode.) If an application uses only nonblocking sockets and uses the WSAAsyncSelect and/or the WSAAsyncGetXByY routines instead of select_win32_select and the getXbyY routines, then the blocking hook will never be called and the application does not need to handle reentrancy.
If an application invokes an asynchronous or nonblocking operation that takes a pointer to a memory object (for example, a buffer, or a global variable) as an argument, the application must ensure that the object is available to the Windows Sockets implementation throughout the operation. The application must not invoke any Windows function that might affect the mapping or addressability of the memory involved. In a multithreaded system, the application is also responsible for coordinating access to the object using appropriate synchronization mechanisms. A Windows Sockets implementation cannot, and will not, address these issues. The possible consequences of failing to observe these rules are beyond the scope of this specification.
+$#A Socket Database Functions
The Windows Sockets specification defines the following database functions. As noted earlier, a Windows Sockets supplier may choose to implement these in a manner that does not depend on local database files. The pointer returned by certain database functions such as gethostbyname_win32_gethostbyname points to a structure allocated by the Windows Sockets library. The data pointed to is volatile and is good only until the next Windows Sockets function call from that thread. Additionally, the application must never attempt to modify this structure or to free any of its components. Only one copy of this structure is allocated for a thread, and so the application should copy any information it needs before issuing any other Windows Sockets function calls.
gethostbyaddr_win32_gethostbyaddr* Retrieves the name(s) and address corresponding to a network address.
gethostbyname_win32_gethostbyname* Retrieves the name(s) and address corresponding to a host name.
gethostname_win32_gethostname Retrieves the name of the local host.
getprotobyname_win32_getprotobyname* Retrieves the protocol name and number corresponding to a protocol name.
getprotobynumber_win32_getprotobynumber* Retrieves the protocol name and number corresponding to a protocol number.
getservbyname_win32_getservbyname* Retrieves the service name and port corresponding to a service name.
getservbyport_win32_getservbyport* Retrieves the service name and port corresponding to a port.
* The function can block under some circumstances.

+$#A Microsoft Windows-Specific Extension Functions
Windows Sockets provides a number of extensions to the standard set of Berkeley Sockets functions. Principally, these extended functions allow message-based, asynchronous access to network events. While use of this extended function set is not mandatory for socket-based programming (with the exception of WSAStartup_win32_WSAStartup and WSACleanup_win32_WSACleanup), it is recommended for conformance with the Microsoft Windows programming paradigm.
The following functions provide asynchronous versions of the standard Berkeley getXbyY functions. For example, the WSAAsyncGetHostByName_win32_WSAAsyncGetHostByName function provides an asynchronous message-based implementation of the standard Berkeley gethostbyname_win32_gethostbyname function.
WSAAsyncGetHostByAddr_win32_WSAAsyncGetHostByAddr
WSAAsyncGetHostByName_win32_WSAAsyncGetHostByName
WSAAsyncGetProtoByName_win32_WSAAsyncGetProtoByName
WSAAsyncGetProtoByNumber_win32_WSAAsyncGetProtoByNumber
WSAAsyncGetServByName_win32_WSAAsyncGetServByName
WSAAsyncGetServByPort_win32_WSAAsyncGetServByPort

WSAAsyncSelect_win32_WSAAsyncSelect Performs asynchronous version of select_win32_select.
WSACancelAsyncRequest_win32_WSACancelAsyncRequest Cancels an outstanding instance of a WSAAsyncGetXByY function.
WSACancelBlockingCall_win32_WSACancelBlockingCall Cancels an outstanding "blocking" function call.
WSACleanup_win32_WSACleanup Signs off from the underlying Windows Sockets DLL.
WSAGetLastError_win32_WSAGetLastError Obtains details of last Windows Sockets function error.
WSAIsBlocking_win32_WSAIsBlocking Determines if the underlying Windows Sockets DLL is already blocking an existing call for this thread.
WSASetBlockingHook_win32_WSASetBlockingHook "Hooks" the blocking method used by the underlying Windows Sockets implementation.
WSASetLastError_win32_WSASetLastError Sets the error to be returned by a subsequent call to WSAGetLastError.
WSAStartup_win32_WSAStartup Initializes the underlying Windows Sockets DLL.
WSAUnhookBlockingHook_win32_WSAUnhookBlockingHook Restores the original blocking function.

+$#A Asynchronous Select Mechanism
The WSAAsyncSelect_win32_WSAAsyncSelect function allows an application to register an interest in one or many network events. This function is provided to supersede the need to do polled network I/O. Any situation in which select_win32_select or nonblocking I/O routines (such as send_win32_send and recv_win32_recv) are either already used or are being considered is usually a candidate for the WSAAsyncSelect function. When declaring interest in such condition(s), you supply a window handle to be used for notification. The corresponding window then receives message-based notification of the conditions in which you declared an interest.
WSAAsyncSelect allows interest to be declared in the following conditions for a particular socket:
· Socket readiness for reading
· Socket readiness for writing
· Out-of-band data ready for reading
· Socket readiness for accepting incoming connection
· Completion of nonblocking connect operation
· Connection closure

+$#A Asynchronous Support Routines
The asynchronous "database" functions allow applications to request information in an asynchronous manner. Some network implementations or configurations perform network-based operations to resolve such requests. The WSAAsyncGetXByY functions allow application developers to request services that would otherwise block the operation of the whole Windows environment if the standard Berkeley function were used. The WSACancelAsyncRequest_win32_WSACancelAsyncRequest function allows an application to cancel any outstanding asynchronous request.
+$#A Hooking Blocking Methods
As noted in Blocking/Nonblocking and Data Volatility_win32_Blocking_Nonblocking_and_Data_Volatility, Windows Sockets implements blocking operations in such a way that Windows message processing can continue, which may result in the application that issued the call receiving a Windows message. In certain situations, an application may need to influence or change the way in which this pseudo-blocking process is implemented. The WSASetBlockingHook_win32_WSASetBlockingHook function provides the ability to substitute a named function that the Windows Sockets implementation is to use when relinquishing the processor during a "blocking" operation.
+$#A Error Handling
For compatibility with thread-based environments, details of function errors are obtained through the WSAGetLastError_win32_WSAGetLastError function. Although the accepted Berkeley-style mechanism for obtaining socket-based network errors is by using errno, this mechanism cannot guarantee the integrity of an error ID in a multithreaded environment. WSAGetLastError allows you to retrieve an error code on a per-thread basis.
WSAGetLastError returns error codes that avoid conflict with standard Microsoft C error codes. Certain error codes returned by certain Windows Sockets routines fall into the standard range of error codes as defined by Microsoft C. If you are NOT using an application development environment that defines error codes consistent with Microsoft C, you should use the Windows Sockets error codes prefixed by "WSA" to ensure accurate error code detection.
Note that this specification defines a recommended set of error codes, and lists the possible errors that may be returned as a result of each function. It may be the case in some implementations that other Windows Sockets error codes will be returned in addition to those listed, and applications should be prepared to handle errors other than those enumerated under each function description. However, a Windows Sockets implementation must not return any value that is not enumerated in the table of legal Windows Sockets errors given in Error Codes_win32_WSAGetLastError_Error_Codes.
+$# Accessing a Windows Sockets DLL from an Intermediate DLL (16-bit Applications)
A Windows Sockets DLL can be accessed both directly from an application and through an "intermediate" DLL. An example of such an intermediate DLL would be a virtual network API layer that supports generalized network functionality for applications and uses Windows Sockets. Such a DLL could be used by several applications simultaneously, and the DLL must take special precautions with respect to the WSAStartup_win32_WSAStartup and WSACleanup_win32_WSACleanup calls to ensure that these routines are called in the context of each task that will make Windows Sockets calls. This is because the Windows Sockets DLL will need a call to WSAStartup for each task in order to set up task-specific data structures, and a call to WSACleanup to free any resources allocated for the task.
There are (at least) two ways to accomplish this. The simplest method is for the intermediate DLL to have calls similar to WSAStartup and WSACleanup that applications call as appropriate. The DLL would then call WSAStartup or WSACleanup from within these routines. Another mechanism is for the intermediate DLL to build a table of task handles, which are obtained from the GetCurrentTask_win32_GetCurrentTask Windows function, and at each entry point into the intermediate DLL check whether WSAStartup has been called for the current task, then call WSAStartup if necessary.
If a DLL makes a blocking call and does not install its own blocking hook, then the DLL author must be aware that control may be returned to the application either by an application-installed blocking hook or by the default blocking hook. Thus, it is possible that the application will cancel the DLL's blocking operation via WSACancelBlockingCall_win32_WSACancelBlockingCall. If this occurs, the DLL's blocking operation will fail with the error code WSAEINTR, and the DLL must return control to the calling task as quickly as possible, because the user has likely pressed a Cancel or Close button and the task has requested control of the CPU. It is recommended that DLLs that make blocking calls install their own blocking hooks with WSASetBlockingHook_win32_WSASetBlockingHook to prevent unforeseen interactions between the application and the DLL.
Note that this is not necessary for DLLs in Windows NT because of its different process and DLL structure. Under Windows NT, the intermediate DLL could simply call WSAStartup_win32_WSAStartup in its DLL initialization routine, which is called whenever a new process which uses the DLL starts.
+$# Internal Use of Messages by Windows Sockets Implementations
To implement Windows Sockets purely as a DLL, it may be necessary for the DLL to post messages internally for communication and timing. This is perfectly legal; however, a Windows Sockets DLL must not post messages to a window handle opened by a client application except for those messages requested by the application. A Windows Sockets DLL that needs to use messages for its own purposes must open a hidden window and post any necessary messages to the handle for that window.
+$#A Private API Interfaces
The WINSOCK.DEF file in DLL Ordinal Numbers_win32_DLL_Ordinal_Numbers lists the ordinals defined for the Windows Sockets functions. In addition to the ordinal values listed, all ordinals 999 and below are reserved for future Windows Sockets use. It may be convenient for a Windows Sockets implementation to export additional, private interfaces from the Windows Sockets DLL. This is perfectly acceptable, as long as the ordinals for these exports are above 1000. Note that any application that uses a particular Windows Sockets DLL's private functions will most likely not work on any other vendor's Windows Sockets implementation. Only the functions defined in this document are guaranteed to be present in every Windows Sockets implementation.
If an application uses private interfaces of a particular vendor's Windows Sockets DLL, it is recommended that the DLL not be statically linked with the application but rather dynamically loaded with the Windows routines LoadLibrary_win32_LoadLibrary and GetProcAddress_win32_GetProcAddress. This allows the application to give an informative error message if it is run on a system with a Windows Sockets DLL that does not support the same set of extended functionality.
+$#A Error Codes and Header Files
The following topics list the error codes and header files.
Error Codes_win32_WSAGetLastError_Error_Codes
Header Files_win32_Header_Files
+$#A Error Codes
The following is a list of possible error codes returned by the WSAGetLastError_win32_WSAGetLastError function, along with their explanations. The error numbers are consistently set across all Windows Sockets-compliant implementations.
Windows Sockets code Berkeley equivalent Error Interpretation
WSAEINTR EINTR 10004 As in standard C
WSAEBADF EBADF 10009 As in standard C
WSAEACCES EACCES 10013 As in standard C
WSAEDISCON None 10101 The message terminated gracefully. Used only for message-oriented protocols.
WSAEFAULT EFAULT 10014 As in standard C
WSAEINVAL EINVAL 10022 As in standard C
WSAEMFILE EMFILE 10024 As in standard C
WSAEWOULDBLOCK EWOULDBLOCK 10035 As in BSD
WSAEINPROGRESS EINPROGRESS 10036 This error is returned if anyWindows Sockets function is called while a blocking function is in progress.
WSAEALREADY EALREADY 10037 As in BSD
WSAENOTSOCK ENOTSOCK 10038 As in BSD
WSAEDESTADDRREQ EDESTADDRREQ 10039 As in BSD
WSAEMSGSIZE EMSGSIZE 10040 As in BSD
WSAEPROTOTYPE EPROTOTYPE 10041 As in BSD
WSAENOPROTOOPT ENOPROTOOPT 10042 As in BSD
WSAEPROTONOSUPPORT EPROTONOSUPPORT 10043 As in BSD
WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT 10044 As in BSD
WSAEOPNOTSUPP EOPNOTSUPP 10045 As in BSD
WSAEPFNOSUPPORT EPFNOSUPPORT 10046 As in BSD
WSAEAFNOSUPPORT EAFNOSUPPORT 10047 As in BSD
WSAEADDRINUSE EADDRINUSE 10048 As in BSD
WSAEADDRNOTAVAIL EADDRNOTAVAIL 10049 As in BSD
WSAENETDOWN ENETDOWN 10050 As in BSD. This error may be reported at any time if the Windows Sockets implementation detects an underlying failure.
WSAENETUNREACH ENETUNREACH 10051 As in BSD
WSAENETRESET ENETRESET 10052 As in BSD
WSAECONNABORTED ECONNABORTED 10053 As in BSD
WSAECONNRESET ECONNRESET 10054 As in BSD
WSAENOBUFS ENOBUFS 10055 As in BSD
WSAEISCONN EISCONN 10056 As in BSD
WSAENOTCONN ENOTCONN 10057 As in BSD
WSAESHUTDOWN ESHUTDOWN 10058 As in BSD
WSAETOOMANYREFS ETOOMANYREFS 10059 As in BSD
WSAETIMEDOUT ETIMEDOUT 10060 As in BSD
WSAECONNREFUSED ECONNREFUSED 10061 As in BSD
WSAELOOP ELOOP 10062 As in BSD
WSAENAMETOOLONG ENAMETOOLONG 10063 As in BSD
WSAEHOSTDOWN EHOSTDOWN 10064 As in BSD
WSAEHOSTUNREACH EHOSTUNREACH 10065 As in BSD
WSASYSNOTREADY None 10091 Returned by WSAStartup_win32_WSAStartup, indicating that the network subsystem is unusable.
WSAVERNOTSUPPORTED None 10092 Returned by WSAStartup, indicating that the Windows Sockets DLL cannot support this app.
WSANOTINITIALISED None 10093 Returned by any function except WSAStartup_win32_WSAStartup, indicating that a successful WSAStartup, has not yet been performed.
WSAHOST_NOT_FOUND HOST_NOT_FOUND 11001 As in BSD
WSATRY_AGAIN TRY_AGAIN 11002 As in BSD
WSANO_RECOVERY NO_RECOVERY 11003 As in BSD
WSANO_DATA NO_DATA 11004 As in BSD

The first set of definitions is present to resolve contentions between standard C error codes, which may be defined inconsistently between various C compilers.
The second set of definitions provides Windows Sockets versions of regular Berkeley Sockets error codes.
The third set of definitions consists of extended Windows Sockets-specific error codes.
The fourth set of errors are returned by Windows Sockets getXbyY and WSAAsyncGetXByY functions, and correspond to the errors that in Berkeley software would be returned in the h_errno variable. They correspond to various failures that may be returned by the Domain Name Service. If the Windows Sockets implementation does not use the DNS, it will use the most appropriate code. In general, a Windows Sockets application should interpret WSAHOST_NOT_FOUND and WSANO_DATA as indicating that the key (name, address, and so on) was not found, while WSATRY_AGAIN and WSANO_RECOVERY suggest that the name service itself is nonoperational.
The error numbers are derived from the WINSOCK.H header file and are based on the fact that Windows Sockets error numbers are computed by adding 10000 to the "normal" Berkeley error number.
Note that this table does not include all of the error codes defined in WINSOCK.H. This is because it includes only errors that might reasonably be returned by a Windows Sockets implementation. WINSOCK.H, on the other hand, includes a full set of BSD definitions to ensure compatibility with ported software.
+$#A Header Files
+$#A Berkeley Header Files
A Windows Sockets supplier who provides a development kit to support the development of Windows Sockets applications must supply a set of vestigial header files with names that match a number of the header files in the Berkeley software distribution. These files are provided for source code compatibility only, and each consists of three lines:
#ifndef _WINSOCKAPI_
#include <winsock.h>
#endif

The header files provided for compatibility are:
NETDB.HARPA/INET.HSYS/TIME.HSYS/SOCKET.HNETINET/IN.H
The file WINSOCK.H contains all of the type and structure definitions, constants, macros, and function prototypes used by the Windows Sockets specification. You may choose to ignore the compatibility headers and include WINSOCK.H in each source file.
+$#A Windows Sockets Header File - WINSOCK.H
The WINSOCK.H header file includes a number of types and definitions from the standard Windows header file WINDOWS.H. The WINDOWS.H file in the Windows 3.0 SDK (Software Developer's Kit) lacks a #include guard, so if you need to include WINDOWS.H as well as WINSOCK.H, you should define the symbol _INC_WINDOWS before #including WINSOCK.H, as follows:
#include <windows.h>
#define _INC_WINDOWS
#include <winsock.h>

Users of the SDK for Windows 3.1 and later need not do this.
A Windows Sockets DLL vendor must not make any modifications to this header file that could impact binary compatibility of Windows Sockets applications. The constant values, function parameters, return codes, and the like must remain consistent across all Windows Sockets DLL vendors.
/* WINSOCK.H--definitions to be used with the WINSOCK.DLL
*
* This header file corresponds to version 1.1 of the Windows Sockets specification.
*
* This file includes parts which are Copyright (c) 1982-1986 Regents
* of the University of California. All rights reserved. The
* Berkeley Software License Agreement specifies the terms and
* conditions for redistribution.
*/
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_
/*
* Pull in WINDOWS.H if necessary
*/
#ifndef _INC_WINDOWS
#include <windows.h>
#endif /* _INC_WINDOWS */
/*
* Basic system type definitions, taken from the BSD file sys/types.h.
*/
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
/*
* The new type to be used in all
* instances which refer to sockets.
*/
typedef u_int SOCKET;
/*
* Select uses arrays of SOCKETs. These macros manipulate such
* arrays. FD_SETSIZE may be defined by the user before including
* this file, but the default here should be >= 64.
*
* CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE
* INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE.
*/
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
typedef struct fd_set {
u_short fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *);
#define FD_CLR(fd, set) do { \
u_int __i; \
for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \
while (__i < ((fd_set FAR *)(set))->fd_count-1) { \
((fd_set FAR *)(set))->fd_array[__i] = \
((fd_set FAR *)(set))->fd_array[__i+1]; \
__i++; \
} \
((fd_set FAR *)(set))->fd_count--; \
break; \
} \
} \
} while(0)
#define FD_SET(fd, set) do { \
if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) \
((fd_set FAR *)(set))->fd_array[((fd_set FAR *)(set))->fd_count++]=fd;\
} while(0)
#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)fd, (fd_set FAR *)set)
/*
* Structure used in select call, taken from the BSD file sys/time.h.
*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
/*
* Operations on timevals.
*
* NB: timercmp does not work for >= or <=.
*/
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
((tvp)->tv_sec cmp (uvp)->tv_sec || \
(tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
/*
* Commands for ioctlsocket, taken from the BSD file fcntl.h.
*
*
* Ioctl's have the command encoded in the lower word,
* and the size of any in or out parameters in the upper
* word. The high 2 bits of the upper word are used
* to encode the in/out status of the parameter; for now
* we restrict parameters to at most 128 bytes.
*/
#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */
#define IOC_VOID 0x20000000 /* no parameters */
#define IOC_OUT 0x40000000 /* copy out parameters */
#define IOC_IN 0x80000000 /* copy in parameters */
#define IOC_INOUT (IOC_IN|IOC_OUT)
/* 0x20000000 distinguishes new &
old ioctl's */
#define _IO(x,y) (IOC_VOID|(x<<8)|y)
#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)
#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)
#define FIONREAD _IOR('f', 127, u_long) /* get # bytes to read */
#define FIONBIO _IOW('f', 126, u_long) /* set/clear non-blocking i/o */
#define FIOASYNC _IOW('f', 125, u_long) /* set/clear async i/o */
/* Socket I/O Controls */
#define SIOCSHIWAT _IOW('s', 0, u_long) /* set high watermark */
#define SIOCGHIWAT _IOR('s', 1, u_long) /* get high watermark */
#define SIOCSLOWAT _IOW('s', 2, u_long) /* set low watermark */
#define SIOCGLOWAT _IOR('s', 3, u_long) /* get low watermark */
#define SIOCATMARK _IOR('s', 7, u_long) /* at oob mark? */
/*
* Structures returned by network database library, taken from the
* BSD file netdb.h. All addresses are supplied in host order, and
* returned in network order (suitable for use in system calls).
*/
struct hostent {
char FAR * h_name; /* official name of host */
char FAR * FAR * h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};
/*
* It is assumed here that a network number
* fits in 32 bits.
*/
struct netent {
char FAR * n_name; /* official name of net */
char FAR * FAR * n_aliases; /* alias list */
short n_addrtype; /* net address type */
u_long n_net; /* network # */
};
struct servent {
char FAR * s_name; /* official service name */
char FAR * FAR * s_aliases; /* alias list */
short s_port; /* port # */
char FAR * s_proto; /* protocol to use */
};
struct protoent {
char FAR * p_name; /* official protocol name */
char FAR * FAR * p_aliases; /* alias list */
short p_proto; /* protocol # */
};
/*
* Constants and structures defined by the Internet system,
* Per RFC 790, September 1981, taken from the BSD file netinet/in.h.
*/
/*
* Protocols
*/
#define IPPROTO_IP 0 /* dummy for IP */
#define IPPROTO_ICMP 1 /* control message protocol */
#define IPPROTO_GGP 2 /* gateway^2 (deprecated) */
#define IPPROTO_TCP 6 /* tcp */
#define IPPROTO_PUP 12 /* pup */
#define IPPROTO_UDP 17 /* user datagram protocol */
#define IPPROTO_IDP 22 /* xns idp */
#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */
#define IPPROTO_RAW 255 /* raw IP packet */
#define IPPROTO_MAX 256
/*
* Port/socket numbers: network standard functions
*/
#define IPPORT_ECHO 7
#define IPPORT_DISCARD 9
#define IPPORT_SYSTAT 11
#define IPPORT_DAYTIME 13
#define IPPORT_NETSTAT 15
#define IPPORT_FTP 21
#define IPPORT_TELNET 23
#define IPPORT_SMTP 25
#define IPPORT_TIMESERVER 37
#define IPPORT_NAMESERVER 42
#define IPPORT_WHOIS 43
#define IPPORT_MTP 57
/*
* Port/socket numbers: host specific functions
*/
#define IPPORT_TFTP 69
#define IPPORT_RJE 77
#define IPPORT_FINGER 79
#define IPPORT_TTYLINK 87
#define IPPORT_SUPDUP 95
/*
* UNIX TCP sockets
*/
#define IPPORT_EXECSERVER 512
#define IPPORT_LOGINSERVER 513
#define IPPORT_CMDSERVER 514
#define IPPORT_EFSSERVER 520
/*
* UNIX UDP sockets
*/
#define IPPORT_BIFFUDP 512
#define IPPORT_WHOSERVER 513
#define IPPORT_ROUTESERVER 520
/* 520+1 also used */
/*
* Ports < IPPORT_RESERVED are reserved for
* privileged processes (e.g. root).
*/
#define IPPORT_RESERVED 1024
/*
* Link numbers
*/
#define IMPLINK_IP 155
#define IMPLINK_LOWEXPER 156
#define IMPLINK_HIGHEXPER 158
/*
* Internet address (old style... should be updated)
*/
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
/* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2
/* host on imp */
#define s_net S_un.S_un_b.s_b1
/* network */
#define s_imp S_un.S_un_w.s_w2
/* imp */
#define s_impno S_un.S_un_b.s_b4
/* imp # */
#define s_lh S_un.S_un_b.s_b3
/* logical host */
};
/*
* Definitions of bits in internet address integers.
* On subnets, the decomposition of addresses to host and net parts
* is done according to subnet mask, not the masks here.
*/
#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
#define IN_CLASSA_NET 0xff000000
#define IN_CLASSA_NSHIFT 24
#define IN_CLASSA_HOST 0x00ffffff
#define IN_CLASSA_MAX 128
#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
#define IN_CLASSB_NET 0xffff0000
#define IN_CLASSB_NSHIFT 16
#define IN_CLASSB_HOST 0x0000ffff
#define IN_CLASSB_MAX 65536
#define IN_CLASSC(i) (((long)(i) & 0xc0000000) == 0xc0000000)
#define IN_CLASSC_NET 0xffffff00
#define IN_CLASSC_NSHIFT 8
#define IN_CLASSC_HOST 0x000000ff
#define INADDR_ANY (u_long)0x00000000
#define INADDR_LOOPBACK 0x7f000001
#define INADDR_BROADCAST (u_long)0xffffffff
#define INADDR_NONE 0xffffffff
/*
* Socket address, internet style.
*/
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
#define WSADESCRIPTION_LEN 256
#define WSASYS_STATUS_LEN 128
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA;
typedef WSADATA FAR *LPWSADATA;
/*
* Options for use with [gs]etsockopt at the IP level.
*/
#define IP_OPTIONS 1 /* set/get IP per-packet options */
/*
* Definitions related to sockets: types, address families, options,
* taken from the BSD file sys/socket.h.
*/
/*
* This is used instead of -1, since the
* SOCKET type is unsigned.
*/
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
/*
* Types
*/
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#define SOCK_RDM 4 /* reliably-delivered message */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
/*
* Option flags per-socket.
*/
#define SO_DEBUG 0x0001 /* turn on debugging info recording */
#define SO_ACCEPTCONN 0x0002 /* socket has had listen */
#define SO_REUSEADDR 0x0004 /* allow local address reuse */
#define SO_KEEPALIVE 0x0008 /* keep connections alive */
#define SO_DONTROUTE 0x0010 /* just use interface addresses */
#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
#define SO_LINGER 0x0080 /* linger on close if data present */
#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
#define SO_DONTLINGER (u_int)(~SO_LINGER)
/*
* Additional options.
*/
#define SO_SNDBUF 0x1001 /* send buffer size */
#define SO_RCVBUF 0x1002 /* receive buffer size */
#define SO_SNDLOWAT 0x1003 /* send low-water mark */
#define SO_RCVLOWAT 0x1004 /* receive low-water mark */
#define SO_SNDTIMEO 0x1005 /* send timeout */
#define SO_RCVTIMEO 0x1006 /* receive timeout */
#define SO_ERROR 0x1007 /* get error status and clear */
#define SO_TYPE 0x1008 /* get socket type */
/*
* TCP options.
*/
#define TCP_NODELAY 0x0001
/*
* Address families.
*/
#define AF_UNSPEC 0 /* unspecified */
#define AF_UNIX 1 /* local to host (pipes, portals) */
#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
#define AF_IMPLINK 3 /* arpanet imp addresses */
#define AF_PUP 4 /* pup protocols: e.g. BSP */
#define AF_CHAOS 5 /* mit CHAOS protocols */
#define AF_NS 6 /* XEROX NS protocols */
#define AF_ISO 7 /* ISO protocols */
#define AF_OSI AF_ISO /* OSI is ISO */
#define AF_ECMA 8 /* european computer manufacturers */
#define AF_DATAKIT 9 /* datakit protocols */
#define AF_CCITT 10 /* CCITT protocols, X.25 etc */
#define AF_SNA 11 /* IBM SNA */
#define AF_DECnet 12 /* DECnet */
#define AF_DLI 13 /* Direct data link interface */
#define AF_LAT 14 /* LAT */
#define AF_HYLINK 15 /* NSC Hyperchannel */
#define AF_APPLETALK 16 /* AppleTalk */
#define AF_NETBIOS 17 /* NetBios-style addresses */
#define AF_MAX 18
/*
* Structure used by kernel to store most
* addresses.
*/
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};
/*
* Structure used by kernel to pass protocol
* information in raw sockets.
*/
struct sockproto {
u_short sp_family; /* address family */
u_short sp_protocol; /* protocol */
};
/*
* Protocol families, same as address families for now.
*/
#define PF_UNSPEC AF_UNSPEC
#define PF_UNIX AF_UNIX
#define PF_INET AF_INET
#define PF_IMPLINK AF_IMPLINK
#define PF_PUP AF_PUP
#define PF_CHAOS AF_CHAOS
#define PF_NS AF_NS
#define PF_ISO AF_ISO
#define PF_OSI AF_OSI
#define PF_ECMA AF_ECMA
#define PF_DATAKIT AF_DATAKIT
#define PF_CCITT AF_CCITT
#define PF_SNA AF_SNA
#define PF_DECnet AF_DECnet
#define PF_DLI AF_DLI
#define PF_LAT AF_LAT
#define PF_HYLINK AF_HYLINK
#define PF_APPLETALK AF_APPLETALK
#define PF_MAX AF_MAX
/*
* Structure used for manipulating linger option.
*/
struct linger {
u_short l_onoff; /* option on/off */
u_short l_linger; /* linger time */
};
/*
* Level number for (get/set)sockopt to apply to socket itself.
*/
#define SOL_SOCKET 0xffff /* options for socket level */
/*
* Maximum queue length specifiable by listen.
*/
#define SOMAXCONN 5
#define MSG_OOB 0x1 /* process out-of-band data */
#define MSG_PEEK 0x2 /* peek at incoming message */
#define MSG_DONTROUTE 0x4 /* send without using routing tables */
#define MSG_MAXIOVLEN 16
/*
* Define constant based on rfc883, used by gethostbyxxxx calls.
*/
#define MAXGETHOSTSTRUCT 1024
/*
* Define flags to be used with the WSAAsyncSelect call.
*/
#define FD_READ 0x01
#define FD_WRITE 0x02
#define FD_OOB 0x04
#define FD_ACCEPT 0x08
#define FD_CONNECT 0x10
#define FD_CLOSE 0x20
/*
* All Windows Sockets error constants are biased by WSABASEERR from
* the "normal"
*/
#define WSABASEERR 10000
/*
* Windows Sockets definitions of regular Microsoft C error constants
*/
#define WSAEINTR (WSABASEERR+4)
#define WSAEBADF (WSABASEERR+9)
#define WSAEACCES (WSABASEERR+13)
#define WSAEFAULT (WSABASEERR+14)
#define WSAEINVAL (WSABASEERR+22)
#define WSAEMFILE (WSABASEERR+24)
/*
* Windows Sockets definitions of regular Berkeley error constants
*/
#define WSAEWOULDBLOCK (WSABASEERR+35)
#define WSAEINPROGRESS (WSABASEERR+36)
#define WSAEALREADY (WSABASEERR+37)
#define WSAENOTSOCK (WSABASEERR+38)
#define WSAEDESTADDRREQ (WSABASEERR+39)
#define WSAEMSGSIZE (WSABASEERR+40)
#define WSAEPROTOTYPE (WSABASEERR+41)
#define WSAENOPROTOOPT (WSABASEERR+42)
#define WSAEPROTONOSUPPORT (WSABASEERR+43)
#define WSAESOCKTNOSUPPORT (WSABASEERR+44)
#define WSAEOPNOTSUPP (WSABASEERR+45)
#define WSAEPFNOSUPPORT (WSABASEERR+46)
#define WSAEAFNOSUPPORT (WSABASEERR+47)
#define WSAEADDRINUSE (WSABASEERR+48)
#define WSAEADDRNOTAVAIL (WSABASEERR+49)
#define WSAENETDOWN (WSABASEERR+50)
#define WSAENETUNREACH (WSABASEERR+51)
#define WSAENETRESET (WSABASEERR+52)
#define WSAECONNABORTED (WSABASEERR+53)
#define WSAECONNRESET (WSABASEERR+54)
#define WSAENOBUFS (WSABASEERR+55)
#define WSAEISCONN (WSABASEERR+56)
#define WSAENOTCONN (WSABASEERR+57)
#define WSAESHUTDOWN (WSABASEERR+58)
#define WSAETOOMANYREFS (WSABASEERR+59)
#define WSAETIMEDOUT (WSABASEERR+60)
#define WSAECONNREFUSED (WSABASEERR+61)
#define WSAELOOP (WSABASEERR+62)
#define WSAENAMETOOLONG (WSABASEERR+63)
#define WSAEHOSTDOWN (WSABASEERR+64)
#define WSAEHOSTUNREACH (WSABASEERR+65)
#define WSAENOTEMPTY (WSABASEERR+66)
#define WSAEPROCLIM (WSABASEERR+67)
#define WSAEUSERS (WSABASEERR+68)
#define WSAEDQUOT (WSABASEERR+69)
#define WSAESTALE (WSABASEERR+70)
#define WSAEREMOTE (WSABASEERR+71)
/*
* Extended Windows Sockets error constant definitions
*/
#define WSASYSNOTREADY (WSABASEERR+91)
#define WSAVERNOTSUPPORTED (WSABASEERR+92)
#define WSANOTINITIALISED (WSABASEERR+93)
/*
* Error return codes from gethostbyname and gethostbyaddr
* (when using the resolver). Note that these errors are
* retrieved via WSAGetLastError and must therefore follow
* the rules for avoiding clashes with error numbers from
* specific implementations or language run-time systems.
* For this reason the codes are based at WSABASEERR+1001.
* Note also that [WSA]NO_ADDRESS is defined only for
* compatibility purposes.
*/
#define h_errno WSAGetLastError
/* Authoritative Answer: Host not found */
#define WSAHOST_NOT_FOUND (WSABASEERR+1001)
#define HOST_NOT_FOUND WSAHOST_NOT_FOUND
/* Non-Authoritative: Host not found, or SERVERFAIL */
#define WSATRY_AGAIN (WSABASEERR+1002)
#define TRY_AGAIN WSATRY_AGAIN
/* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
#define WSANO_RECOVERY (WSABASEERR+1003)
#define NO_RECOVERY WSANO_RECOVERY
/* Valid name, no data record of requested type */
#define WSANO_DATA (WSABASEERR+1004)
#define NO_DATA WSANO_DATA
/* no address, look for MX record */
#define WSANO_ADDRESS WSANO_DATA
#define NO_ADDRESS WSANO_ADDRESS
/*
* Windows Sockets errors redefined as regular Berkeley error constants
*/
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define ENOTSOCK WSAENOTSOCK
#define EDESTADDRREQ WSAEDESTADDRREQ
#define EMSGSIZE WSAEMSGSIZE
#define EPROTOTYPE WSAEPROTOTYPE
#define ENOPROTOOPT WSAENOPROTOOPT
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define EOPNOTSUPP WSAEOPNOTSUPP
#define EPFNOSUPPORT WSAEPFNOSUPPORT
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#define EADDRINUSE WSAEADDRINUSE
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define ENETDOWN WSAENETDOWN
#define ENETUNREACH WSAENETUNREACH
#define ENETRESET WSAENETRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ENOBUFS WSAENOBUFS
#define EISCONN WSAEISCONN
#define ENOTCONN WSAENOTCONN
#define ESHUTDOWN WSAESHUTDOWN
#define ETOOMANYREFS WSAETOOMANYREFS
#define ETIMEDOUT WSAETIMEDOUT
#define ECONNREFUSED WSAECONNREFUSED
#define ELOOP WSAELOOP
#define ENAMETOOLONG WSAENAMETOOLONG
#define EHOSTDOWN WSAEHOSTDOWN
#define EHOSTUNREACH WSAEHOSTUNREACH
#define ENOTEMPTY WSAENOTEMPTY
#define EPROCLIM WSAEPROCLIM
#define EUSERS WSAEUSERS
#define EDQUOT WSAEDQUOT
#define ESTALE WSAESTALE
#define EREMOTE WSAEREMOTE
/* Socket function prototypes */
#ifdef __cplusplus
extern "C" {
#endif
SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr,
int FAR *addrlen);
int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen);
int PASCAL FAR closesocket (SOCKET s);
int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen);
int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp);
int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name,
int FAR * namelen);
int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name,
int FAR * namelen);
int PASCAL FAR getsockopt (SOCKET s, int level, int optname,
char FAR * optval, int FAR *optlen);
u_long PASCAL FAR htonl (u_long hostlong);
u_short PASCAL FAR htons (u_short hostshort);
unsigned long PASCAL FAR inet_addr (const char FAR * cp);
char FAR * PASCAL FAR inet_ntoa (struct in_addr in);
int PASCAL FAR listen (SOCKET s, int backlog);
u_long PASCAL FAR ntohl (u_long netlong);
u_short PASCAL FAR ntohs (u_short netshort);
int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags);
int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags,
struct sockaddr FAR *from, int FAR * fromlen);
int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
fd_set FAR *exceptfds, const struct timeval FAR *timeout);
int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags);
int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags,
const struct sockaddr FAR *to, int tolen);
int PASCAL FAR setsockopt (SOCKET s, int level, int optname,
const char FAR * optval, int optlen);
int PASCAL FAR shutdown (SOCKET s, int how);
SOCKET PASCAL FAR socket (int af, int type, int protocol);
/* Database function prototypes */
struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr,
int len, int type);
struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);
int PASCAL FAR gethostname (char FAR * name, int namelen);
struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto);
struct servent FAR * PASCAL FAR getservbyname(const char FAR * name,
const char FAR * proto);
struct protoent FAR * PASCAL FAR getprotobynumber(int proto);
struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name);
/* Microsoft Windows Extension function prototypes */
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
int PASCAL FAR WSACleanup(void);
void PASCAL FAR WSASetLastError(int iError);
int PASCAL FAR WSAGetLastError(void);
BOOL PASCAL FAR WSAIsBlocking(void);
int PASCAL FAR WSAUnhookBlockingHook(void);
FARPROC PASCAL FAR WSASetBlockingHook(FARPROC lpBlockFunc);
int PASCAL FAR WSACancelBlockingCall(void);
HANDLE PASCAL FAR WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
const char FAR * name,
const char FAR * proto,
char FAR * buf, int buflen);
HANDLE PASCAL FAR WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
const char FAR * proto, char FAR * buf,
int buflen);
HANDLE PASCAL FAR WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
const char FAR * name, char FAR * buf,
int buflen);
HANDLE PASCAL FAR WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
int number, char FAR * buf,
int buflen);
HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
const char FAR * name, char FAR * buf,
int buflen);
HANDLE PASCAL FAR WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
const char FAR * addr, int len, int type,
const char FAR * buf, int buflen);
int PASCAL FAR WSACancelAsyncRequest(HANDLE hAsyncTaskHandle);
int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
long lEvent);
#ifdef __cplusplus
}
#endif
/* Microsoft Windows Extended data types */
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr *PSOCKADDR;
typedef struct sockaddr FAR *LPSOCKADDR;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr_in *PSOCKADDR_IN;
typedef struct sockaddr_in FAR *LPSOCKADDR_IN;
typedef struct linger LINGER;
typedef struct linger *PLINGER;
typedef struct linger FAR *LPLINGER;
typedef struct in_addr IN_ADDR;
typedef struct in_addr *PIN_ADDR;
typedef struct in_addr FAR *LPIN_ADDR;
typedef struct fd_set FD_SET;
typedef struct fd_set *PFD_SET;
typedef struct fd_set FAR *LPFD_SET;
typedef struct hostent HOSTENT;
typedef struct hostent *PHOSTENT;
typedef struct hostent FAR *LPHOSTENT;
typedef struct servent SERVENT;
typedef struct servent *PSERVENT;
typedef struct servent FAR *LPSERVENT;
typedef struct protoent PROTOENT;
typedef struct protoent *PPROTOENT;
typedef struct protoent FAR *LPPROTOENT;
typedef struct timeval TIMEVAL;
typedef struct timeval *PTIMEVAL;
typedef struct timeval FAR *LPTIMEVAL;
/*
* Windows message parameter composition and decomposition
* macros.
*
* WSAMAKEASYNCREPLY is intended for use by the Windows Sockets implementation
* when constructing the response to a WSAAsyncGetXByY routine.
*/
#define WSAMAKEASYNCREPLY(buflen,error) MAKELONG(buflen,error)
/*
* WSAMAKESELECTREPLY is intended for use by the Windows Sockets implementation
* when constructing the response to WSAAsyncSelect.
*/
#define WSAMAKESELECTREPLY(event,error) MAKELONG(event,error)
/*
* WSAGETASYNCBUFLEN is intended for use by the Windows Sockets application
* to extract the buffer length from the lParam in the response
* to a WSAGetXByY.
*/
#define WSAGETASYNCBUFLEN(lParam) LOWORD(lParam)
/*
* WSAGETASYNCERROR is intended for use by the Windows Sockets application
* to extract the error code from the lParam in the response
* to a WSAGetXByY.
*/
#define WSAGETASYNCERROR(lParam) HIWORD(lParam)
/*
* WSAGETSELECTEVENT is intended for use by the Windows Sockets application
* to extract the event code from the lParam in the response
* to a WSAAsyncSelect.
*/
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
/*
* WSAGETSELECTERROR is intended for use by the Windows Sockets application
* to extract the error code from the lParam in the response
* to a WSAAsyncSelect.
*/
#define WSAGETSELECTERROR(lParam) HIWORD(lParam)
#endif /* _WINSOCKAPI_ */

+$#A Notes for Windows Sockets Suppliers
+$#A Introduction to Notes for Windows Sockets Suppliers
A WINSOCK.DLL or WINSOCK32.DLL must implement ALL the functionality described in the Windows Sockets documentation.
Windows Sockets version 1.1 implementations must support both TCP and UDP type sockets. An implementation may support raw sockets (of type SOCK_RAW), but their use is deprecated.
Certain functions documented in this Help file have special notes for Windows Sockets implementors. A Windows Sockets implementation shoul conform to the function as documented. The special notes are provided for assistance and clarification.
+$#A Windows Sockets Components
+$#A Development Components
The Windows Sockets development components for use by Windows Sockets application developers will be provided by each Windows Sockets supplier. These Windows Sockets development components are:
Component Description
Windows Sockets Documentation This document
WINSOCK.LIB file Windows Sockets API Import Library
WINSOCK.H file Windows Sockets Header File
NETDB.H file Berkeley Compatible Header File
ARPA/INET.H file Berkeley Compatible Header File
SYS/TIME.H file Berkeley Compatible Header File
SYS/SOCKET.H file Berkeley Compatible Header File
NETINET/IN.H file Berkeley Compatible Header File

#Run-Time Components
The run-time component provided by each Windows Sockets supplier is:
Component Description
WINSOCK.DLL The Windows Sockets API implementation DLL

+$#A Multithreadedness and Blocking Routines
Data areas returned by, for example, the functions in the getXbyY family (gethostbyaddr_win32_gethostbyaddr, and so on) MUST be on a per-thread basis.
Note that an application must be prevented from making multiple nested Windows Sockets function calls. Only one outstanding function call will be allowed for a particular task. Any Windows Sockets call performed when an existing blocking call is already outstanding will fail with an error code of WSAEINPROGRESS. There are two exceptions to this restriction: WSACancelBlockingCall_win32_WSACancelBlockingCall and WSAIsBlocking_win32_WSAIsBlocking may be called at any time. Windows Sockets suppliers should note that although preliminary drafts of this specification indicated that the restriction only applied to blocking function calls, and that it would be permissible to make nonblocking calls while a blocking call was in progress, this is no longer true.
Regarding the implementation of blocking routines, the solution in Windows Sockets is to simulate the blocking mechanism by having each routine call PeekMessage_win32_PeekMessage as it waits for the completion of its operation. In anticipation of this, the function WSASetBlockingHook_win32_WSASetBlockingHook is provided to allow you to define a special routine to be called instead of the default PeekMessage loop.
+$#A Database Files Implementation
The database routines in the getXbyY family (gethostbyaddr_win32_gethostbyaddr, and so on.) were originally designed (in the first Berkeley UNIX releases) as mechanisms for looking up information in text databases. A Windows Sockets supplier may choose to employ local files OR a name service to provide some or all of this information. If local files exist, the format of the files must be identical to that used in BSD UNIX, allowing for the differences in text file formats.
+$#A FD_ISSET
It is necessary to implement the FD_ISSET Berkeley macro using a supporting function: __WSAFDIsSet. It is the responsibility of a Windows Sockets implementation to make this available as part of the Windows Sockets API. Unlike the other functions exported by a Windows Sockets DLL, however, this function is not intended to be invoked directly by Windows Sockets applications: It should be used only to support the FD_ISSET macro. The source code for this function is:
int FAR
__WSAFDIsSet(SOCKET fd, fd_set FAR *set)
{
int i = set->fd_count;

while (i--)
if (set->fd_array[i] == fd)
return 1;

return 0;
}

+$#A Error Codes Returned by WSAGetLastError
To avoid conflict between various compiler environments, Windows Sockets implementations MUST return the error codes listed in the API specification, using the manifest constants beginning with "WSA". The Berkeley-compatible error code definitions are provided solely for compatibility purposes for applications being ported from other platforms.
+$#A DLL Ordinal Numbers
The WINSOCK.DEF file for use by every Windows Sockets implementation is as follows. Ordinal values starting at 1000 are reserved for Windows Sockets implementors to use for exporting private interfaces to their DLLs. A Windows Sockets implementation must not use any ordinals 999 and below except for those functions listed following. An application that is to work with any Windows Sockets DLL must use only those routines listed follwoing; using a private export makes an application dependent on a particular Windows Sockets implementation.
;
; File: winsock.def
; System: MS-Windows 3.x
; Summary: Module definition file for Windows Sockets DLL.
;
LIBRARY WINSOCK ; Application's module name
DESCRIPTION 'BSD Socket API for Windows'
EXETYPE WINDOWS ; required for all windows applications
STUB 'WINSTUB.EXE' ; generates error message if application
; is run without Windows
;CODE can be FIXED in memory because of potential upcalls
CODE PRELOAD FIXED
;DATA must be SINGLE and at a FIXED location since this is a DLL
DATA PRELOAD FIXED SINGLE
HEAPSIZE 1024
STACKSIZE 16384
; All functions that will be called by any Windows routine
; must be exported. Any additional exports beyond those defined
; here must have ordinal numbers 1000 or above.
EXPORTS
accept @1
bind @2
closesocket @3
connect @4
getpeername @5
getsockname @6
getsockopt @7
htonl @8
htons @9
inet_addr @10
inet_ntoa @11
ioctlsocket @12
listen @13
ntohl @14
ntohs @15
recv @16
recvfrom @17
select @18
send @19
sendto @20
setsockopt @21
shutdown @22
socket @23
gethostbyaddr @51
gethostbyname @52
getprotobyname @53
getprotobynumber @54
getservbyname @55
getservbyport @56
gethostname @57
WSAAsyncSelect @101
WSAAsyncGetHostByAddr @102
WSAAsyncGetHostByName @103
WSAAsyncGetProtoByNumber @104
WSAAsyncGetProtoByName @105
WSAAsyncGetServByPort @106
WSAAsyncGetServByName @107
WSACancelAsyncRequest @108
WSASetBlockingHook @109
WSAUnhookBlockingHook @110
WSAGetLastError @111
WSASetLastError @112
WSACancelBlockingCall @113
WSAIsBlocking @114
WSAStartup @115
WSACleanup @116
__WSAFDIsSet @151
WEP @500 RESIDENTNAME
;eof

+$#A Validation Suite
The Windows Sockets API Tester (WSAT) to ensure Windows Sockets compatibility between Windows Sockets DLL implementations is currently in beta test. This beta version includes functionality testing of the Windows Sockets interface and is supported by a comprehensive scripting language. The final version of WSAT will be available in Spring 1993. If you wish to receive the tester or more information on the beta, send email to wsat@microsoft.com.
+$#A For Further Reference
This documentation is intended to cover the Windows Sockets interface to TCP/IP in detail. Many details of TCP/IP and Windows, however, are intentionally omitted in the interest of brevity, and this specification often assumes background knowledge of these topics. For more information, the following references may be helpful:
Braden, R.[1989], RFC 1122, Requirements for Internet Hosts ¾ Communication Layers, Internet Engineering Task Force.
Comer, D. [1991], Internetworking with TCP/IP Volume I: Principles, Protocols, and Architecture, Prentice Hall, Englewood Cliffs, New Jersey.
Comer, D. and Stevens, D. [1991], Internetworking with TCP/IP Volume II: Design, Implementation, and Internals, Prentice Hall, Englewood Cliffs, New Jersey.
Comer, D. and Stevens, D. [1991], Internetworking with TCP/IP Volume III: Client-Server Programming and Applications, Prentice Hall, Englewood Cliffs, New Jersey.
Leffler, S. et al., An Advanced 4.3BSD Interprocess Communication Tutorial.
Petzold, C. [1992], Programming Windows 3.1, Microsoft Press, Redmond, Washington.
Stevens, W.R. [1990], Unix Network Programming, Prentice Hall, Englewood Cliffs, New Jersey.
+$#A Background Information
+$#A Legal Status of Windows Sockets
The copyright for the Windows Sockets specification is owned by the specification authors listed on the title page. Permission is granted to redistribute this specification in any form, provided that the contents of the specification are not modified. Windows Sockets implementors are encouraged to include this specification with their product documentation.
The Windows Sockets logo on the title page of this document is meant for use on both Windows Sockets implementations and for applications that use the Windows Sockets interface. Use of the logo is encouraged on packaging, documentation, collateral, and advertising. The logo is available on microdyne.com in pub/winsock as WINSOCK.BMP. The suggested color for the logo's title bar is blue, the electrical socket grey, and the text and outline black.
UNIX is a trademark of Unix System Laboratories, Inc.
VAX is a trademark of Digital Equipment Corporation.
Windows is a trademark of Microsoft Corporation
68000 is a trademark of Motorola, Inc.
NT and Windows/NT are trademarks of Microsoft Corporation.

+$#A Windows Sockets for Windows NT
Microsoft® Windows NT™ fully supports the Windows Sockets functions as defined in the Win32 SDK (Software Development Kit). However, Windows NT has some additional features available through its Windows Sockets interface that are not described in the Windows Sockets specification. This document describes the extended features of the Windows Sockets interface in Windows NT, a superset of the Windows Sockets functionality.
Some of the features described in this document were not implemented in the initial release of Windows NT but are available in Windows NT version 3.5. The most notable new features are the SO_SNDTIMEO, SO_RCVTIMEO, and SO_OPENTYPE socket options and support for NetBIOS providers.
+$#A Support for 32-bit Applications
Included with the Windows NT release disks is the WINSOCK.DLL file, which provides the functions necessary for 16-bit applications to use Windows Sockets through 16-bit subsystem of Windows NT. Win32-based applications use a different DLL, WSOCK32.DLL, which exports routines that take exclusively 32-bit arguments.
The 32-bit versions of the Windows Sockets functions take all the same parameters, with the notable exception that all int and SOCKET parameters are widened from 16 bits to 32 bits. This widening is of no concern if you are writing applications to be portable between 16-bit and 32-bit environments, since the compiler performs the necessary steps automatically.
+$#A Windows NT Socket Handles
From a developer's standpoint, perhaps the most notable aspect of the Windows Sockets support in Windows NT is that the handles returned from the socket_win32_socket and accept_win32_accept functions are native Windows NT system handles that have been opened, by default, as "asynchronous" or "overlapped" handles. This allows an application to use the handles as arguments to system routines in addition to the Windows Sockets routines and also to share the handles between threads and processes.
+$#A Sharing Sockets Between Threads
Because a handle value is global to all threads in a process, a handle can be accessed in any thread, even threads that did not open the handle. For example, one thread can call the socket_win32_socket function, which returns a socket handle. A different thread in the same process can make other Windows Sockets calls with the returned socket handle. No special action is required to enable the second thread to use the handle; it just needs to obtain the handle value.
It is also possible for multiple threads to access a single socket simultaneously, but an application that does this must synchronize important actions. For example, if one thread performs a bind_win32_bind operation on a socket at the same time another thread is performing a getsockname_win32_getsockname operation, then because of the preemptive scheduling of Windows NT there are no guarantees that the getsockname operation will execute before the bind operation. It could happen that getsockname returns WSAEINVAL because the socket is not yet bound.
Another important example of simultaneous access in multiple threads to a single socket is data transfer. An application can have one thread calling send_win32_send on a socket and another thread calling recv_win32_recv with no adverse effects whatsoever. However, if multiple threads call send on a socket, there are no guarantees as to the order of data delivery. The only guarantee is that the data in each send operation will be sent as a contiguous chunk ¾ there is no possibility of intermixing data from different send calls. The analagous situation exists for multiple simultaneous recv calls ¾ there is no guarantee as to which will finish first, but the data returned to any given recv operation is contiguous.
Making multiple simultaneous calls from different threads will not in any way damage the internal state of the socket maintained by Windows Sockets. Windows Sockets internally synchronizes the socket state whenever it changes in any way. Therefore, you need not worry about synchronizing calls for the sake of preserving the internal state of Windows Sockets.
+$#A Sharing Sockets Between Processes
Handles in Windows NT can also be shared between processes. However, since each process has its own handle table, it is more work to share handles between processes than between threads. There are two mechanisms for sharing handles between processes: inheritance and duplication.
In inheritance, a process (called the "parent process") creates another process (called the "child process") using the Win32 function CreateProcess_win32_CreateProcess with the fInheritHandles parameter set to TRUE. This causes the system to map into the child's process table all the inheritable handles of the parent process. Note that while not all Win32 handles are inheritable (inheritance requires opening a handle in a special manner), all socket handles are inheritable.
Once you create the child process, it must be given the values of the handles it needs to access. When it has the handle values, it can pass the handles to Windows Sockets or other functions.
In duplication, a process can pass a handle to another existing process with the Win32 function DuplicateHandle_win32_DuplicateHandle. This function allows one process to map a handle into the process table of another process, although the specific handle value may be different in the receiving process.
When sharing sockets between processes, the same synchronization issues exist as when sharing sockets between threads. In addition, you must be aware of two other special properties of sharing sockets between processes.
First, since both processes have a view of the socket handle, both processes must call closesocket before the system closes the socket. The process that first calls closesocket only takes the socket out of its internal handle table. The first closesocket call does not affect the second process's access to the socket in any way. The second closesocket does the actual cleanup of the socket, including initiating the protocol-level virtual circuit termination if the socket is connected. If the socket sharing is really a "hand-off" from one process to another, it is best to close the socket in the first process immediately after the CreateProcess_win32_CreateProcess or DuplicateHandle_win32_DuplicateHandle call.
Second, some socket options, such as SO_LINGER, are kept on a per-process basis. Therefore, after a socket handle has been passed to a second process, changes in socket options by one process may not be reflected in the other process. For this reason it is advisable to set up any socket options that both processes will need before sharing the socket handle.
Many Unix® services that use sockets make use of child process creation and inheritance to handle multiple simultaneous clients. This is typically done by a "master" process creating a socket, calling listen on the socket, and using accept to accept incoming connections. Then this process creates a "slave" process that actually services the client while the master immediately closes the accepted socket. This is useful in Unix, where multiple threads are generally not available, and this algorithm will work in Windows NT. However, processes use more memory than threads. Context switches between processes use about twice the CPU instructions used in context switches between threads within a single process. In addition, threads can share information much more easily than processes, so it is usually easier in the long run to use threads rather than processes. For all these reasons, it is strongly recommended that you write a service that handles multiple clients simultaneously so that it uses one thread per client rather than one process per client. It is even better to use multiple clients per thread. This technique is discussed in Accessing Sockets Through the Win32 I/O Functions_win32_Accessing_Sockets_Through_the_Win32_I_O_Functions.
+$#A Using Sockets as Standard I/O Handles
A special case of sharing sockets between processes is using sockets as standard input, output, or errors for a child process created with CreateProcess_win32_CreateProcess. This is useful for causing a process to unwittingly read input from a socket and to write output to a socket. Such a child process may "know" absolutely nothing about sockets or even networks; it just reads from its standard input and writes to its standard output.
There are two key steps in making this possible. First, a socket must be opened for "synchronous" access (that is, not overlapped) with the SO_OPENTYPE socket option described following. Second, the socket must be connected with either the connect or accept Windows Sockets function calls. After these steps, making a socket handle accessible as a standard I/O handle is just like doing so for other handles: Specify the handle value in the hStdInput, hStdOutput, and/or hStdError fields of the STARTUPINFO structure passed to CreateProcess Win32 function. The created process will read from and write to the socket without ever knowing it is a socket.
+$#A Accessing Sockets Through the Win32 I/O Functions
Since Windows NT socket handles are system socket handles, it is possible to use Windows NT socket handles as parameters to several of the Win32 functions. The most useful Win32 functions for sockets are the ReadFile_win32_ReadFile and WriteFile_win32_WriteFile functions since these allow an application to perform aynchronous I/O on a handle by means of the lpOverlapped parameter. In overlapped I/O, the request is started with the initial call but the call does not block waiting for the request to be completed. For example, if an application makes an overlapped ReadFile call when no data exists to be read on a socket, the calling thread will regain control immediately rather than waiting for data to arrive on the socket. The application will be notified when the request has been completed via the lpOverlapped parameter.
Overlapped I/O differs from the socket concept of nonblocking I/O in that overlapped I/O actually performs the I/O eventually if it cannot be completed immediately. In nonblocking I/O, if the I/O cannot be performed immediately, then it fails with the error code WSAEWOULDBLOCK and the application must reissue the request at a later point.
Overlapped I/O is especially useful in a service that handles multiple sockets. A single thread can be used to perform I/O on all of the sockets without the overhead of the select call. Also, the use of overlapped I/O allows an application to mix different types of I/O in a single thread; for example, I/O on named pipes and I/O on sockets.
Because Windows NT socket handles are, by default, opened as overlapped handles, an application must specify the lpOverlapped parameter on calls to ReadFile_win32_ReadFile and WriteFile_win32_WriteFile. However, an application can also use the SO_OPENTYPE socket option to open sockets that are not overlapped.
+$#A Accessing Sockets Through the C Run-time Libraries
The C run-time libraries (CRTs) provide useful functionality for input and output formatting through routines such as fprintf, fscanf, and so on. It may also be useful to access socket handles through these routines in existing code that uses C run-time calls such as _read and _write.
The C run-time libraries take two different inputs to identify a file: a CRT handle (different from a system handle such as a socket) and a file pointer. The low-level I/O routines, such as _read, take CRT handles, while the more sophisticated formatting routines, such as fprintf, take file pointers.
To access sockets through the C run-time routines, an application must perform several steps:
1. Use the SO_OPENTYPE socket option described following so that any opened socket handles are synchronous.
2. Open a connected socket by calling either socket and connect or by calling accept. It is legal to use datagram sockets, although their inherent unreliability may make them unsuitable for C run-time access in many applications.
3. Obtain a CRT socket handle with the _open_osf_handle C run-time API. If the application intends to use only the low-level C run-time calls, the handle returned by this call is sufficient and the application is ready to make C run-time calls.
4. If the application wants to use the higher-level C run-time calls that do data formatting, the application must obtain a file pointer (FILE *) by using the _fdopen C run-time call.

+$#A Socket Options Specific to Windows NT
Windows NT supplies several socket options beyond those included in the Windows Sockets specification. Because these options are not currently part of the formal Windows Sockets specification, an application that uses these options is not compatible with Windows Sockets. However, for applications written exclusively for Windows NT, these options can be very useful.
+$#A SO_SNDTIMEO and SO_RCVTIMEO
These two options set up timeouts for the send, sendto, recv, and recvfrom functions. You can obtain the same functionality by calling select with a timeout just before the I/O call, but these options offer a significant improvement in performance by avoiding a kernel transition and the other overhead of the select call. For any code whose performance is very critical, applications should use these timeout options rather than select.
You can set these options on any type of socket in any state. The default value for these options is zero, which refers to an infinite timeout. Any other setting is the timeout, in milliseconds. It is legal to set the timeout to any value, but values less than 500 milliseconds (half a second) are interpreted to be 500 milliseconds.
To set a send timeout, use
int timeout = TIMEOUT_VALUE;
int err;
SOCKET s;

s = socket( ... );
err = setsockopt(
s,
SOL_SOCKET,
SO_SNDTIMEO,
(char *)&timeout,
sizeof(timeout));
if (err != NO_ERROR) {
/* failed for some reason... */
}

where TIMEOUT_VALUE is the needed timeout in milliseconds. To set a receive timeout, substitute SO_RCVTIMEO for SO_SNDTIMEO in the preceding example.
After setting one of these options to a nonzero value, I/O through the Windows Sockets calls fails with the error WSAETIMEDOUT if the request cannot be satisfied within the specified timeout. If a request times out, an application has no guarantees as to how much data was actually sent or received in the I/O call.
+$#A SO_OPENTYPE
By default, all socket handles are opened as overlapped handles so that asynchronous I/O can be performed on them. However, in many situations it is preferable to have nonoverlapped (synchronous) socket handles. For example, only nonoverlapped handles can be used with the C run-time libraries or used as standard I/O handles for a process. The SO_OPENTYPE socket option allows an application to open nonoverlapped socket handles.
The SO_OPENTYPE option must be set on a per-thread basis since it affects how each thread opens sockets, and it must be set before a socket handle is opened in order to affect the socket. The default value for this option is zero, which opens sockets as overlapped handles. Other possible values for this option are SO_SYNCHRONOUS_ALERT and SO_SYNCHRONOUS_NONALERT. SO_SYNCHRONOUS_ALERT allows operations performed on the socket handle to be alerted by an APC routine (software interrupt), while SO_SYNCHRONOUS_NONALERT blocks APCs from being delivered to threads waiting on requests on socket handles. Typically, it does not matter which of these options is chosen: either makes socket handles nonoverlapped.
Use the following to open a nonoverlapped socket handle:
SOCKET s;
int optionValue = SO_SYNCHRONOUS_NONALERT;
int err;

err = setsockopt(
INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&optionValue,
sizeof(optionValue));
if (err != NO_ERROR) {
/* failed for some reason... */
}

s = socket( ... );

Note that the socket handle passed to setsockopt is ignored for this option.
The SO_OPENTYPE socket option affects both the socket and accept Windows Sockets calls. The setting of SO_OPENTYPE is preserved throughout the lifetime of the thread in which it is set, so it is not necessary to set its value before each socket opens. However, it is necessary to set this option for every thread that opens sockets if the nondefault behavior is desired. Also, if different layers of an application open sockets and have different expectations of the type of handles opened, it is advisable to use getsockopt before the setsockopt call to obtain the original setting of SO_OPENTYPE, then reset SO_OPENTYPE to the original value after opening the socket. For example,
SOCKET s;
int originalValue;
int optionValue = SO_SYNCHRONOUS_ALERT;
int err;
int optionLength = sizeof(originalValue);

/* Get the original setting of SO_OPENTYPE. */

err = getsockopt(
INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&originalValue,
optionLength);
if (err != NO_ERROR) {
/* failed for some reason... */
}

/* Set SO_OPENTYPE so that we get a synchronous socket handle. */

err = setsockopt(
INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&optionValue,
sizeof(optionValue));
if (err != NO_ERROR) {
/* failed for some reason... */
}

s = socket( ... );

/* Reset the SO_OPENTYPE setting to its original value. */

err = setsockopt(
INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&optionValue,
sizeof(optionValue));
if (err != NO_ERROR) {
/* failed for some reason... */
}

This technique is especially useful in DLLs that use synchronous sockets.
There are some Windows Sockets features that you cannot use with synchronous sockets. The WSAAsyncSelect call cannot be used with synchronous sockets and will fail with the error WSAEINVAL. It is also not possible to set the SO_SNDTIMEO and SO_RCVTIMEO socket options on synchronous sockets; setsockopt with these options on synchronous sockets fails with WSAEINVAL as well.
+$#A Multiple Protocol Support
In addition to TCP/IP, Windows NT supports several additional transport protocols through its Windows Sockets interface. Although these protocols are not explicitly addressed in the Windows Sockets specification, the interface to Windows Sockets over different transport protocols is quite feasible. This is because the fundamental job of a transport protocol is data transfer, along with the fact that Windows Sockets provides a rich interface for low-level data transfer in applications. Having a common, well-known interface allows applications to be ported quickly between different transport protocols, and there are several successful examples of this.
Because of the architecture of the Windows Sockets component of Windows NT, all supported transport protocols are accessed through the same DLL, WSOCK32.DLL. In fact, a single application can simultaneously use sockets of different transport protocols.
An application uses the parameters to the socket function to specify the protocol it will use. For example, a TCP socket is opened with
s_tcp = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

while an SPX socket is opened with
s_spx = socket( AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX );

After opening a socket for a specific protocol, all actions performed on the socket go through the specified protocol. In the preceding example, if the application did a listen on the s_tcp socket but not on the s_spx socket, it would receive incoming connects through TCP but not receive incoming connects through SPX.
+$#A Protocol Characteristics
It is important to understand the different characteristics of transport protocols when designing an application that uses them. While all transport protocols share the same fundamental purpose of bidirectional data transfer, many details of the data transfer differ. The following sections list key characteristics of transport protocols that apply to Windows Sockets applications.
+$#A Addressing
The most obvious difference between different transport protocols is the way each uses transport addresses, called "socket addresses" in Windows Sockets terms. Virtually all transport protocols expose different address formats to applications, the one exception being NetBIOS protocols, which all use the same address format.
A number of Windows Sockets functions take socket addresses as input or output parameters, including bind, accept, sendto, recvfrom, getsockname, and getpeername. For all these functions a socket address is really two arguments: a pointer to the socket address structure itself and a length integer that gives the number of bytes in the socket address. For many transport protocols the length of a socket address is always the same, but socket address lengths can differ between transport protocols. For example, a TCP/IP socket address has 16 bytes and an IPX/SPX socket address has 14 bytes.
The format of socket address structures used by a transport protocol is specified in the af, or "address family" argument to the socket function. All TCP/IP sockets (including both TCP and UDP sockets) are opened with the AF_INET address family, while IPX/SPX sockets are opened with AF_IPX. Other transport protocols use different address family values as defined in the header file WINSOCK.H.
An application must fill in a socket address when binding to a local address or connecting to a remote address, and also to interpret the socket addresss returned from the accept, sendto, recvfrom, getpeername, and getsockname functions.
+$#A Connection-Oriented vs. Connectionless Transport
In a connection-oriented transport protocol, applications are required to establish a virtual circuit (sometimes abbreviated to "VC") before data transfer can take place. Virtual circuit establishment is assymetrical. One side, the server, must indicate to the transport protocol that it can receive incoming connections via the listen function. The other side, the client, initiates the circuit with the connect function, and the server can obtain a socket for the circuit with the accept function. Once the circuit is established, data transfer takes place with the send and recv functions. Protocol-level activity results from this circuit establishment, and more protocol activity tears down the circuit when the sockets are closed. TCP and SPX are examples of connection-oriented transport protocols.
In a connectionless transport protocol, circuit establishment is not required for data transfer. An application need only open and bind a socket, after which it can use the sendto and recvfrom functions to send and receive data. Of course, in order to specify the remote address for sending data or the address from which received data was sent, you must specify a socket address to these routines. UDP and IPX are examples of connectionless transport protocols.
It is possible to use the connect function on sockets opened for connectionless protocols. This is merely an application convenience, allowing the application to use the send and recv functions, and this does not result in any protocol activity. If a socket is connected in this manner, it will receive only packets sent from the connected address; other packets destined for the socket are silently discarded.
+$#A Reliable vs. Unreliable Data Delivery
Most physical networks are inherently unreliable. They can drop data, corrupt it, deliver it out of order, and deliver multiple copies of it. Some transport protocols hide this unreliability from applications by using protocol-level mechanisms to ensure that data is delivered in the correct order, without loss, corruption, or duplication. Other transport protocols do not make these guarantees; they simply make a best effort to get the data to its intended recipient, but physical network failures can lead to data integrity problems.
Because they require fewer system resources and less processing, unreliable protocols often provide better performance, especially for applications that send small amounts of data in a request-response (transaction) format. However, applications that use connectionless protocols must guarantee reliability, which makes applications more complicated. Reliable protocols are usually simpler for the application at the expense of a higher cost in system resources and performance.
Typically, connection-oriented protocols are reliable, while connectionless protocols are unreliable. While this is true of all the transport protocols supported by Windows NT, there are exceptions.
+$#A Terminating a Virtual Circuit
For a connection-oriented transport protocol, there are two ways to terminate a virtual circuit: orderly and abortive. In an orderly release, both sides get a chance to indicate that they have sent all the data they intend to send, and only when both sides have finished is the actual circuit terminated. For such a release, one side can use the shutdown function to indicate that it has finished sending, while the other side continues sending. In an abortive termination of a virtual circuit, one side decides that it is time to terminate the circuit and unilaterally ends the connection. If the other side attempts to use the connected socket, the request fails.
By default, closesocket also attempts an orderly release of the connection. However, under several circumstances, such as pending unreceived, outstanding send calls, and more, a closesocket will result in an abortive close.
All transport protocols support the concept of an abortive release, but not all protocols support orderly release. If an application attempts an orderly release on a transport protocol that does not support orderly release, the connection is terminated abortively. TCP supports orderly release, while SPX supports only abortive release.
+$#A Message-Oriented vs. Stream-Oriented
Connection-oriented transport protocols can be either message-oriented or stream-oriented. In a message-oriented transport protocol, each block of data is sent and received as a single block. An individual message is never broken down when received, although a transport protocol can be forced to break down a message if it is larger than the physical network's maximum transmission unit (MTU). However, this is transparent to the application.
In a stream-oriented transport protocol, data is sent and received as a continuous stream, without any message boundaries. The transport protocol is free to coalesce data from discrete send calls and deliver it to a single recv call, as well as to break down the data buffer given to a single send call and deliver it to several recv calls. An application cannot control how these breakdowns occur, and gets no notification of how data was sent. If an application needs to communicate in discrete messages, it must frame them, for example, by preceding message data with the length of the message until the specified length arrives.
Some message-oriented protocols support stream semantics in addition to message semantics. If an application opens a socket with SOCK_STREAM on such a transport protocol, then the transport ignores all message boundaries when transmitting and receiving data. It simply delivers data as it becomes available, regardless of whether the data is a complete message. This feature is handy for maximizing application portability. Note, however, that stream-oriented transport protocols cannot implement message-oriented semantics due to lack of information.
+$#A Expedited Data
Some connection-oriented transport protocols support expedited data, also referred to as "urgent data" or "out-of-band data." Data sent with the MSG_OOB flag in the send function is sent as expedited data, and the transport protocol makes every effort to deliver the data as quickly as possible. Often the data is delivered out of order so that it can get to the other application as quickly as possible. For maximum portability, avoid expedited data since it is not supported by all transport protocols.
+$#A Connect and Disconnect Data
A few transport protocols support connect and disconnect data, in which a small amount of data is sent by each side when a virtual circuit is established or terminated. This data can be anything, but is often negotiation information about the version of software connecting, and so on. Again, for protocol portability, avoid this feature since it is not universally supported.
+$#A Broadcasts
Most connectionless transport protocols support broadcasts in the same fashion, in which any bound socket can send a broadcast if the SO_BROADCAST option is set, and broadcasts sent to the appropriate local endpoint are received without any additional work on the part of the application. NetBIOS transport protocols, however, handle broadcasts somewhat differently. In order to receive broadcasts, an application must bind to the NetBIOS broadcast address, which is an asterisk ("*") followed by 15 blank spaces (ASCII character 0x20). This means two things: A socket must be specially bound to receive broadcasts, and applications cannot depend on receiving broadcasts intended only for a specific application, since all NetBIOS broadcasts are delivered to this address. In other protocols such as UDP/IP and IPX, broadcasts are delivered to a socket only if the broadcast was sent to the same port to which the socket was bound.
+$#A Extended Support for Message-Oriented Protocols
The Berkeley Software Distribution (BSD) sockets interface was originally designed to support primarily TCP/IP, and the Windows Sockets interface is based on BSD sockets. This interface is not sufficiently rich for message-oriented transport protocols, however, so Windows NT includes some minor extensions of the interface so that applications can fully exploit message-oriented transport protocols.
+$#A MSG_PARTIAL Flag
Under some circumstances, applications may not have available in a single, contiguous data buffer all the data that make up a single message to be sent. The message can be very large, for example, or can be composed of discrete parts in an application's virtual address space. To allow an application to send this message without having to allocate a buffer and copy all the data into the buffer, Windows NT allows the application to specify the MSG_PARTIAL bit in the flags parameter of the send function. This bit tells the transport protocol that the data in the send operation is just part of a larger message, and that the protocol should take whatever action is necessary to deliver the data as a partial message.
+$#A WSARecvEx Function
By default, for message-oriented transport protocols the Windows Sockets recv function receives a single message in each call to the API. This works fine for most cases, but there are two situations in which this is insufficient: when the application's data buffer is smaller than the message, and when the message is large and arrives in several pieces.
When the buffer is smaller than the data, as much of the message as will fit is copied into the user's buffer and recv returns with the error code WSAEMSGSIZE. A subsequent call to recv will get the next part of the message. Applications written for message-oriented transport protocols should be coded for this possibility if message sizing is not guaranteed by the application's data transfer protocol. If an application gets this error code, then the entire data buffer is filled with data.
It is more complicated when a very large message arrives a little at a time. For example, if an application sends a 1-megabyte message, the transport protocol must break up the message in order to send it over the physical network. It is theoretically possible for the transport protocol on the receiving side to buffer all the data in the message, but this would be quite expensive in terms of resources.
It would be better to allow a recv call to complete with only a partial message and some indication to the application that the data is only a partial message. However, the Windows Sockets recv function has only one output parameter: the number of bytes received. Therefore, Windows NT supplies the WSARecvEx function, which is identical to the recv function except that the flags parameter is an in-out parameter:
int
PASCAL
FAR
WSARecvEx (
SOCKET s,
char FAR * buf,
int len,
int *flags
);

On return from WSARecvEx, if a partial message was received, the MSG_PARTIAL bit is set in the flags parameter. If a complete message was received, MSG_PARTIAL is not set in flags. For a stream-oriented transport protocol, MSG_PARTIAL is never set on return from WSARecvEx; for stream transport protocols, this function behaves identically to the Windows Sockets recv function.
+$#A WSAEDISCON Error Code
After a virtual circuit is gracefully terminated on a stream-oriented transport protocol and all data has been received, the recv and WSARecvEx functions return zero to indicate that there will never be any more data to read on the connection. However, most message-oriented transport procotols support the concept of a zero-length message. Applications therefore need a mechanism to distinguish a zero-length message and graceful circuit termination.
The Windows Sockets interface in Windows NT uses the WSAEDISCON error code to solve this problem. When the circuit is gracefully terminated, this error code is returned to the application, and a return value of zero is used to indicate a message of length zero.
Following is an example of client-side code that establishes an SPX connection, and receives and processes messages until the connection is terminated.
SOCKET s;
SOCKADDR_IPX sockaddrIpx;
INT err;
INT bytesReceived;
INT flags;

/* open the SPX socket handle */
s = socket( AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX );

/* initialize the sockaddr for the remote machine */
sockaddrIpx.sa_family = AF_IPX;
sockaddrIpx.sa_netnum = 0;
sockaddrIpx.sa_nodenum = 123; /* substitute real nodenum here */
sockaddrIpx.sa_socket = 1; /*substitute real socket number here */

/* connect the socket */
err = connect(
s,
(PSOCKADDR)&sockaddrIpx,
sizeof(sockaddrIpx) );
if ( err != 0 ) {
/* indicate connection failure */
}

/* receive data until the connection terminates */
while ( TRUE ) {

/* use WSARecvEx to receive data */
flags = 0;
bytesReceived = WSARecvEx( s, buffer, buflen, &flags );

/* check for error or graceful termination */
if ( bytesReceived < 0 ) {
if ( WSAGetLastError( ) == WSAEDISCON ) {
/* graceful termination */
break;
}
/* some other error, indicate to user */
break;
}

/* check for partial message */
if ( (flags & MSG_PARTIAL) != 0 ) {
/* a partial message; handle specially */
}

/* check for 0-length message */
if ( bytesReceived == 0 ) {
/* 0-length message, process */
} else {
/* message contained data */
}
}

/* circuit terminated or error, close socket */
closesocket( s );

+$#A Windows NT Support for Connect and Disconnect Data
Some transport protocols, such as DECNet and OSI TP4, support the concept of connect and disconnect data. This is additional data, not in the normal data stream, that is sent on the wire along with connect or disconnect requests and is typically used for things like application-level version negotiation. The TDI interface provides support for connect and disconnect data through the TDI_CONNECTION_INFORMATION structure, which has fields for UserDataLength, UserData, OptionsLength, and Options and is passed on TDI connect, accept, and disconnect requests.
The sockets interface, however, does not provide input parameters for connect data on the connect function or for disconnect data on the shutdown and closesocket functions. However, it is relatively natural to use the setsockopt function to specify connect and disconnect data to send, and to use the getsockopt function to retrieve connect and disconnect data sent by the remote side. To support this, the following socket options are included with the Windows NT version of the Windows Sockets header file (WINSOCK.H):
/*
* Options for connect and disconnect data and options. Used only by
* non-TCP/IP transports such as DECNet, OSI TP4, etc.
*/
#define SO_CONNDATA 0x7000
#define SO_CONNOPT 0x7001
#define SO_DISCDATA 0x7002
#define SO_DISCOPT 0x7003
#define SO_CONNDATALEN 0x7004
#define SO_CONNOPTLEN 0x7005
#define SO_DISCDATALEN 0x7006
#define SO_DISCOPTLEN 0x7007

How an application uses these options in conjunction with getsockopt and setsockopt depends on the type of application, server or client, and how it will use connect data. Each case is explained following.
Connect and disconnect options are semantically the same as connect and disconnect data, from the standpoint of the Windows Sockets interface. The "options" are really just a buffer of data that is passed to the transport protocol prior to the connect or disconnect, and a buffer that is retrieved following a connect or disconnect. Therefore, an application uses connect or disconnect options in much the same manner it uses connect or disconnect data. The only difference is the option names used on the getsockopt and setsockopt calls; for example, use SO_CONNOPT in place of SO_CONNDATA.
+$#A Client Applications and Connect Data
A Windows Sockets client application written for a transport protocol that supports connect data should call setsockopt with SOL_SOCKET as the level, SO_CONNDATA as the option name, and a buffer in the option value field that contains the connect data it intends to send. This call must be made before calling the connect function. The specified connect data is then sent on the connect request.
Two steps are required to retrieve response connect data. First, before the connect call, the application must inform the Windows Sockets DLL how much space to reserve for the response connect data. This is done by calling setsockopt with option level SOL_SOCKET, option name SO_CONNDATALEN, and having the option value parameter point to an integer that contains the maximum number of connect data bytes expected. The Windows Sockets DLL will set aside a buffer of this size to contain connect data sent by the server application.
After the connect has completed successfully, the application can call getsockopt with an option level of SOL_SOCKET, an option name of SO_CONNDATA, and a buffer in the option value parameter large enough to hold the response connect data. The Windows Sockets DLL then copies the response connect data into the caller's buffer.
Note that the Windows Sockets DLL keeps the response connect data buffer valid for the lifetime of the socket. Therefore, an application can retrieve the response connect data for the socket at any time after the socket is connected and before it is closed.
+$#A Server Applications and Connect Data
A server application's use of connect data implicitly differs in two important ways from a client application's use. First, a server application receives the client's connect data before it sends its own. Second, because the socket's interface does not support the concept of delayed accept, a server application is limited to sending the same response connect data to all clients.
A server application that is to receive connect data should use setsockopt with the SO_CONNDATALEN option to force the Windows Sockets DLL to reserve buffer space into which to receive connect data for incoming connect requests. The specified amount of connect data buffer space is used for all incoming connects, but each connect gets a separate buffer. After a new connected socket is accepted with accept the application calls getsockopt with the SO_CONNDATA option and a buffer large enough to hold expected response data. The Windows Sockets DLL fills in this buffer with the connect data sent by the client.
To send connect data, a server application calls setsockopt with the SO_CONNDATA option and a buffer of data before incoming connections are accepted (typically before listen). Thereafter, any client that connects to the server will receive the specified connect data. The connect data sent by a server can be changed at any time with another setsockopt call, which discards the previous data and replaces it with new connect data.
+$#A Disconnect Data
Client and server applications send and receive disconnect data in a manner similar to the way a client application sends and receives connect data. To send disconnect data the application calls setsockopt with the SO_DISCDATA option and a buffer of disconnect data to send. The data will then be sent either when the application calls shutdown to disconnect the send side of the socket or when the application calls closesocket on the socket.
To receive disconnect data, an application must call setsockopt with the SO_DISCDATALEN option before a remote closes the connection. This option specifies the amount of disconnect data the application expects to receive.
After the remote has disconnected, the SO_DISCDATA option on getsockopt retrieves the disconnect data sent by the remote. The application passes in a buffer large enough to hold all the disconnect data, and the Windows Sockets DLL copies the received disconnect data into the buffer.
+$#A Additional Items
The following topics provide details on additional items in Windows Sockets.
· Blocking Sockets Calls
· WSAGetLastError vs. GetLastError
· Closing Sockets: Graceful vs. Abortive
· Notes on Performance
· Windows NT Extensions to WINSOCK.H

+$#A Blocking Sockets Calls
The Windows Sockets specification defines a "blocking hook" mechanism, accessed through the WSASetBlockingHook, WSAUnhookBlockingHook, WSAIsBlocking, and WSACancelBlockingCall functions. The blocking hook works by polling for Windows messages while a Windows Sockets function call is blocking on network activity and calling back into the application when a message arrives. This mechanism allows a measure of multitasking to occur within the nonpreemptive Windows environment. In fact, an application that makes blocking calls gets a "default blocking hook" installed, which can result in reentrance to the application if the application makes any calls that must block.The blocking hook is useful for promoting multitasking, but Windows NT, being a fully preemptive, multitasking operating system, doesn't need any help from applications to do multitasking. Additionally, programming Windows applications that use the blocking is complex, requiring special attention to reentrance issues. Therefore, 32-bit applications for Windows NT do not get a default blocking hook installed. If a thread makes a blocking call, the thread simply blocks, and the system schedules other threads as appropriate, waking up the blocked thread when the I/O completes.
If a 32-bit application really does require the blocking hook semantics, such an application can use WSASetBlockingHook to install any blocking hook on a per-thread basis. This blocking hook will operate exactly as specified in the Windows Sockets specification. Note that the fact that the blocking hook is per-thread means two things for multithreaded applications: If they need blocking hooks, they must install a blocking hook for every thread that makes blocking Windows Sockets calls; and they can install a different blocking hook in diff erent threads of their process, allowing different blocking hook behavior in different threads.
If a 16-bit application is ported to Win32 and depends on the behavior of the default blocking hook, the application can install as a blocking hook code that performs the same actions as the default blocking hook:
BOOL
DefaultBlockingHook (
VOID
)
{
MSG msg;
BOOLEAN retrievedMessage;

//
// Get the next message, if any.
//

retrievedMessage = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

//
// Process the message if we got one.
//

if (retrievedMessage) {
TranslateMessage((CONST MSG *)&msg);
DispatchMessage((CONST MSG *)&msg);
}

//
// If we got a message, indicate that we want to be called again.
//

return retrievedMessage;

} // DefaultBlockingHook

Note that 16-bit Windows Sockets applications running in the 16-bit subsystem of Windows NT do get the default blocking hook semantics as described in the Windows Sockets specification, so there is no need to modify 16-bit applications to take advantage of the blocking hook behavior.
+$#A WSAGetLastError vs. GetLastError
Windows Sockets applications use the WSAGetLastError function to retrieve specific error information on failed function calls. For 32-bit applications under Windows NT, the WSAGetLastError function is just a call through to theWin32 GetLastError call, which provides identical functionality. Therefore, if an application makes a Windows Sockets call that fails, then a Win32 function call that also fails, the result of WSAGetLastError will give the error code for the Win32 function failure. In addition, some C run-time calls, specifically printf, reset the thread's last error to zero, so a call to WSAGetLastError after one of these function calls can return the value zero rather than the actual failure code. It is strongly recommended that applications be written to call WSAGetLastError (or GetLastError) immediately after a failed Windows Sockets call is detected without any intervening function calls.
Note also that WSASetLastError is just a call through to SetLastError, so calling WSASetLastError will reset the error obtained by GetLastError.
+$#A Closing Sockets: Graceful vs. Abortive
For transport protocols that support graceful close, Windows NT has been carefully designed to perform graceful closes when appropriate, and to perform abortive close operations when appropriate. By default, closing a connected socket with closesocket results in a graceful close of the virtual circuit. In addition, using shutdown on the send side of a socket initiates a graceful close. If an application is to force an abortive close, it should set the SO_LINGER option on the socket to have a timeout of zero.
Windows NT uses an abortive close to indicate to the remote end that a connection was terminated in an ungraceful manner. There are several causes for this:
· Closing a socket when there is unreceived data buffered on the socket.
· Exiting a process without calling closesocket on all connected sockets.
· Data arrives after the graceful close has been initiated.
· The SO_LINGER option is nonzero, but some data remains unsent after the timeout expires.

To ensure a graceful close on a virtual circuit, an application must receive all data on the socket and manually call closesocket on the socket. To do this properly, some application protocols require the shutdown function to initiate graceful close in conjunction with recv or WSARecvEx to ensure that all data sent by the remote is received locally.
+$#A Notes on Performance
Application performance can be tuned so that data can be transferred as quickly as possible with minimal burden on the system CPU. There are several general guidelines that can help you tune your code. Many of these simple guidelines will seem obvious, but it is surprising how often they are overlooked.
First, send and receive data in chunks as large as possible, up to approximately 64 kilobytes. There is a certain amount of unavoidable overhead in each send or recv call, and sending and receiving large chunks of data spreads the overhead over fewer calls.
Second, do as little in performance paths as possible. Do not include any extraneous calls, either Windows Sockets or Win32. If you need to send a lot of data, do it in a tight loop without any calls other than send. If you must specify a timeout on the send, do not use the select function, which has the inherent overhead of a kernel transition. Instead, set the SO_SNDTIMEO option on the socket and use send only. If you're handling several sockets in your application, use threads so that you can avoid calls to select (but be careful not to use too many threads ¾ threads cost memory and introduce context switching overhead).
It is possible to achieve very high data transfer rates with the Windows Sockets interface in Windows NT. With TCP/IP on a 10-megabit-per-second ethernet, we have seen rates of more than a megabyte per second, which is close to the theoretical maximum for the medium, and the system CPUs were not fully saturated. This demonstrates that in a carefully coded and tuned application it is possible to achieve excellent performance through Windows Sockets.
+$#A Windows NT Extensions to WINSOCK.H
To support some of the features unique to Windows NT described in this document, the WINSOCK.H header file that is included with the Win32 SDK contains some additional information, as shown:
/*
* Options for connect and disconnect data and options. Used only by
* non-TCP/IP transports such as DECNet, OSI TP4, etc.
*/
#define SO_CONNDATA 0x7000
#define SO_CONNOPT 0x7001
#define SO_DISCDATA 0x7002
#define SO_DISCOPT 0x7003
#define SO_CONNDATALEN 0x7004
#define SO_CONNOPTLEN 0x7005
#define SO_DISCDATALEN 0x7006
#define SO_DISCOPTLEN 0x7007

/*
* Option for opening sockets for synchronous access.
*/
#define SO_OPENTYPE 0x7008

#define SO_SYNCHRONOUS_ALERT 0x10
#define SO_SYNCHRONOUS_NONALERT 0x20

#define WSAEDISCON (WSABASEERR+101)

int PASCAL FAR WSARecvEx (SOCKET s, char FAR * buf, int len, int *flags);

+$#A Windows Sockets Service Registration and Name Resolution
The service registration and resolution functionality helps you write client server applications. Using this functionality, a service can register its existence with a name space (registration), and a client can find a service within a name space (resolution).
+$#A Service Registration and Resolution Definitions
The following functions and structures let you implement service registration and resolution functionality.
+$#A Service Registration and Resolution Functions
Use these functions to implement service registration and resolution functionality:
EnumProtocols_win32_EnumProtocolsGetAddressByName_win32_GetAddressByNameGetNameByType_win32_GetNameByTypeGetService_win32_GetServiceGetTypeByName_win32_GetTypeByNameSetService_win32_SetService
+$#A Service Registration and Resolution Structures
Use these structures to implement service registration and resolution functionality:
BLOB_win32_BLOB_strCSADDR_INFO_win32_CSADDR_INFO_strNS_SERVICE_INFO_win32_NS_SERVICE_INFO_strPROTOCOL_INFO_win32_PROTOCOL_INFO_strSERVICE_ADDRESS_win32_SERVICE_ADDRESS_strSERVICE_ADDRESSES_win32_SERVICE_ADDRESSES_strSERVICE_INFO_win32_SERVICE_INFO_strSERVICE_TYPE_INFO_ABS_win32_SERVICE_TYPE_INFO_ABS_strSERVICE_TYPE_VALUE_ABS_win32_SERVICE_TYPE_VALUE_ABS_str
+$#A Introduction to Audio
Microsoft® Windows® provides several different types and levels of audio services. You should become familiar with these types and levels so you can choose the services appropriate for your application. This overview assumes you have a basic knowledge of digital audio and Musical Instrument Digital Interface (MIDI).
+$#A About Audio Services
This overview covers the following topics:
· Types of audio services_win32_Types_of_Audio_Services
· Levels of audio services_win32_Levels_of_Audio_Services
· Windows audio architecture_win32_Windows_Audio_Architecture
· Audio file formats_win32_Audio_File_Formats
· Further reading_win32_Further_Reading

+$#A Types of Audio Services
Different types of audio services require different formats for the audio information and different technologies to reproduce audio. Windows offers the following types of audio services:
· Waveform audio services provide playback and recording support for digital audio hardware. Waveform audio is useful for reproducing nonmusical audio material such as sound effects and voice-over narration. Waveform audio has moderate storage-space and data-transfer rate requirements ¾ as low as 11 kilobytes per second of audio.
· MIDI audio services provide support for MIDI-file and MIDI-event playback (through internal or external synthesizers) and MIDI-event recording. MIDI audio is useful with music-related applications such as music composition and MIDI sequencer programs. Because they require less storage space and a lower data-transfer rate than waveform audio, you may want to use MIDI audio services to provide introductory and background music in your applications.
· Compact disc (CD) audio services provide support for playback of Red Book audio information on CDs by means of the CD-ROM drive on multimedia computers. CD audio offers the highest quality reproduction of musical material but has the highest storage-space requirements ¾ 176 kilobytes per second of audio. You cannot read from the CD-ROM drive while playing CD audio.

+$#A Levels of Audio Services
The different levels of audio services let you choose services appropriate to the requirements of your application and to your programming ability. Windows offers two levels of audio services:
· High-level audio services allow you to play and record audio files by using as little as one function call. Compared with low-level audio services, high-level services are easier to use and require less programming. High-level services also meet the audio requirements of most applications.
· Low-level audio services allow you to communicate directly with audio device drivers to manage playback and recording. Low-level audio services require more programming than high-level services but give you more control over audio playback and recording.

The easiest way to play and record audio is to use high-level audio services. If the high-level audio services do not meet the audio needs of your application, you can use low-level audio services to write your own routines to manage audio playback and recording.
+$#A Windows Audio Architecture
Windows provides audio services through high-level audio functions, Media Control Interface (MCI) device drivers, low-level audio functions, the MIDI Mapper, and low-level audio device drivers. The following illustration shows the relationship between an application and the elements of Windows that provide audio support.
+$#A High-Level Audio Support
MCI functions, along with MCI device drivers, provide high-level audio support for MIDI, waveform, and CD audio devices. MCI drivers can play disk-resident (hard disk and CD-ROM) audio files in the background while an application runs in the foreground. In addition to MCI, Windows provides high-level audio support with three special-purpose functions that play waveform sounds: MessageBeep_win32_MessageBeep, PlaySound!AL(`_win32_PlaySound',3,`No_SDK'), and sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK'). You can also use these functions to play waveform sounds associated with system alerts and sounds identified by entries in the registry.
High-level audio functions should meet the audio requirements of most applications. For information about using MCI and the MessageBeep, PlaySound, and sndPlaySound functions, see High-Level Audio_win32_High_Level_Audio.
+$#A Low-Level Audio Support
Low-level audio functions provide a device-independent interface to audio hardware in multimedia computers. These functions supply low-level audio services by letting applications communicate directly with the audio device drivers. MCI drivers and the MessageBeep_win32_MessageBeep, PlaySound!AL(`_win32_PlaySound',3,`No_SDK'), and sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') functions use these low-level functions to provide high-level audio services.
Low-level audio functions are provided for applications ¾ such as audio tools programs ¾ that have special-purpose audio requirements. For example, a MIDI sequencer application would use low-level audio functions to record and play MIDI data. For information about using low-level audio services, see Low-Level Audio_win32_Low_Level_Audio.
+$#A The MIDI Mapper
The MIDI Mapper provides standard patch services for device-independent playback of MIDI files. Standard patch services ensure that different MIDI synthesizers use the same instrument sounds to reproduce the music in a MIDI file.
The MIDI Mapper translates and redirects messages sent to it by low-level MIDI output functions. Because the high-level MIDI services use low-level MIDI output functions, the MIDI Mapper can be used with high-level as well as low-level MIDI services. For information about the architecture of the MIDI Mapper and how to use the MIDI Mapper with high-level audio services, see High-Level Audio_win32_High_Level_Audio. For information about using the MIDI Mapper with low-level audio services, see Low-Level Audio_win32_Low_Level_Audio. MIDI files must follow certain authoring guidelines to use the standard patch services provided by the MIDI Mapper. For details about these authoring guidelines, see High-Level Audio_win32_High_Level_Audio.
+$#A Audio File Formats
Windows supports a tagged file structure called the Resource Interchange File Format (RIFF). There are two RIFF file formats currently defined for audio files.
RIFF type Filename extension Description
RMID .RMI MIDI audio file
WAVE .WAV Waveform audio file

The WAVE file format supports a number of digital audio data formats. All multimedia computers support pulse code modulated (PCM) formats of 8-bit monaural data at sample rates of 11.025 kilohertz (kHz) and 22.05 kHz.
In addition to Resource Interchange File Format MIDI (RMID) files, the MCI MIDI sequencer plays standard MIDI files in the format defined by the International MIDI Association in the "Standard MIDI Files 1.0" specification. For information about how to obtain this specification, see Further Reading_win32_Further_Reading. RMID files are standard MIDI files with a RIFF header.
+$#A Using RIFF Files
The multimedia file input and output (I/O) services include functions for working with RIFF files. For information about using these functions, see Multimedia File Input/Output_win32_Multimedia_File_Input_Output.
+$#A Further Reading
For more information on digital audio, digital-signal processing, and computer music, see the following books:
Boom, Michael. Music Through MIDI. Redmond: Microsoft Press, 1987.
Chamberlin, Hal. Musical Applications of Microprocessors. Hasbrouk Heights: Hayden Book Company, Inc., 1985.
Pohlmann, Ken. Principles of Digital Audio. Indianapolis: Howard W. Sams & Co., 1985.
Roads, Curtis, and John Strawn. Foundations of Computer Music. Cambridge: The MIT Press, 1985.
Strawn, John F. Digital Audio Engineering, An Anthology. Los Altos: William Kaufmann, Inc., 1985.
Strawn, John F. Digital Audio Signal Processing, An Anthology. Los Altos: William Kaufmann, Inc., 1985.
For information about MIDI and standard MIDI files, see the following MIDI specifications:
MIDI 1.0 Detailed Specification
Standard MIDI Files 1.0 Specification
These specifications are available from the International MIDI Association at the following address:
International MIDI Association5316 West 57th StreetLos Angeles, CA 90056

+$#A High-Level Audio
This overview explains how to use the high-level audio services of Microsoft® Windows® to add sound to applications. For an overview of the audio services, see Introduction to Audio_win32_Introduction_to_Audio.
This overview covers the following topics:
· Playing waveform sounds_win32_Playing_Waveform_Sounds
· Playing and Recording Audio Using the Media Control Interface!AL(`_win32_Playing_and_Recording_Audio_Using_the_Media_Control_Interface',3,`No_SDK')
· Opening waveform and MIDI sequencer devices_win32_Opening_Waveform_and_MIDI_Sequencer_Devices
· Authoring MIDI files_win32_Authoring_Guidelines_for_MIDI_Files

The topics in this overview assume you have a basic knowledge of digital audio and MIDI. For additional information on these subjects, see Further Reading_win32_Further_Reading.
+$#A Function Prefixes
High-level audio function names begin with the following prefixes:
Prefix Function group
snd System-alert sound functions
mci MCI functions

+$#A Playing Waveform Sounds
One basic way to use sound is to associate a sound with a user action or with a warning or alert message. These sounds tend to be short in duration and are often played repeatedly. Windows provides three functions to play waveform sounds.
Function Description
MessageBeep_win32_MessageBeep Plays the sound that corresponds to a given system-alert level.
PlaySound!AL(`_win32_PlaySound',3,`No_SDK') Plays the sound that corresponds to the given filename or registry entry.
sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') Plays the sound that corresponds to the given filename or registry entry.

These functions provide methods of playing the following waveform sounds:
· WAVE files stored on a hard disk or CD-ROM
· In-memory WAVE resources
· WAVE files specified by entries in the registry
· WAVE files associated with system-alert levels

+$#A Restrictions in Playing Waveform Sounds
There are some restrictions to using the MessageBeep, PlaySound, and sndPlaySound function to play waveform sounds:
· The entire sound must fit in available physical memory.
· The sound must be in a data format playable by one of the installed waveform audio device drivers.

Use MessageBeep_win32_MessageBeep, PlaySound!AL(`_win32_PlaySound',3,`No_SDK'), and sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') to play WAVE files that are relatively small in size ¾ up to about 100 kilobytes. For larger sound files, use the MCI services. For information about using MCI, see Playing and Recording Audio Using the Media Control Interface!AL(`_win32_Playing_and_Recording_Audio_Using_the_Media_Control_Interface',3,`No_SDK').
+$#A Using the PlaySound and sndPlaySound Functions
The PlaySound!AL(`_win32_PlaySound',3,`No_SDK') and sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') functions can be used to perform the following tasks:
· Playing WAVE files_win32_Playing_WAVE_Files
· Looping High-Level Audio Sounds_win32_Looping_High_Level_Audio_Sounds
· Playing High-Level Audio WAVE Resources_win32_Playing_High_Level_Audio_WAVE_Resources
· Playing High-Level Audio Sounds Specified in the Registry_win32_Playing_High_Level_Audio_Sounds_Specified_in_the_Registry

Although the examples shown in these topics use the sndPlaySound function, most of the sample code works with the PlaySound function as well.
+$#A Playing WAVE Files
The following statement plays the C:\SOUNDS\BELLS.WAV file.
sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_SYNC);

If the specified file does not exist or does not fit into the available physical memory, sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') plays the default system sound specified by the SystemDefault entry in the [sounds] section of the registry. If there is no SystemDefault entry, sndPlaySound fails without producing any sound. If you don't want the default system sound to play, specify the SND_NODEFAULT flags in the following example.
sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_SYNC | SND_NODEFAULT);

+$#A Looping High-Level Audio Sounds
If you specify the SND_LOOP and SND_ASYNC flags for the wFlags parameter of the sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') function, as in the following example, the sound continues to play repeatedly.
sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_LOOP | SND_ASYNC);

If you want to "loop" a sound, you must play it asynchronously. You cannot use the SND_SYNC flag with the SND_LOOP flag. A looped sound continues to play until sndPlaySound is called to play another sound. To stop playing a looped or asynchronous sound without playing another sound, use the following statement.
sndPlaySound(NULL, 0);

+$#A Playing High-Level Audio WAVE Resources
You can also build WAVE files into an application as resources and use the sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') function to play these sounds by specifying the SND_MEMORY flag. The SND_MEMORY flag indicates that the lpszSoundName parameter is a pointer to an in-memory image of the WAVE file. To include a WAVE file as a resource in an application, add the following entry to the application's resource script (.RC) file.
soundName WAVE c:\sounds\bells.wav

The name soundName is a placeholder for a name that you supply to refer to this WAVE resource. WAVE resources are loaded and accessed just like other application-defined Windows resources. The function in the following example plays a specified WAVE resource.
/* Plays a specified WAVE resource. */

BOOL PlayResource(LPSTR lpName)
{
BOOL bRtn;
LPSTR lpRes;
HANDLE hResInfo, hRes;

/* Find the WAVE resource. */

hResInfo = FindResource(hInst, lpName, "WAVE");
if (hResInfo == NULL)
return FALSE;

/* Load the WAVE resource. */

hRes = LoadResource(hInst, hResInfo);
if (hRes == NULL)
return FALSE;

/* Lock the WAVE resource and play it. */

lpRes = LockResource(hRes);
if (lpRes != NULL) {
bRtn = sndPlaySound(lpRes, SND_MEMORY | SND_SYNC |
SND_NODEFAULT);
UnlockResource(hRes);
}
else
bRtn = 0;

/* Free the WAVE resource and return success or failure. */

FreeResource(hRes);
return bRtn;
}

To play a WAVE resource by using this function, pass to the function a pointer to a string containing the name of the resource, as shown in the following example.
PlayResource("soundName");

+$#A Playing High-Level Audio Sounds Specified in the Registry
The sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') function also plays waveform sounds referred to by a key-name in the [sounds] section of the registry. This enables users to assign their own sounds to system alerts and warnings, or to user actions such as mouse-button click. For example, the [sounds] section of the registry might look like the following.
[sounds]
SystemDefault=C:\SOUNDS\BUMMER.WAV, Says BUMMER
SystemAsterisk=C:\SOUNDS\WHALES.WAV, Whale sounds
SystemExclamation=C:\SOUNDS\LASER.WAV, Laser gun sounds
SystemHand=C:\SOUNDS\OHOH.WAV, Says OH-OH
SystemQuestion=C:\SOUNDS\JIBERISH.WAV, Person mumbling
SystemStart=C:\SOUNDS\CHORD.WAV, Plays a chord
MouseClick=C:\SOUNDS\CLICK.WAV, Recording of a clicking sound

To play a sound identified by a registry entry, call sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') with the lpszSoundName parameter pointing to a string that contains the name of the entry that identifies the sound. For example, to play the sound associated with the MouseClick entry in the [sounds] section of the registry and wait for the sound to finish before returning, use the following statement.
sndPlaySound("MouseClick", SND_SYNC);

If the specified registry entry or the waveform file it identifies does not exist, or if the sound does not fit into the available physical memory, sndPlaySound plays the default system sound specified by the SystemDefault entry. If there is no SystemDefault entry, sndPlaySound fails without producing any sound. If you don't want the default system sound to play, specify the SND_NODEFAULT flag, as in the following example.
sndPlaySound("MouseClick", SND_SYNC | SND_NODEFAULT);

Note The sndPlaySound function always searches the [sounds] section of the registry for a keyname matching the lpszSoundName parameter before attempting to load a file with this name.
The [sounds] section of the registry includes an optional string following the waveform filename for descriptive text about the sound in the file. This descriptive text is displayed in the Sound option of the Control Panel.

+$#A Playing System-Alert Sounds
The MessageBeep_win32_MessageBeep function uses the waveform audio hardware to produce a variety of user-selectable sounds.
When you call MessageBeep, it searches the [sounds] section of the registry for the WAVE file that corresponds to the specified alert level. Following are the registry entries for system-alert sounds.
Registry Entry Description
SystemDefault Identifies the sound produced when the MessageBeep function is called with the wAlert parameter set to an invalid alert level or when the requested alert sound can't be found. This is called the default system sound.
SystemAsterisk Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONASTERISK.
SystemExclamation Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONEXCLAMATION.
SystemHand Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONHAND.
SystemQuestion Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONQUESTION.

If MessageBeep is called with wAlert set to zero, it plays the default system sound. If wAlert is set to -1, MessageBeep uses the computer speaker to produce a standard beep sound. If MessageBeep can't play the requested sound, it plays the default system sound. If it can't play the default system sound, then it produces a beep sound using the computer speaker.
The Sound option of the Control Panel enables users to set these registry entries to customize their environment for system-alert sounds. It also enables users to disable these alert sounds.
+$# Playing and Recording Audio Using the Media Control Interface
The Media Control Interface (MCI) provides a high-level interface for controlling both internal and external media devices. It provides support for playing wave- form, MIDI, and compact disc audio and for recording waveform audio. MCI is the easiest way for multimedia applications to play and record audio.
MCI uses device drivers to interpret and execute high-level MCI commands. MCI device drivers can stream digital audio and MIDI data directly from a storage device to the appropriate device driver, allowing applications to play files too large to fit in available physical memory. This data streaming takes place in the background while an application is running. The application is responsible only for setting up MCI and telling it to start playing or recording.
MCI provides two types of interface: command-message and command-string. This section explains how to use the command-message interface for waveform, MIDI, and compact disc audio devices. For an introduction to MCI and for information about the command-string interface, see Media Control Interface_win32_Media_Control_Interface.
+$#A MCI Audio Data Types
The MMSYSTEM.H header file defines data types and function prototypes for MCI. You must include this header file in any source-code module that uses MCI functions.
Following are the data types for structures used with extended MCI commands related to audio.
Type Description
MCI_SEQ_SET_PARMS!AL(`_win32_MCI_SEQ_SET_PARMS',3,`No_SDK') Specifies a structure for the MCI_SET command message for MIDI sequencer devices.
MCI_WAVE_DELETE_PARMS!AL(`_win32_MCI_WAVE_DELETE_PARMS',3,`No_SDK') Specifies a structure for the MCI_DELETE command message for waveform audio devices.
MCI_WAVE_OPEN_PARMS!AL(`_win32_MCI_WAVE_OPEN_PARMS',3,`No_SDK') Specifies a structure for the MCI_OPEN command message for waveform audio devices.
MCI_WAVE_SET_PARMS!AL(`_win32_MCI_WAVE_SET_PARMS',3,`No_SDK') Specifies a structure for the MCI_SET command message for waveform audio devices.

+$#A MCI Audio Commands
MCI provides a standard set of commands applying to all types of media devices. Some of these commands can be extended to accommodate unique features of a particular type of device.
Following are the MCI command messages that perform common audio playback and recording tasks.
MCI command message Audio task
MCI_OPEN!AL(`_win32_MCI_OPEN',3,`No_SDK') Open an audio device.
MCI_CLOSE!AL(`_win32_MCI_CLOSE',3,`No_SDK') Close an audio device.
MCI_PLAY!AL(`_win32_MCI_PLAY',3,`No_SDK') Play all or part of audio selection; resume playback from pause.
MCI_STOP!AL(`_win32_MCI_STOP',3,`No_SDK') Stop playback.
MCI_PAUSE!AL(`_win32_MCI_PAUSE',3,`No_SDK') Pause playback.
MCI_SEEK!AL(`_win32_MCI_SEEK',3,`No_SDK') Change current location.
MCI_CUE!AL(`_win32_MCI_CUE',3,`No_SDK') Cue a device so playback or recording begins with minimum delay.
MCI_RECORD!AL(`_win32_MCI_RECORD',3,`No_SDK') Begin recording on a waveform audio device.
MCI_SAVE!AL(`_win32_MCI_SAVE',3,`No_SDK') Save a recorded waveform audio file.
MCI_INFO!AL(`_win32_MCI_INFO',3,`No_SDK') Query device information such as the product name and the name of the device element currently associated with the device (returns information in string format).
MCI_GETDEVCAPS!AL(`_win32_MCI_GETDEVCAPS',3,`No_SDK') Query device capabilities such as the device name and number of inputs and outputs (if the device can record).
MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') Query device status such as current playback position, media length, media format, time format, record level, CD audio track, and MIDI sequencer tempo.
MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') Set device parameters such as time format, waveform data format, and MIDI sequencer tempo.

+$#A Opening MCI Audio Devices
MCI requires that audio devices be opened before they can be accessed. Use the MCI_OPEN command message along with the MCI_OPEN_PARMS!AL(`_win32_MCI_WAVE_DELETE_PARMS',3,`No_SDK') structure to open an audio device and obtain an MCI device identifier. Subsequent commands use this device identifier to identify the device to receive the command.
The MCI device identifier is returned in the wDeviceID member. Always check the return value of the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function after sending an MCI_OPEN command message before using this device identifier. A nonzero return value indicates that there was an error in opening the device and that the returned device identifier is not valid.
For waveform audio devices, you can use an extended structure, MCI_WAVE_OPEN_PARMS!AL(`_win32_MCI_WAVE_OPEN_PARMS',3,`No_SDK'). This structure has a dwBufferSeconds member to specify the number of seconds of buffering used by the MCI waveform device driver. If you use this member, you must specify the MCI_WAVE_OPEN_BUFFER flag for the dwParam1 parameter of mciSendCommand to validate the member.
If you do not want to specify the number of seconds of buffering for the driver to use, you can use the MCI_WAVE_OPEN_PARMS structure when you open waveform audio devices.
When you open a simple device, such as a compact disc audio device, you must specify the device name. When you open a compound device, such as a waveform or MIDI sequencer device, the device name is optional. Use the lpstrDeviceType member of the MCI_OPEN_PARMS!AL(`_win32_MCI_WAVE_DELETE_PARMS',3,`No_SDK') structure to specify the device name. MCI lets you use a string or a constant for this member. Following are the strings and constants for MCI audio devices.
Device String Constant
Compact disc cdaudio MCI_DEVTYPE_CD_AUDIO
Waveform waveaudio MCI_DEVTYPE_WAVEFORM_AUDIO
MIDI sequencer sequencer MCI_DEVTYPE_SEQUENCER

A string is the default format for specifying device names. If you use a constant to specify the device name, you must specify the MCI_OPEN_TYPE_ID flag in addition to the MCI_OPEN_TYPE flag.
+$#A Opening Waveform and MIDI Sequencer Devices
Waveform and MIDI sequencer devices are compound devices. Compound devices require an associated device element ¾ a WAVE or MIDI file. There are three ways to open compound devices:
· Specify only the device name.
· Specify only the device element and let MCI select the device associated with the file extension of the device element.
· Specify both the device name and the device element.

Specify only the device name when opening a device to query its capabilities with the MCI_GETDEVCAPS!AL(`_win32_MCI_GETDEVCAPS',3,`No_SDK') command message and when you plan to use the device to play more than one device element.
If you don't specify a device name when you open a compound device, MCI chooses an appropriate device type by comparing the file extension of the device element with entries in the [mci extensions] section of the registry. The following example uses this technique to open a MIDI sequencer device to play a MIDI file named CHOPIN.RMI.
UINT wDeviceID;
MCI_OPEN_PARMS mciOpenParms;
.ifdef REF
.
.
.
.endif
/* Open the device by specifying only the device element. */
mciOpenParms.lpstrElementName = "CHOPIN.RMI";
if (mciSendCommand(0, /* device ID */
MCI_OPEN, /* command */
MCI_OPEN_ELEMENT, /* flags */
(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */
/* Error, unable to open device. */
return FALSE;
else
/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

Instead of letting MCI choose the device, you can specify the device name when you open the device. The following example uses this technique to open a waveform audio device to play the BELLS.WAV file. This example uses a string to specify the device name. For an example using a constant to specify the device name, see Opening Compact Disc Devices_win32_Opening_Compact_Disc_Devices.
UINT wDeviceID;
MCI_OPEN_PARMS mciOpenParms;
.ifdef REF
.
.
.
.endif
/*
* Open the device by specifying both the device
* element and the device name.
*/
mciOpenParms.lpstrDeviceType = "waveaudio";
mciOpenParms.lpstrElementName = "C:\\SOUNDS\\BELLS.WAV";
if (mciSendCommand(0, /* device ID */
MCI_OPEN, /* command */
MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, /* flags */
(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */
/* Error, unable to open device. */
return FALSE;
else
/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

+$#A Using the MIDI Mapper With the MCI Sequencer
The MIDI Mapper is the default output device for the MCI MIDI sequencer. It provides standard patch services for device-independent playback of MIDI files. Applications that use the MCI sequencer to play MIDI files should use the MIDI Mapper. For details about the MIDI Mapper, see The MIDI Mapper_win32_The_MIDI_Mapper. For information about authoring device-independent MIDI files, see Authoring MIDI Files_win32_Authoring_MIDI_Files.
When you open the MCI MIDI sequencer, MCI attempts to select the MIDI Mapper as the output device. If the MIDI Mapper is unavailable because it is already in use, MCI selects another MIDI output device.

Note The MIDI Mapper currently supports only one client at a time in the current versions of Microsoft Windows NT.

After opening the sequencer, you should check to see if the MIDI Mapper was available and was selected as the output device. The following example uses the MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') command message to verify that the MIDI Mapper is the output device for the MCI sequencer.
UINT wDeviceID;
DWORD dwReturn;
MCI_STATUS_PARMS mciStatusParms;
.ifdef REF
.
.
.
.endif
/* Make sure the opened device is the MIDI Mapper. */
mciStatusParms.dwItem = MCI_SEQ_STATUS_PORT;
if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,
MCI_STATUS_ITEM, (DWORD)(LPVOID) &mciStatusParms)) {
/* Error sending MCI_STATUS command. */
}
if (LOWORD(mciStatusParms.dwReturn) == MIDI_MAPPER) {
/* The MIDI Mapper is the output device. */
}
else {
/* The MIDI Mapper is not the output device. */
}

MCI also provides a command message that explicitly selects the MIDI Mapper as the output device for the sequencer. The following example uses the MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') command message to select the MIDI Mapper as the output device for the MCI sequencer.
UINT wDeviceID;
DWORD dwReturn;
MCI_SEQ_SET_PARMS mciSeqSetParms;
.ifdef REF
.
.
.
.endif
/* Set the MIDI Mapper as the output port for the open device. */

mciSeqSetParms.dwPort = MIDI_MAPPER;
if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SEQ_SET_PORT,
(DWORD)(LPVOID) &mciSeqSetParms)) {
/* Error, unable to set Mapper as output port. */
}

Before querying the sequencer or setting an output port, you must successfully open the sequencer. Both of the previous examples assume that the wDeviceID parameter contains a valid device identifier for the sequencer.
+$#A Opening Compact Disc Devices
Because a compact disc audio device is a simple device, you need only specify the device name when opening it by using either a string or a constant device identifier. The following example opens a compact disc device by using a constant device identifier to specify the device name.
UINT wDeviceID;
MCI_OPEN_PARMS mciOpenParms;
.ifdef REF
.
.
.
.endif
/* Open the device by specifying a constant device ID. */

mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
if (mciSendCommand(0, /* device ID */
MCI_OPEN, /* command */
MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, /* flags */
(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */
/* Error, unable to open device. */
return FALSE;
else
/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

+$#A Handling High-Level Audio MCI Errors
Always check the return value of the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function. If it indicates an error, you can use the mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK') function to get a textual description of the error. You can also interpret the error code yourself; MMSYSTEM.H defines constants for MCI error return codes.

Note To interpret an error return value from mciSendCommand yourself, mask the high-order word; the low-order word contains the error code. However, if you pass the error return value to the mciGetErrorString function, you must pass the entire doubleword value.

The following example function takes the MCI error code specified by dwError, passes it to mciGetErrorString, and displays the resulting textual error description by using the MessageBox_win32_MessageBox function.
/*
* Uses mciGetErrorString to get a
* text description of an MCI error.
* Displays the error description by using MessageBox.
*/
void showError(DWORD dwError)
{
char szErrorBuf[MAXERRORLENGTH];

MessageBeep(MB_ICONEXCLAMATION);
if(mciGetErrorString(dwError, (LPSTR) szErrorBuf,
MAXERRORLENGTH))
MessageBox(hMainWnd, szErrorBuf,
"MCI Error", MB_ICONEXCLAMATION);
else
MessageBox(hMainWnd, "Unknown Error", "MCI Error",
MB_ICONEXCLAMATION);
}

+$#A Starting Playback
Once you successfully open an MCI audio device, you can use the MCI_PLAY!AL(`_win32_MCI_PLAY',3,`No_SDK') command message along with the MCI_PLAY_PARMS!AL(`_win32_MCI_PLAY',3,`No_SDK')_PARMS structure to begin playback. MMSYSTEM.H defines MCI_PLAY_PARMS as follows.
typedef struct {
DWORD dwCallback; /* callback for MCI_NOTIFY flag */
DWORD dwFrom; /* playback begin position */
DWORD dwTo; /* playback end position */
} MCI_PLAY_PARMS;

Playback begins at the current position in the device element. When a device element is opened, the current position is set to the beginning of the medium. After a device element is played, the current position is at the end of the medium. You can use the MCI_SEEK!AL(`_win32_MCI_SEEK',3,`No_SDK') command message to change the current position. For more information about MCI_SEEK, see Changing the Current Position_win32_Changing_the_Current_Position.
You can also set beginning and ending positions for playback by specifying the MCI_FROM and MCI_TO flags with the MCI_PLAY command message. If you specify one of these flags, you must fill in the corresponding dwFrom or dwTo member in the MCI_PLAY_PARMS structure with the desired beginning or ending time. If you are using a time format other than the default time format, you must set the time format before specifying a beginning or ending time with MCI_PLAY.
+$#A Example: Playing a WAVE File
The following example function opens a waveform device and plays the WAVE file specified by the lpszWAVEFileName parameter.
/*
* Plays a given WAVE file by using MCI_OPEN, MCI_PLAY.
* Returns when playback
* begins. Returns 0L on success;
* otherwise returns an MCI error code.
*/
DWORD playWAVEFile(HWND hWndNotify, LPSTR lpszWAVEFileName)
{
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;
MCI_PLAY_PARMS mciPlayParms;

/*
* Open the device by specifying the
* device name and device element.
* MCI will choose a device capable
* of playing the given file.
*/
mciOpenParms.lpstrDeviceType = "waveaudio";
mciOpenParms.lpstrElementName = lpszWAVEFileName;
if (dwReturn = mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)(LPVOID) &mciOpenParms)) {
/*
* Failed to open device;
* don't close it, just return error.
*/
return dwReturn;
}

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

/*
* Begin playback. The window-procedure function
* for the parent window is notified with an
* MM_MCINOTIFY message when playback is
* complete. The window procedure then
* closes the device.
*/
mciPlayParms.dwCallback = (DWORD) hWndNotify;
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,
MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

return 0;
}

+$#A Example: Playing a MIDI File
The following example function opens a MIDI sequencer device, verifies that the MIDI Mapper was selected as the output port, plays the MIDI file specified by the lpszMIDIFileName parameter, and closes the device after playback is complete.
/*
* Plays a given MIDI file using MCI_OPEN, MCI_PLAY.
* Returns as soon as playback begins. The window
* procedure function for the given window is notified
* when playback is complete. Returns 0L on success;
* otherwise returns an MCI error code.
*/
DWORD playMIDIFile(HWND hWndNotify, LPSTR lpszMIDIFileName)
{
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;
MCI_PLAY_PARMS mciPlayParms;
MCI_STATUS_PARMS mciStatusParms;

/*
* Open the device by specifying the
* device name and device element.
* MCI will attempt to choose the
* MIDI Mapper as the output port.
*/
mciOpenParms.lpstrDeviceType = "sequencer";
mciOpenParms.lpstrElementName = lpszMIDIFileName;
if (dwReturn = mciSendCommand(NULL, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)(LPVOID) &mciOpenParms)) {
/*
* Failed to open device;
* don't close it, just return error.
*/
return dwReturn;
}

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

/* See if the output port is the MIDI Mapper. */
mciStatusParms.dwItem = MCI_SEQ_STATUS_PORT;
if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,
MCI_STATUS_ITEM,
(DWORD)(LPVOID) &mciStatusParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

/*
* The output port is not the MIDI Mapper.
* Ask if user wants to continue.
*/
if (LOWORD(mciStatusParms.dwReturn) != MIDI_MAPPER) {
if (MessageBox(hMainWnd,
"The MIDI Mapper is not available. Continue?",
"", MB_YESNO) == IDNO) {
/*
* User does not want to continue. Not an error;
* just close the device and return.
*/
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return 0;
}
}

/*
* Begin playback. The window procedure function
* for the parent window is notified with an
* MM_MCINOTIFY message when playback is complete.
* The window procedure then closes the device.
*/
mciPlayParms.dwCallback = (DWORD) hWndNotify;
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,
MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

return 0;
}

+$#A Example: Playing a Compact Disc Track
The following example function opens a compact disc device, plays the track specified by the wTrack parameter, and closes the device after playback is complete.
/*
* Plays a given compact disc track using
* MCI_OPEN, MCI_PLAY. Returns as
* soon as playback begins. The window procedure
* function for the given window will be notified
* when playback is complete. Returns 0L on
* success; otherwise, it returns an MCI error code.
*/
DWORD playCDTrack(HWND hWndNotify, BYTE bTrack)
{
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;
MCI_SET_PARMS mciSetParms;
MCI_PLAY_PARMS mciPlayParms;

/*
* Open the compact disc device by specifying
* the device name.
*/
mciOpenParms.lpstrDeviceType = "cdaudio";
if (dwReturn = mciSendCommand(NULL, MCI_OPEN,
MCI_OPEN_TYPE, (DWORD)(LPVOID) &mciOpenParms)) {
/*
* Failed to open device;
* don't close it, just return error.
*/
return dwReturn;
}

/* Device opened successfully, get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

/* Set the time format to track/minute/second/frame. */
mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
if (dwReturn = mciSendCommand(wDeviceID, MCI_SET,
MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

/*
* Begin playback from the given track and play
* until the beginning of the next track. The window
* procedure function for the parent window will be
* notified with an MM_MCINOTIFY message when playback
* is complete. Unless the play command fails, the
* window procedure closes the device.
*/
mciPlayParms.dwFrom = MCI_MAKE_TMSF(bTrack, 0, 0, 0);
mciPlayParms.dwTo = MCI_MAKE_TMSF(bTrack + 1, 0, 0, 0);
mciPlayParms.dwCallback = (DWORD) hWndNotify;
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,
MCI_FROM | MCI_TO | MCI_NOTIFY,
(DWORD)(LPVOID) &mciPlayParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

return 0;
}

To specify a track-relative position with a compact disc device, you must use the track/minute/second/frame time format. For details, see Setting the Time Format_win32_Setting_the_Time_Format.
+$#A Changing the Current Position
To change the current position in a device element, use the MCI_SEEK!AL(`_win32_MCI_SEEK',3,`No_SDK') command message along with the MCI_TO flag and the MCI_SEEK_PARMS!AL(`_win32_MCI_SEEK',3,`No_SDK')_PARMS structure. MMSYSTEM.H defines MCI_SEEK_PARMS as follows.
typedef struct {
DWORD dwCallback; /* callback for MCI_NOTIFY flag */
DWORD dwTo; /* seek position */
} MCI_SEEK_PARMS;

If you use the dwTo member to specify a seek position with MCI_SEEK, you should query the time format and set it, if necessary.
In addition to specifying a position with the dwTo member, you can specify the MCI_SEEK_TO_START or MCI_SEEK_TO_END flags for the dwParam1 parameter of the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function to find the starting and ending positions of the device element. If you use one of these flags, don't specify the MCI_TO flag.
+$#A Setting the Time Format
Use the MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') command message along with the MCI_SET_PARMS!AL(`_win32_MCI_SET',3,`No_SDK')_PARMS structure to set the time format for an open device. Set the dwTimeFormat member to one of the following constants.
Constant Time format
MCI_FORMAT_BYTES Bytes (in pulse code modulated [PCM] format files)
MCI_FORMAT_MILLISECONDS Milliseconds
MCI_FORMAT_MSF Minute/second/frame
MCI_FORMAT_SAMPLES Samples
MCI_FORMAT_SMPTE_24 SMPTE, 24 frame
MCI_FORMAT_SMPTE_25 SMPTE, 25 frame
MCI_FORMAT_SMPTE_30 SMPTE, 30 frame
MCI_FORMAT_SMPTE_30DROP SMPTE, 30 frame drop
MCI_FORMAT_TMSF Track/minute/second/frame
MCI_SEQ_FORMAT_SONGPTR MIDI song pointer

The following example sets the time format to milliseconds on the device specified by wDeviceID:
UINT wDeviceID;
MCI_SET_PARMS mciSetParms;
.ifdef REF
.
.
.
.endif
/* Set time format to milliseconds. */
mciSetParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
if (mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT,
(DWORD) &mciSetParms))
/* Error, unable to set time format. */
return FALSE;
else
/* Time format set successfully. */
return TRUE;

+$#A Using the Minute/Second/Frame Time Format
In the minute/second/frame time format specified with the MCI_FORMAT_MSF constant, the time is relative to the beginning of the medium. The time is packed into a double as follows.
Word Byte Contents
High-order word High-order byte Unused
Low-order byte Frames
Low-order word High-order byte Seconds
Low-order byte Minutes

The MMSYSTEM.H header file defines the following macros to get and set elements of a minute/second/frame packed doubleword.
Macro Description
MCI_MSF_MINUTE!AL(`_win32_MCI_MSF_MINUTE',3,`No_SDK') Gets minute value in a minute/second/frame doubleword.
MCI_MSF_SECOND!AL(`_win32_MCI_MSF_SECOND',3,`No_SDK') Gets second value in a minute/second/frame doubleword.
MCI_MSF_FRAME!AL(`_win32_MCI_MSF_FRAME',3,`No_SDK') Gets frame value in a minute/second/frame doubleword.
MCI_MAKE_MSF!AL(`_win32_MCI_MAKE_MSF',3,`No_SDK') Sets minute, second, and frame values in a minute/second/frame doubleword.

Note The first few seconds of audio compact discs contain table-of-contents data. To play from the beginning of a disc using the minute/second/frame time format, use the MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') command message to get the position of the first track and play from that position.

+$#A Using the Track/Minute/Second/Frame Time Format
In the track/minute/second/frame time format specified with the MCI_FORMAT_TMSF constant, the time is relative to the beginning of the specified track. The time is packed into a doubleword, as follows.
Word Byte Contents
High-order word High-order byte Frames
Low-order byte Seconds
Low-order word High-order byte Minutes
Low-order byte Track

The MMSYSTEM.H header file defines the following macros to get and set elements of a track/minute/second/frame packed doubleword.
Macro Description
MCI_TMSF_TRACK!AL(`_win32_MCI_TMSF_TRACK',3,`No_SDK') Gets track value in a track/minute/second/frame doubleword.
MCI_TMSF_MINUTE!AL(`_win32_MCI_TMSF_MINUTE',3,`No_SDK') Gets minute value in a track/minute/second/frame doubleword.
MCI_TMSF_SECOND!AL(`_win32_MCI_TMSF_SECOND',3,`No_SDK') Gets second value in a track/minute/second/frame doubleword.
MCI_TMSF_FRAME!AL(`_win32_MCI_TMSF_FRAME',3,`No_SDK') Gets frame value in a track/minute/second/frame doubleword.
MCI_MAKE_TMSF!AL(`_win32_MCI_MAKE_TMSF',3,`No_SDK') Sets track, minute, second, and frame values in a track/minute/second/frame doubleword.

+$#A Using the SMPTE Time Formats
Society of Motion Picture and Television Engineers (SMPTE) time formats are based on standard time formats developed for the motion picture and television industries. For SMPTE time formats, the time is packed into a doubleword, as shown in the following table follows.
Word Byte Contents
High-order word High-order byte Frames
Low-order byte Seconds
Low-order word High-order byte Minutes
Low-order byte Hours

+$#A Closing MCI Audio Devices
After you finish using an MCI device, you must use the MCI_CLOSE!AL(`_win32_MCI_CLOSE',3,`No_SDK') command messages to close the device. If you are playing a compound device, such as a waveform or MIDI device, and want to play a different device element using the same device, close the device and reopen it using the new device element.

Note If you're playing multiple device elements with the same device, you'll get better performance if you open the device by explicitly specifying its name, then open, play, and close the individual device elements separately. Don't close the device until you have finished playing all of the device elements. Otherwise, the driver is reloaded each time you open the device.

+$#A Getting Information About Devices and Media
Use the MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') command message along with the MCI_STATUS_PARMS!AL(`_win32_MCI_STATUS',3,`No_SDK')_PARMS structure to get information about the status of an open device and its associated device element.
Before using the MCI_STATUS command message, you must identify the status item to query for by putting a constant in the dwItem member of the structure. Select a topic below to view the constants and their corresponding status items for the different audio device types.
All Audio Devices_win32_All_Audio_Devices Waveform Devices_win32_Waveform_DevicesCompact Disc Devices_win32_Compact_Disc_DevicesMIDI Sequencer Devices_win32_MIDI_Sequencer_Devices
+$#A All Audio Devices
Constant Status item
MCI_STATUS_LENGTH Length of the medium
MCI_STATUS_POSITION Current position
MCI_STATUS_MODE Current mode
MCI_STATUS_TIME_FORMAT Time format
MCI_STATUS_READY Ready state

+$#A Waveform Devices
Constant Status item
MCI_WAVE_STATUS_BLOCKALIGN Block alignment
MCI_WAVE_STATUS_FORMATTAG Format tag
MCI_WAVE_STATUS_CHANNELS Number of channels
MCI_WAVE_STATUS_SAMPLESPERSEC Sample rate
MCI_WAVE_STATUS_AVGBYTESPERSEC Average bytes per second
MCI_WAVE_STATUS_BITSPERSAMPLE Bits per sample (in PCM format files)
MCI_WAVE_STATUS_LEVEL Record level

+$#A Compact Disc Devices
Constant Status item
MCI_STATUS_NUMBER_OF_TRACKS Number of tracks
MCI_STATUS_MEDIA_PRESENT Media present
MCI_STATUS_CURRENT_TRACK Current track

+$#A MIDI Sequencer Devices
Constant Status item
MCI_SEQ_STATUS_TEMPO Tempo
MCI_SEQ_STATUS_PORT Port
MCI_SEQ_STATUS_OFFSET SMPTE offset
MCI_SEQ_STATUS_DIVTYPE Division type of file

+$#A Getting Track-Relative Information for Compact Disc Devices
For compact disc devices, you can get the starting position and length of a track by specifying the MCI_TRACK flag and setting the dwTrack member of the MCI_STATUS_PARMS!AL(`_win32_MCI_STATUS',3,`No_SDK')_PARMS structure to the desired track number. To get the starting position of a track, set the dwItem member to MCI_STATUS_POSITION. To get the length of a track, set dwItem to MCI_STATUS_LENGTH. The following function gets the number of tracks on the disc and the starting position of each track. It then uses the MessageBox_win32_MessageBox function to report the starting positions of the tracks.
/*
* Uses the MCI_STATUS command to get and
* display the starting times for the tracks
* on a compact disc. Returns 0L on success;
* otherwise, it returns an MCI error code.
*/
DWORD getCDTrackStartTimes(void)
{
UINT wDeviceID;
int i, iNumTracks;
DWORD dwReturn;
DWORD *pMem;
char szTempString[64];
char szTimeString[512] = "\0"; /* enough for 20 tracks */
MCI_OPEN_PARMS mciOpenParms;
MCI_SET_PARMS mciSetParms;
MCI_STATUS_PARMS mciStatusParms;

/* Open the compact disc device by specifying the device name.
*/
mciOpenParms.lpstrDeviceType = "cdaudio";
if (dwReturn = mciSendCommand(NULL, MCI_OPEN,
MCI_OPEN_TYPE,
(DWORD)(LPVOID) &mciOpenParms)) {
/*
* Failed to open device.
* Don't have to close it; just return error.
*/
return dwReturn;
}

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

/* Set the time format to minute/second/frame format. */
mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;
if (dwReturn = mciSendCommand(wDeviceID, MCI_SET,
MCI_SET_TIME_FORMAT,
(DWORD)(LPVOID) &mciSetParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

/* Get the number of tracks; limit to number
* we can display (20).
*/
mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,
MCI_STATUS_ITEM,
(DWORD)(LPVOID) &mciStatusParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}
iNumTracks = mciStatusParms.dwReturn;
iNumTracks = min(iNumTracks, 20);

/* Allocate memory to hold starting positions. */
pMem = (DWORD *)LocalAlloc(LPTR, iNumTracks * sizeof(DWORD));
if (pMem == NULL) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return 0xFFFFFFFF;
}

/*
* For each track, get and save the starting position and
* build a string containing starting positions.
*/
for(i = 1; i <= iNumTracks; i++) {
mciStatusParms.dwItem = MCI_STATUS_POSITION;
mciStatusParms.dwTrack = i;
if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,
MCI_STATUS_ITEM | MCI_TRACK,
(DWORD)(LPVOID) &mciStatusParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

pMem[i-1] = mciStatusParms.dwReturn;

wsprintf(szTempString, "Track %2d - %02d:%02d:%02d\n",
i, MCI_MSF_MINUTE(pMem[i-1]),
MCI_MSF_SECOND(pMem[i-1]),
MCI_MSF_FRAME(pMem[i-1]));

lstrcat(szTimeString, szTempString);
}

/* Use MessageBox to display starting times. */
MessageBox(hMainWnd, szTimeString, "Track Starting Position",
MB_ICONINFORMATION);

/* Free memory and close the device. */
LocalFree((HANDLE) pMem);
if (dwReturn = mciSendCommand(wDeviceID,
MCI_CLOSE, 0, NULL)) {
return dwReturn;
}

return 0;
}

+$#A Recording With Waveform Audio Devices
MCI supports recording with waveform audio devices. You can insert or overwrite recorded information in an existing file or record into a new file. To open an existing file for recording, open a waveform device and device element as you would open them for playback. To record into a new file, open the device but specify a zero-length filename for the device element.
When MCI creates a new file for recording, the waveform data format is set to a default format specified by the device driver. To use a format other than the default format, you can use the MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') command message to change the format.
To begin recording, use the MCI_RECORD!AL(`_win32_MCI_RECORD',3,`No_SDK') command message along with the MCI_RECORD_PARMS!AL(`_win32_MCI_RECORD',3,`No_SDK')_PARMS structure.
If you record to an existing file, you can use the MCI_TO and MCI_FROM flags to specify beginning and ending points for recording. For example, if you record to an existing file 20 seconds long, and you begin recording at 5 seconds and end recording at 10 seconds, you will have a recording 25 seconds long. It will contain 5 seconds of the original recording followed by the 5 seconds of new recording, and will end with the remaining 15 seconds of the original recording.
If you don't specify an ending position, recording continues until you send an MCI_STOP!AL(`_win32_MCI_STOP',3,`No_SDK') command message, or until the driver runs out of free disk space. If you record to a new file, you can omit the MCI_FROM flag or set it to 0 to start recording at the beginning of a new file. You can specify an ending position to terminate recording when recording to a new file.
If you record in overwrite mode to an existing file, you can use the MCI_TO and MCI_FROM flags to specify beginning and ending points of the waveform section that is to be overwritten. For example, if you record to an existing file 20 seconds long, and you begin recording at 5 seconds and end recording at 10 seconds, you still have a recording 20 seconds long. However, the section beginning at 5 seconds and ending at 10 seconds has been replaced.
+$#A Saving a Recorded File
When recording is complete, use the MCI_SAVE!AL(`_win32_MCI_SAVE',3,`No_SDK') command message along with the MCI_SAVE_PARMS!AL(`_win32_MCI_SAVE',3,`No_SDK')_PARMS structure to save the recording before closing the device.
If you close the device without saving, the recorded data is lost.
+$#A Checking Input Levels on PCM Devices
To get the level of the input signal before recording on a pulse code modulated (PCM) waveform input device, use the MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') command message. Specify the MCI_STATUS_ITEM flag and set the dwItem member in the MCI_STATUS_PARMS!AL(`_win32_MCI_STATUS',3,`No_SDK')_PARMS structure to MCI_WAVE_STATUS_LEVEL. The average input signal level is returned in the dwReturn member of the MCI_STATUS_PARMS structure. The left-channel value is in the high-order word, and the right- or mono-channel value is in the low-order word.
The input level is represented as an unsigned value. For 8-bit samples, this value is in the range 0 through 127 (0x7F). For 16-bit samples, it is in the range 0 through 32,767 (0x7FFF).
+$#A Example of Recording With a Waveform Audio Device
The following example function opens a waveform audio device with a new file, records for the specified time, plays the recording, and prompts the user to specify whether the recording should be saved as a file.
/*
* Uses the MCI_OPEN, MCI_RECORD, MCI_SAVE commands to record and
* save a waveform audio file. Returns 0L on success; otherwise,
* returns an MCI error code.
*/
DWORD recordWAVEFile(DWORD dwMilliSeconds)
{
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;
MCI_RECORD_PARMS mciRecordParms;
MCI_SAVE_PARMS mciSaveParms;
MCI_PLAY_PARMS mciPlayParms;

/*
* Open a waveform device with a new file for recording.
*/
mciOpenParms.lpstrDeviceType = "waveaudio";
mciOpenParms.lpstrElementName = "";
if (dwReturn = mciSendCommand(0, MCI_OPEN,
MCI_OPEN_ELEMENT | MCI_OPEN_TYPE,
(DWORD)(LPVOID) &mciOpenParms)) {
/*
* Failed to open device. Don't close it; just return
* error.
*/
return dwReturn;
}

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

/*
* Begin recording and record for the specified number
* of milliseconds. Wait for recording to complete before
* continuing. Assume the default time format for the
* waveform device (milliseconds).
*/
mciRecordParms.dwTo = dwMilliSeconds;
if (dwReturn = mciSendCommand(wDeviceID, MCI_RECORD,
MCI_TO | MCI_WAIT, (DWORD)(LPVOID) &mciRecordParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

/* Play the recording and query user to save the file. */
mciPlayParms.dwFrom = 0;
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,
MCI_FROM | MCI_WAIT,
(DWORD)(LPVOID) &mciPlayParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}
if (MessageBox(hMainWnd, "Do you want to save this recording?",
"", MB_YESNO) == IDNO) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return 0;
}

/*
* Save the recording to a file named TEMPFILE.WAV. Wait for
* the operation to finish before continuing.
*/
mciSaveParms.lpfilename = "tempfile.wav";
if (dwReturn = mciSendCommand(wDeviceID, MCI_SAVE,
MCI_SAVE_FILE | MCI_WAIT,
(DWORD)(LPVOID) &mciSaveParms)) {
mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
return dwReturn;
}

return 0;
}

+$#A Using the MCI MIDI Sequencer with High-Level Audio
The MCI MIDI sequencer plays standard MIDI files and Resource Interchange File Format (RIFF) MIDI files (RMID files). Standard MIDI files conform to the "Standard MIDI Files 1.0" specification. For information about this specification, see Introduction to Audio_win32_Introduction_to_Audio. Because RMID files are standard MIDI files with a RIFF header, information about standard MIDI files also applies to RMID files.
There are currently three variations of standard MIDI files. The MCI sequencer plays only two of these: format 0 and format 1 MIDI files.

Note When the MCI MIDI sequencer encounters a MIDI file not marked as being authored for Windows, it displays a dialog box informing the user. MIDI files authored for Windows should be marked with the MARKMIDI utility program. For additional information about MARKMIDI, see Using the MARKMIDI Utility_win32_Using_the_MARKMIDI_Utility.

· Querying for sequence division types_win32_Querying_for_Sequence_Division_Types
· Querying and Setting the MIDI Tempo_win32_Querying_and_Setting_the_MIDI_Tempo
· Changing sequencer synchronization_win32_Changing_Sequencer_Synchronization

+$#A Querying for Sequence Division Types
The division type of a MIDI sequence refers to the technique used to represent the time between MIDI events in the sequence. Use the MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') command message and set the dwItem member of the MCI_STATUS_PARMS!AL(`_win32_MCI_STATUS',3,`No_SDK')_PARMS structure to MCI_SEQ_STATUS_DIVTYPE to determine the division type of a sequence.
If the MCI_STATUS command message succeeds, the dwReturn member of the MCI_STATUS_PARMS structure contains one of the following values to indicate the division type.
Value Division type
MCI_SEQ_DIV_PPQN PPQN (parts per quarter-note)
MCI_SEQ_DIV_SMPTE_24 SMPTE, 24 frames per second (fps)
MCI_SEQ_DIV_SMPTE_25 SMPTE, 25 fps
MCI_SEQ_DIV_SMPTE_30 SMPTE, 30 fps
MCI_SEQ_DIV_SMPTE_30DROP SMPTE, 30 fps drop frame

You must know the division type of a sequence to change or query its tempo. You can't change the division type of a sequence by using the MCI sequencer.
+$#A Querying and Setting the MIDI Tempo
Use the MCI_STATUS command message and set the dwItem member of the MCI_STATUS_PARMS structure to MCI_SEQ_STATUS_TEMPO to get the tempo of a sequence. If the MCI_STATUS command message succeeds, the dwReturn member of the MCI_STATUS_PARMS structure contains the current tempo.
To change tempo, use the MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') command message with the MCI_SEQ_SET_PARMS!AL(`_win32_MCI_SEQ_SET_PARMS',3,`No_SDK') structure. Specify the MCI_SEQ_SET_TEMPO flag and set the value of the dwTempo member of the structure to the desired tempo.
The way tempo is represented depends on the division type of the sequence. If the division type is PPQN, the tempo is represented in beats per minute. If the division type is one of the SMPTE division types, the tempo is represented in frames per second. For information about determining the division type of a sequence, Querying for Sequence Division Types_win32_Querying_for_Sequence_Division_Types.
+$#A Changing Sequencer Synchronization
To change the synchronization mode of a sequencer device, use the MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') command message with the MCI_SEQ_SET_MASTER and MCI_SEQ_SET_SLAVE flags. Two members in the MCI_SEQ_SET_PARMS!AL(`_win32_MCI_SEQ_SET_PARMS',3,`No_SDK') structure, dwMaster and dwSlave, are used to specify the master and slave synchronization modes.
The master synchronization mode controls synchronization information sent by the sequencer to an output port. The slave synchronization mode controls where the sequencer gets its timing information to play a MIDI file. Following are the constants for the dwMaster member and their corresponding master synchronization modes.
Constant Synchronization mode
MCI_SEQ_MIDI MIDI Synchronization. Send timing information to output port using MIDI timing clock messages.
MCI_SEQ_SMPTE SMPTE Synchronization. Send timing information to output port using MIDI quarter-frame messages.
MCI_SEQ_NONE No Synchronization. Send no timing information.

Following are the constants for the dwSlave member and their corresponding slave synchronization modes.
Constant Synchronization mode
MCI_SEQ_FILE File Synchronization. Get timing information from MIDI file.
MCI_SEQ_MIDI MIDI Synchronization. Get timing information from input port using MIDI timing clock messages.
MCI_SEQ_SMPTE SMPTE Synchronization. Get timing information from input port using MIDI quarter-frame messages.
MCI_SEQ_NONE No Synchronization. Get timing information from MCI commands only and ignore timing information (such as tempo changes) that are in the MIDI file.

Note Currently, for master synchronization, the MCI MIDI sequencer supports only the No Synchronization mode (MCI_SEQ_NONE). For slave synchronization, it supports only the File Synchronization mode (MCI_SEQ_FILE) and the No Synchronization mode (MCI_SEQ_NONE).

+$#A The MIDI Mapper
The MIDI Mapper's standard patch services provide device-independent MIDI file playback for applications. You don't need to understand exactly how the MIDI Mapper works to use these services. The MIDI Mapper can be used with the MCI MIDI sequencer or with low-level MIDI output services.
To learn more about the MIDI Mapper or about authoring standard MIDI files for Windows, press the browse button (>>) to view more topics about how the MIDI Mapper works. For specific information about how to use the MIDI Mapper, see Using the MCI MIDI Sequencer!AL(`_win32_Using_the_MCI_MIDI_Sequencer',3,`No_SDK') or Low-Level Audio_win32_Low_Level_Audio.
+$#A MIDI Notational Conventions
Unless stated otherwise, all references to MIDI channel numbers use the logical channel numbers 1 through 16. These logical channel numbers correspond to the physical channel numbers 0 through 15 that are actually part of the MIDI message. All references to MIDI program-change and key values use the physical values 0 through 127. All numbers are decimal unless preceded by 0x prefix, in which case they are hexadecimal.
In the discussion of the MIDI Mapper, the term source refers to the input side of the MIDI Mapper. The term destination refers to the output side of the MIDI Mapper. For example, a source channel is the MIDI channel of a message sent to the MIDI Mapper, and a destination channel is the MIDI channel of a message sent from the MIDI Mapper to an output device.
+$#A The MIDI Mapper and Windows
The MIDI Mapper is part of the system software. The following illustration shows how the MIDI Mapper relates to other elements of the audio services.
From the viewpoint of an application, the MIDI Mapper looks like another MIDI output device. The MIDI Mapper receives messages sent to it by the low-level MIDI output functions midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') and midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK'). The MIDI Mapper modifies these messages and redirects them to a MIDI output device according to the current MIDI setup map. The current MIDI setup map is selected by the user by means of the MIDI Control Panel option. Only the user can select the current setup map; applications cannot change the current setup map.
+$#A The MIDI Mapper Architecture
The MIDI Mapper uses a MIDI setup map to determine how to translate and redirect messages it receives. A MIDI setup map consists of the following types of maps.
· Channel map_win32_The_Channel_Map
· Patch map_win32_Patch_Maps
· Key map_win32_Key_Maps

The following illustration shows the roles of channel, patch, and key maps in a MIDI setup map.
+$#A The Channel Map
The channel map affects all MIDI channel messages. MIDI channel messages include note-on, note-off, polyphonic-key-aftertouch, control-change, program- change, channel-aftertouch, and pitch-bend-change messages. The MIDI Mapper uses a single channel map with an entry for each of the 16 MIDI channels. Each channel-map entry specifies the following:
· A destination channel for the MIDI message
· A destination output device for the MIDI message
· An optional patch map specifying other possible modifications for the MIDI message

The destination channel is set to one of the 16 MIDI channels. MIDI messages are modified to reflect each new channel assignment. For example, if the destination channel entry for MIDI channel 4 is set to 6, all MIDI messages sent to channel 4 will be mapped to channel 6, as shown in the following illustration.
In this example, the MIDI status byte 0x93 is mapped to 0x95. The low-order of a MIDI status byte specify the channel number. Source channels are set to either active or inactive. Messages sent to inactive source channels are ignored, so an inactive channel is in effect muted or turned off.
The destination output device is set to one of the available MIDI output devices. A MIDI output device can be an internal synthesizer or a physical MIDI output port.
MIDI system messages are MIDI messages (with status bytes) from 0xF0 to 0xFF. There is no channel associated with MIDI system messages, so they can't be mapped. MIDI system messages are sent to all MIDI output devices listed in a channel map.
+$#A Patch Maps
Each channel map entry can have an associated patch map. Patch maps affect MIDI program-change and volume-controller messages. Program-change messages tell a synthesizer to change the instrument sound (patch) for a specified channel. Volume-controller messages set the volume for a channel.
A patch map has a translation table with an entry for each of the 128 program-change values. Each patch map specifies the following:
· A destination program-change value
· A volume scalar_win32_The_Volume_Scalar
· An optional key map_win32_Key_Maps

When program-change messages are received by the MIDI Mapper, the destination program-change value is substituted for the program-change value in the message. For example, if the destination program-change value for program-change 16 is 18, the MIDI Mapper modifies the MIDI program-change message as shown in the following illustration.
+$#A The Volume Scalar
The purpose of the volume scalar is to allow adjustments between the relative output levels of different patches on a synthesizer. For example, if the bass patch on a synthesizer is too loud compared with its piano patch, you can change the setup map to scale the bass volume down or the piano volume up.
The volume scalar specifies a percentage value for changing all MIDI main-volume controller messages that follow an associated program-change message. For example, if the volume scalar value is 50%, the MIDI Mapper modifies MIDI main-volume controller messages as shown in the following illustration:
+$#A Key Maps
Each entry in the patch-map translation table can have an associated key map. Key maps affect note-on, note-off, and polyphonic-key-aftertouch messages. A key map has a translation table with an entry for each of the 128 MIDI key values. For example, if the entry for key value 60 is 72, the MIDI Mapper modifies MIDI note-on messages as shown in the following illustration.
Key maps are useful with synthesizers that have key-based percussion instruments with a particular percussion sound assigned to each key. Key maps are usually assigned to the first patch in the patch maps on the percussion channels (10 and 16).
+$#A Summary of Maps and MIDI Messages
Following are the status bytes for MIDI messages and the map types that affect each message.
MIDI status Description Map types
0x80-0x8F Note-off Channel maps, key maps
0x90-0x9F Note-on Channel maps, key maps
0xA0-0xAF Polyphonic-key-aftertouch Channel maps, key maps
0xB0-0xBF Control-change Channel maps, patch maps
0xC0-0xCF Program-change Channel maps, patch maps
0xD0-0xDF Channel-aftertouch Channel maps
0xE0-0xEF Pitch-bend-change Channel maps
0xF0-0xFF System Not mapped

· The high-order four bits represent the status value. The low-order four bits represent the channel information.
· Patch maps affect only controller 7 (main volume).
· System messages are sent to all devices listed in a channel map.

+$#A Authoring MIDI Files
The "MIDI 1.0 Detailed Specification" does not define any standard patch assignments for synthesizers. Therefore, when you create a MIDI file, it won't be reproduced correctly unless it is played back on the same MIDI synthesizer setup used to create it. For example, if you create a piano concerto for your Yamaha® DX7 and try to play it back on a Roland® LAPC-1, it may be played with a flute instead of a piano.
To enable MIDI files to be a viable format for representing music in multimedia computing, Windows provides MIDI authoring guidelines. These guidelines include a list of standard patch assignments and standard key assignments for percussion instruments. Using the MIDI Mapper, MIDI files authored in accordance with these guidelines can be played on any multimedia computer with internal or external MIDI synthesizers.
+$#A About Base-Level and Extended Synthesizers
Although it is difficult to clearly quantify distinctions between synthesizers, it is important to have some guidelines so you can create MIDI files that will play on all multimedia computers. The terms used to distinguish synthesizers for the purpose of authoring MIDI files are base-level synthesizer and extended synthesizer.
The distinctions between base-level and extended synthesizers are made solely on the number of instruments and notes the synthesizer can play, not on the quality or the cost of the synthesizer. Following are the minimum capabilities of base-level and extended synthesizers.
Synthesizer Melodic instruments Percussive instruments
Number Polyphony Number Polyphony
Base-level 3 instruments 6 notes 3 instruments 3 notes
Extended 9 instruments 16 notes 8 instruments 16 notes

Polyphony is the number of notes the synthesizer can play simultaneously. The polyphony expressed above applies to each group of instruments ¾ melodic and percussive. For example, a base-level synthesizer is capable of playing six notes distributed among three melodic instruments and three notes distributed among three percussive instruments. Each melodic instrument is on a different MIDI channel; the percussive instruments are key-based ¾ all on a single MIDI channel.
All multimedia computers provide at least a base-level synthesizer. Users can enhance their computer by adding internal or external synthesizers, which can be either base-level or extended synthesizers. When adding a synthesizer, the user must configure the MIDI Mapper to use the new device. If the MIDI Mapper is not configured, the instrument sounds will be incorrect when MIDI files are played, unless the application has the ability to send output directly to an internal synthesizer or output port. The MIDI Control Panel option allows a user to configure the MIDI Mapper as needed.
+$#A Authoring Guidelines for MIDI Files
Follow these guidelines to author device-independent MIDI files for Windows:
· Author for both base-level and extended synthesizer setups.
· Use MIDI channels 13 through 16 for base-level synthesizer data (reserve channel 16 for key-based percussion instruments).
· Use MIDI channels 1 through 10 for extended synthesizer data (reserve channel 10 for key-based percussion instruments).
· Prioritize MIDI data by putting crucial data in the lower-numbered channels.
· Limit the polyphony of nonpercussive channels to a total of 6 notes for base-level data and 16 notes for extended data.
· Limit the polyphony of percussive channels to a total of 3 notes for base-level data and 16 notes for extended data.
· Use the standard MIDI patch assignments and key assignments.
· Always send a program-change message to a channel to select a patch before sending other messages to that channel. For the two percussion channels (10 and 16), select program number 0.
· Always follow a MIDI program-change message with a MIDI main-volume controller message (controller number 7) to set the relative volume of the patch.
· Use a value of 80 (0x50) for the main-volume controller for normal listening levels. For quieter or louder levels, you can use lower or higher values.
· Use only the following MIDI messages: note-on with velocity, note-off, program change, pitch bend, main volume (controller 7), and damper pedal (controller 64). Internal synthesizers are required to respond to these messages and most MIDI musical instruments respond to them as well.
· Use the MARKMIDI utility program to mark MIDI files authored for Windows.

The following illustration summarizes the use of the 16 MIDI channels in a standard MIDI file authored for Windows.
+$#A Prioritizing MIDI Data
Synthesizers don't always fall cleanly into the base-level and extended designations defined earlier. It's up to the user to determine how to use synthesizers that are capable of more than the base-level requirements but do not fully meet the extended requirements. For this reason, it's important to prioritize the melodic data by putting the most critical data in lower-numbered channels. For example, a user may have a synthesizer capable of playing six melodic instruments with 12-note polyphony. The user can use this device as an extended synthesizer by setting up the MIDI Mapper to play only the first six melodic channels and ignore any information on channels 7, 8, and 9.
+$#A Standard MIDI Patch Assignments
The standard MIDI patch assignments for authoring MIDI files for use with Windows are based on the MIDI Manufacturers Association (MMA) General MIDI Mode specification. Following are the standard MIDI patch assignments.
Piano Chromatic Percussion Organ
0 Acoustic grand piano 8 Celesta 16 Hammond organ
1 Bright acoustic piano 9 Glockenspiel 17 Percussive organ
2 Electric grand piano 10 Music box 18 Rock organ
3 Honky-tonk piano 11 Vibraphone 19 Church organ
4 Rhodes piano 12 Marimba 20 Reed organ
5 Chorused piano 13 Xylophone 21 Accordion
6 Harpsichord 14 Tubular bells 22 Harmonica
7 Clavinet 15 Dulcimer 23 Tango accordion

Guitar Bass Strings
24 Acoustic guitar (nylon) 32 Acoustic bass 40 Violin
25 Acoustic guitar (steel) 33 Electric bass (finger) 41 Viola
26 Electric guitar (jazz) 34 Electric bass (pick) 42 Cello
27 Electric guitar (clean) 35 Fretless bass 43 Contrabass
28 Electric guitar (muted) 36 Slap bass 1 44 Tremolo strings
29 Overdriven guitar 37 Slap bass 2 45 Pizzicato strings
30 Distortion guitar 38 Synth bass 1 46 Orchestral harp
31 Guitar harmonics 39 Synth bass 2 47 Timpani

Ensemble Brass Reed
48 String ensemble 1 56 Trumpet 64 Soprano sax
49 String ensemble 2 57 Trombone 65 Alto sax
50 Synth. strings 1 58 Tuba 66 Tenor sax
51 Synth. strings 2 59 Muted trumpet 67 Baritone sax
52 Choir Aahs 60 French horn 68 Oboe
53 Voice Oohs 61 Brass section 69 English horn
54 Synth voice 62 Synth. brass 1 70 Bassoon
55 Orchestra hit 63 Synth. brass 2 71 Clarinet

Pipe Synth Lead Synth Pad
72 Piccolo 80 Lead 1 (square) 88 Pad 1 (new age)
73 Flute 81 Lead 2 (sawtooth) 89 Pad 2 (warm)
74 Recorder 82 Lead 3 (calliope lead) 90 Pad 3 (polysynth)
75 Pan flute 83 Lead 4 (chiff lead) 91 Pad 4 (choir)
76 Bottle blow 84 Lead 5 (charang) 92 Pad 5 (bowed)
77 Shakuhachi 85 Lead 6 (voice) 93 Pad 6 (metallic)
78 Whistle 86 Lead 7 (fifths) 94 Pad 7 (halo)
79 Ocarina 87 Lead 8 (brass + lead) 95 Pad 8 (sweep)

Sound Effects
120 Guitar fret noise
121 Breath noise
122 Seashore
123 Bird tweet
124 Telephone ring
125 Helicopter
126 Applause
127 Gunshot

+$#A Standard MIDI Key Assignments
The standard MIDI key assignments for percussion instruments are based on the MMA General MIDI Mode specification. The following illustration shows the standard key assignments for MIDI files authored for Windows.
+$#A Using the MARKMIDI Utility
The MARKMIDI utility program marks MIDI files as following the authoring guidelines for Windows MIDI files. The program does not verify that the file is authored correctly; it only marks the file by adding a sequencer-specific meta-event to the first track chunk. MARKMIDI has the following command syntax.
MARKMIDI src-filename dest-filename
If the MCI MIDI sequencer is used to play a MIDI file not marked by MARKMIDI, it displays a dialog box informing the user that the file is not authored for Windows.

Note Do not use MARKMIDI to mark MIDI files unless the files were authored according to the guidelines presented in this guide. If your application uses MIDI files not authored to these guidelines, the application must instruct users to install or create the proper maps by using the MIDI Mapper Control Panel application. For example, if your application uses MIDI files following the General MIDI channel recommendations, your users should create or install a General MIDI map for their synthesizers.

+$#A High-Level Audio Functions
Following are the functions used with high-level audio services.
mciGetCreatorTask!AL(`_win32_mciGetCreatorTask',3,`No_SDK')mciGetDeviceID!AL(`_win32_mciGetDeviceID',3,`No_SDK')mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK')mciGetYieldProc!AL(`_win32_mciGetYieldProc',3,`No_SDK')mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK')mciSendString!AL(`_win32_mciSendString',3,`No_SDK')mciSetYieldProc!AL(`_win32_mciSetYieldProc',3,`No_SDK')PlaySound!AL(`_win32_PlaySound',3,`No_SDK')sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK')
+$#A Low-Level Audio
This overview explains how to use the low-level audio services of Microsoft® Windows® to manage playback and recording of waveform and Musical Instrument Digital Interface (MIDI) audio.
For an overview of Windows audio services, see Introduction to Audio_win32_Introduction_to_Audio. For a discussion of high-level audio services, see High-Level Audio_win32_High_Level_Audio.
This overview covers the following topics:
· Using low-level audio services_win32_Using_Low_Level_Audio_Services
· Playing waveform audio_win32_Playing_Waveform_Audio
· Recording waveform audio!AL(`_win32_Recording_Waveform_Audio',3,`No_SDK')
· Playing MIDI audio_win32_Playing_MIDI_Audio
· Recording MIDI audio!AL(`_win32_Recording_MIDI_Audio',3,`No_SDK')
· Using auxiliary audio devices_win32_Auxiliary_Audio_Devices
· Using audio clipboard formats_win32_Audio_Clipboard_Formats

You should have a basic knowledge of digital audio and MIDI before reading this overview. If you need additional information on these subjects, see Introduction to Audio_win32_Introduction_to_Audio.
+$#A Function Prefixes
Low-level audio function names begin with the following prefixes.
Prefix Description
aux Auxiliary audio functions
midi MIDI audio functions
wave Waveform audio functions

+$#A Using Low-Level Audio Services
Low-level audio services control different types of audio devices, including waveform, MIDI, and auxiliary audio devices. Many of the concepts for using low-level services apply to more than one type of device. The following topics present general information on using low-level audio services:
· Querying audio devices_win32_Querying_Audio_Devices
· Opening and closing device drivers!AL(`_win32_Opening_and_closing_device_drivers',3,`No_SDK')
· Allocating and preparing audio data blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks
· Managing audio data blocks_win32_Managing_Audio_Data_Blocks
· Using the MMTIME structure_win32_Using_the_MMTIME_Structure
· Handling errors with audio functions!AL(`_win32_Handling_errors_with_audio_functions',3,`No_SDK')

Subsequent topics in this overview discuss the use of specific types of audio devices.
+$#A Querying Audio Devices
Before playing or recording audio, you must determine the capabilities of the audio hardware present in the system. Audio capability can vary from one multimedia computer to the next; applications should not make assumptions about the audio hardware present in a given system.
+$#A Getting the Number of Devices
The Win32 application programming interface (API) provides the following functions to determine how many devices of a certain type are available in a given system.
Function Description
auxGetNumDevs!AL(`_win32_auxGetNumDevs',3,`No_SDK') Retrieves the number of auxiliary audio devices present in the system.
midiInGetNumDevs!AL(`_win32_midiInGetNumDevs',3,`No_SDK') Retrieves the number of MIDI input devices present in the system.
midiOutGetNumDevs!AL(`_win32_midiOutGetNumDevs',3,`No_SDK') Retrieves the number of MIDI output devices present in the system.
waveInGetNumDevs!AL(`_win32_waveInGetNumDevs',3,`No_SDK') Retrieves the number of waveform input devices present in the system.
waveOutGetNumDevs!AL(`_win32_waveOutGetNumDevs',3,`No_SDK') Retrieves the number of waveform output devices present in the system.

Audio devices are identified by a device identifier. The device identifier is determined implicitly from the number of devices present in a given system. Device identifiers range from zero to one less than the number of devices present. For example, if there are two MIDI output devices in a system, valid device identifiers are 0 and 1.
+$#A Getting the Capabilities of a Device
After you determine how many devices of a certain type are present in a system, you can inquire about the capabilities of each device. The Win32 API provides the following functions to determine the capabilities of audio devices.
Function Description
auxGetDevCaps!AL(`_win32_auxGetDevCaps',3,`No_SDK') Retrieves the capabilities of a given auxiliary audio device.
midiInGetDevCaps!AL(`_win32_midiInGetDevCaps',3,`No_SDK') Retrieves the capabilities of a given MIDI input device.
midiOutGetDevCaps!AL(`_win32_midiOutGetDevCaps',3,`No_SDK') Retrieves the capabilities of a given MIDI output device.
waveInGetDevCaps!AL(`_win32_waveInGetDevCaps',3,`No_SDK') Retrieves the capabilities of a given waveform input device.
waveOutGetDevCaps!AL(`_win32_waveOutGetDevCaps',3,`No_SDK') Retrieves the capabilities of a given waveform output device.

Each of these functions takes a pointer to a structure that it fills with information on the capabilities of a specified device. Following are the structures that correspond to each of the device-inquiry functions.
Function Structure
auxGetDevCaps!AL(`_win32_auxGetDevCaps',3,`No_SDK') AUXCAPS!AL(`_win32_AUXCAPS',3,`No_SDK')
midiInGetDevCaps!AL(`_win32_midiInGetDevCaps',3,`No_SDK') MIDIINCAPS!AL(`_win32_MIDIINCAPS',3,`No_SDK')
midiOutGetDevCaps!AL(`_win32_midiOutGetDevCaps',3,`No_SDK') MIDIOUTCAPS!AL(`_win32_MIDIOUTCAPS',3,`No_SDK')
waveInGetDevCaps!AL(`_win32_waveInGetDevCaps',3,`No_SDK') WAVEINCAPS!AL(`_win32_WAVEINCAPS',3,`No_SDK')
waveOutGetDevCaps!AL(`_win32_waveOutGetDevCaps',3,`No_SDK') WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK')

+$#A Opening and Closing Low-Level Audio Device Drivers
After getting the capabilities of an audio device, you must open the device before you can use it. Audio devices aren't guaranteed to be shareable, so a particular device might not be available when you request it. If this happens, you should notify the user and allow the user to try to open the device again. When you open an audio device, be sure to close it as soon as you finish using it.
The Win32 API provides the following functions to open and close different types of audio devices.
Function Description
midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK') Opens a specified MIDI input device for recording.
midiInClose!AL(`_win32_midiInClose',3,`No_SDK') Closes a specified MIDI input device.
midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK') Opens a MIDI output device for playback.
midiOutClose!AL(`_win32_midiOutClose',3,`No_SDK') Closes a specified MIDI output device.
waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK') Opens a waveform input device for recording.
waveInClose!AL(`_win32_waveInClose',3,`No_SDK') Closes a specified waveform input device.
waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') Opens a waveform output device for playback.
waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK') Closes a specified waveform output device.

These functions are discussed in detail later in this section.
+$#A About Device Handles
Each function that opens an audio device takes as parameters a device identifier, a pointer to a memory location, and some parameters unique to each type of device. The memory location is filled with a device handle. Use this device handle of identify the open audio device when calling other audio functions.
The distinction between audio-device identifiers and audio-device handles is subtle, but very important. Don't confuse the two in your application. Following are the differences between device identifiers and device handles.
· Device identifiers are determined implicitly from the number of devices present in a system, which is obtained by using the device-numbering functions (auxGetNumDevs!AL(`_win32_auxGetNumDevs',3,`No_SDK'), midiInGetNumDevs!AL(`_win32_midiInGetNumDevs',3,`No_SDK'), and so on).
· Device handles are returned when device drivers are opened by using the device-opening functions (midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK'), midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK'), and so on)
· The only functions that take device identifiers as parameters are the device-capabilities functions (waveOutGetDevCaps!AL(`_win32_waveOutGetDevCaps',3,`No_SDK'), midiInGetDevCaps!AL(`_win32_midiInGetDevCaps',3,`No_SDK'), and so on), the device-opening functions, and volume functions (waveOutGetVolume!AL(`_win32_waveOutGetVolume',3,`No_SDK'), waveOutSetVolume!AL(`_win32_waveOutSetVolume',3,`No_SDK'), and so on). All other functions take device handles.

There are no functions for opening and closing auxiliary audio devices. Auxiliary audio devices don't need to be opened and closed like MIDI and waveform devices because there is no continuous data transfer associated with them. All auxiliary audio functions take device identifiers to identify devices.
+$#A Allocating and Preparing Audio Data Blocks
Some low-level audio functions require applications to allocate data blocks to pass to the device drivers for playback or recording purposes. Each of these functions uses a structure (or header) to describe its data block. The following table identifies these functions and their associated header structures. (The MMSYSTEM.H file defines the data structures for these headers.)
Function Header structure Purpose
waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK') WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') Waveform playback
waveInAddBuffer!AL(`_win32_waveInAddBuffer',3,`No_SDK') WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') Waveform recording
midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') MIDI system-exclusive playback
midiInAddBuffer!AL(`_win32_midiInAddBuffer',3,`No_SDK') MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') MIDI system-exclusive recording

Before you use one of the functions listed above to pass a data block to a device driver, you must allocate memory for the data block according to the guidelines discussed in the following topics.
+$#A Allocating Memory for Audio Data Blocks
Before preparing a data block, you must allocate memory for the data block and the header structure that describes the data block.
To allocate memory, use the GlobalAlloc_win32_GlobalAlloc function with the GMEM_MOVEABLE flag to get a handle of the memory object. Then pass this handle of the GlobalLock_win32_GlobalLock function to get a pointer to the memory object.
To free a data block, use GlobalUnlock_win32_GlobalUnlock and GlobalFree_win32_GlobalFree.
+$#A Preparing Audio Data Blocks
Before you pass an audio data block to a device driver, you must prepare the data block by passing it to a header-preparation function. When the device driver is finished with the data block and returns it, you must clean up this preparation by passing the data block to a header clean-up function before any allocated memory can be freed.
Windows provides the following functions for preparing and cleaning up audio data blocks.
Function Description
midiInPrepareHeader!AL(`_win32_midiInPrepareHeader',3,`No_SDK') Prepares a MIDI-input data block.
midiInUnprepareHeader!AL(`_win32_midiInUnprepareHeader',3,`No_SDK') Cleans up the preparation on a MIDI-input data block.
midiOutPrepareHeader!AL(`_win32_midiOutPrepareHeader',3,`No_SDK') Prepares a MIDI-output data block.
midiOutUnprepareHeader!AL(`_win32_midiOutUnprepareHeader',3,`No_SDK') Cleans up the preparation on a MIDI-output data block.
waveInPrepareHeader!AL(`_win32_waveInPrepareHeader',3,`No_SDK') Prepares a waveform-input data block.
waveInUnprepareHeader!AL(`_win32_waveInUnprepareHeader',3,`No_SDK') Cleans up the preparation on a waveform-input data block.
waveOutPrepareHeader!AL(`_win32_waveOutPrepareHeader',3,`No_SDK') Prepares a waveform-output data block.
waveOutUnprepareHeader!AL(`_win32_waveOutUnprepareHeader',3,`No_SDK') Cleans up the preparation on a waveform-output data block.

+$#A Managing Audio Data Blocks
Unless the audio data is small enough to be contained in a single data block, applications must continually supply the device driver with data blocks until playback or recording is complete. This is true for waveform input and output, and for MIDI system-exclusive input messages. Regular MIDI channel messages don't require data blocks for input or output.
Even if a single data block is used, applications must be able to determine when a device driver is finished with the data block so the application can free the memory associated with the data block and header structure. There are three ways to determine when a device driver is finished with a data block:
· Specify a window to receive a message sent by the driver when it is finished with a data block.
· Specify a callback function to receive a message sent by the driver when it is finished with a data block.
· Poll a bit in the dwFlags member of the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') or MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') data structure sent with each data block.

If an application doesn't get a data block to the device driver when needed, there can be an audible gap in playback or a loss of incoming recorded information. Use a double-buffering scheme to stay at least one data block ahead of the device driver.

Note To get time-stamped MIDI input data, you must use a callback function.

+$#A Using a Window to Process Driver Messages
The easiest type of callback function to use to process driver messages is a window callback. To use a window callback, specify the CALLBACK_WINDOW flag in the dwFlags parameter and a window handle as the dwCallback parameter of the device-opening function. Driver messages will be sent to the window-procedure function for the window identified by the handle in dwCallback.
Messages sent to the window function are specific to the audio device type used. For details on these messages, see the topics later in this section on using window messages for each specific audio device type.
+$# Using a Callback Function to Process Low-Level Audio Driver Messages
You can also write your own low-level callback function to process messages sent by the device driver. To use a low-level callback function, specify the CALLBACK_FUNCTION flag in the dwFlags parameter and the address of the callback in the dwCallback parameter of the device-opening function.
Messages sent to a callback function are similar to messages sent to a window, except that they have two doubleword parameters instead of one unsigned-integer and one doubleword parameter. For details on these messages, see the sections later in this section about using low-level callbacks for each specific audio device type.
Callback functions for the low-level audio services are often called from another thread running asynchronously from the application's thread. It is therefore important to use critical sections to protect data shared between the callback routine and the rest of the application. For more information about critical sections, see Synchronization_win32_Synchronization.
There are several differences between callback functions for the low-level audio services under Windows NT and under Windows 3.1:
· Under Windows 3.1, a low-level audio callback function is called at interrupt time. The call to a low-level audio callback function occurs on a separate thread under Windows NT.
· Under Windows 3.1, a low-level audio callback function has to be in a dynamic-link library. Under Windows NT, there is no such restriction.
· Under Windows 3.1, a low-level audio callback function must be exported in a .DEF file. Under Windows NT, that is not necessary.

If source code must be compatible with Windows 3.1, it should follow the Windows 3.1 rules as noted briefly above and specified more fully in the Windows 3.1 documentation.
One other item should be noted carefully. There is a limited set of APIs that a Windows 3.1 low-level audio callback function may call. In Win32, that strict limitation has been removed. However, it is strongly recommended that a Windows NT low-level audio callback function restrain its calls to a similar set of APIs. Here is the Windows NT recommended list:
EnterCriticalSection_win32_EnterCriticalSection ReleaseSemaphore_win32_ReleaseSemaphore
LeaveCriticalSection_win32_LeaveCriticalSection SetEvent_win32_SetEvent
midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') timeGetSystemTime!AL(`_win32_timeGetSystemTime',3,`No_SDK')
midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') timeGetTime!AL(`_win32_timeGetTime',3,`No_SDK')
OutputDebugString_win32_OutputDebugString timeKillEvent!AL(`_win32_timeKillEvent',3,`No_SDK')
PostMessage_win32_PostMessage timeSetEvent!AL(`_win32_timeSetEvent',3,`No_SDK')
PostThreadMessage_win32_PostThreadMessage

If an application's low-level audio callback function does stray from this list, it should be careful not to call APIs that take a long time to complete.
In particular, calling wave and midi APIs may result in a deadlock.
If a Win32 low-level audio callback shares data with other code, a Critical Section or similar mutual exclusion mechanism should be used to protect the integrity of the data.
Use one of the following techniques to pass instance data from an application to a low-level callback function residing in a dynamic link library:
· Pass the instance data using the dwInstance parameter of the function that opens the device driver.
· Pass the instance data using the dwUser member of the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') and MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structures that identify an audio data block being sent to a device driver.

If you need more than 32 bits of instance data, pass a pointer to a structure containing the additional information.
+$#A Managing Low-Level Audio Data Blocks by Polling
In addition to using a callback function, you can poll the dwFlags member of a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') or MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure to determine when an audio device is finished with a data block. There are times when it's better to poll dwFlags rather than wait for a window to receive messages from the drivers. For example, immediately after you call the waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK') function to release pending data blocks, you can poll to be sure that the data blocks are indeed done before proceeding to call the waveOutUnprepareHeader!AL(`_win32_waveOutUnprepareHeader',3,`No_SDK') function and free the memory for the data block.
MMSYSTEM.H defines two flags for testing the dwFlags member: WHDR_DONE for a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure, and MHDR_DONE for a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure.
+$#A Using the MMTIME Structure
Windows uses a structure called MMTIME!AL(`_win32_MMTIME',3,`No_SDK') to represent time. Low-level audio functions that use MMTIME include waveInGetPosition!AL(`_win32_waveInGetPosition',3,`No_SDK') and waveOutGetPosition!AL(`_win32_waveOutGetPosition',3,`No_SDK'). The timeGetSystemTime function also uses MMTIME to represent system time.
+$#A Setting the Time Format
MMTIME!AL(`_win32_MMTIME',3,`No_SDK') can represent time in one or more different formats including milliseconds, samples, Society of Motion Picture and Television Engineers (SMPTE), and MIDI song-pointer formats. The wType member specifies the format used to represent time. Before calling a function that uses the MMTIME structure, you must set the wType member to indicate your requested time format. Be sure to check wType after the call to see if the requested time format is supported. If the requested time format is not supported, the time is specified in an alternate time format selected by the device driver and the wType member is changed to indicate the selected time format. MMSYSTEM.H defines the following flags for the wType member of the MMTIME!AL(`_win32_MMTIME',3,`No_SDK') structure.
Flag Description
TIME_MS Milliseconds
TIME_SAMPLES Number of waveform audio samples
TIME_BYTES Number of waveform audio bytes
TIME_SMPTE SMPTE time
TIME_MIDI MIDI song-position pointer

For details on using MMTIME!AL(`_win32_MMTIME',3,`No_SDK') with the waveOutGetPosition!AL(`_win32_waveOutGetPosition',3,`No_SDK') function, seeGetting the Current Playback Position_win32_Getting_the_Current_Playback_Position.
+$#A Getting the System Time
Use the timeGetSystemTime!AL(`_win32_timeGetSystemTime',3,`No_SDK') or timeGetTime!AL(`_win32_timeGetTime',3,`No_SDK') function to get the system time. System time is defined as the time (in milliseconds) since Windows was started. For more information on timeGetSystemTime and timeGetTime, see Multimedia Timer and Joystick Services_win32_Multimedia_Timer_and_Joystick_Services.
+$#AK Handling Errors with Low-Level Audio Functions
Low-level audio functions return a nonzero error code. A set of functions convert these error codes into a textual description of the error. The application must still look at the error value itself to determine how to proceed, but textual descriptions of errors can be used in dialog boxes describing errors to users.
The following functions can be used to get textual descriptions of low-level audio errors.
Function Description
midiInGetErrorText!AL(`_win32_midiInGetErrorText',3,`No_SDK') Retrieves a textual description of a specified MIDI input error.
midiOutGetErrorText!AL(`_win32_midiOutGetErrorText',3,`No_SDK') Retrieves a textual description of a specified MIDI output error.
waveInGetErrorText!AL(`_win32_waveInGetErrorText',3,`No_SDK') Retrieves a textual description of a specified waveform input error.
waveOutGetErrorText!AL(`_win32_waveOutGetErrorText',3,`No_SDK') Retrieves a textual description of a specified waveform output error.

The only low-level audio functions that don't return error codes are the device-numbering functions (auxGetNumDevs!AL(`_win32_auxGetNumDevs',3,`No_SDK'), midiInGetNumDevs!AL(`_win32_midiInGetNumDevs',3,`No_SDK'), and so on). These functions return a value of zero if no devices are present in a system, or if any errors are encountered by the function.
+$#A Playing Waveform Audio
If your application plays waveform audio, you can use the Media Control Interface (MCI) to control waveform output devices. If the MCI waveform playback services don't meet the needs of your application, you can manage waveform playback by using the low-level waveform services.
+$#A Waveform Output Data Types
The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for the waveform output functions.
Type Description
HKL Handle to a keyboard layout.
HWAVEOUT Handle to an audio waveform output device.
WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK') Structure used to inquire about the capabilities of a particular waveform output device.
WAVEFORMAT!AL(`_win32_WAVEFORMAT',3,`No_SDK') Structure that specifies the data formats supported by a particular waveform output device. This structure is also used for waveform input devices.
WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') Structure that is a header for a block of waveform output data. This structure is also used for waveform input devices.

+$#A Querying Waveform Output Devices
Before playing a waveform, call the waveOutGetDevCaps!AL(`_win32_waveOutGetDevCaps',3,`No_SDK') function to determine the waveform output capabilities of the playback device as described earlier in this section. This function takes a pointer to a WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK') structure, which it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.
In addition, the WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK') structure provides information on the standard waveform formats and features supported by the device driver.
+$#A Determining Standard Format Support
The dwFormats member of the WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK') structure specifies the standard waveform formats supported by a device. The MMSYSTEM.H header file defines the following standard waveform format identifiers for the dwFormats member.
Format identifier Waveform format
WAVE_FORMAT_1M08 8-bit mono at 11.025 kHz
WAVE_FORMAT_1S08 8-bit stereo at 11.025 kHz
WAVE_FORMAT_1M16 16-bit mono at 11.025 kHz
WAVE_FORMAT_1S16 16-bit stereo at 11.025 kHz
WAVE_FORMAT_2M08 8-bit mono at 22.05 kHz
WAVE_FORMAT_2S08 8-bit stereo at 22.05 kHz
WAVE_FORMAT_2M16 16-bit mono at 22.05 kHz
WAVE_FORMAT_2S16 16-bit stereo at 22.05 kHz
WAVE_FORMAT_4M08 8-bit mono at 44.1 kHz
WAVE_FORMAT_4S08 8-bit stereo at 44.1 kHz
WAVE_FORMAT_4M16 16-bit mono at 44.1 kHz
WAVE_FORMAT_4S16 16-bit stereo at 44.1 kHz

The value of the dwFormats member is the result of a logical OR operation on the flags listed above.
This information on standard-format support also applies to the WAVEINCAPS!AL(`_win32_WAVEINCAPS',3,`No_SDK') structure used with waveform input devices. For information about the WAVEINCAPS structure, see Querying Waveform Input Devices_win32_Querying_Waveform_Input_Devices.
To determine whether a specific format is supported by a device, use the waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function with the WAVE_FORMAT_QUERY flag as shown in the next section.
+$#A Determining Low-Level Audio Nonstandard Format Support
Waveform devices can support nonstandard formats not listed in the preceding table. To see whether a particular format (standard or nonstandard) is supported by a device, you can call the waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function with the WAVE_FORMAT_QUERY flag. The WAVE_FORMAT_QUERY flag tells waveOutOpen to check whether the requested format is supported. The wave device is not actually opened. The requested format is specified by the structure pointed to by the lpFormat parameter passed to waveOutOpen. For information about setting up this structure, see Specifying Waveform Data Formats_win32_Specifying_Waveform_Data_Formats. The following example uses this technique to determine whether a given waveform device supports a given format.
/*
* Determines whether the given waveform output device supports
* a given wave-form format. Returns 0 if the format is supported,
* WAVEERR_BADFORMAT if the format is not supported, and one of
* the MMSYSERR_ error codes if there are other errors in opening
* the given waveform device.
*/
UINT IsFormatSupported(LPPCMWAVEFORMAT lpPCMWaveFormat,
UINT wDeviceID)
{
return (waveOutOpen(
NULL, // NULL for query
wDeviceID, // device identifier
(LPWAVEFORMAT)lpPCMWaveFormat, // requested format
NULL, // no callback
NULL, // no instance data
WAVE_FORMAT_QUERY)); // query only
}

This technique to determine nonstandard format support also applies to waveform input devices. The only difference is that the waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK') function is used in place of waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') to query for format support.

Note To determine whether a particular waveform-data format is supported by any of the waveform devices in a system, use the technique illustrated in the previous example, but specify the WAVE_MAPPER constant for the wDeviceID parameter. For more information about using the WAVE_MAPPER constant, see Selecting a Waveform Output Device_win32_Selecting_a_Waveform_Output_Device.

+$#A Determining Capabilities of Waveform Output Devices
Waveform output devices vary in the capabilities they support. The dwSupport member of the WAVEOUTCAPS!AL(`_win32_WAVEOUTCAPS',3,`No_SDK') structure indicates whether a given device supports capabilities such as volume and pitch changes. MMSYSTEM.H defines the following flags for the dwSupport member.
Flag Description
WAVECAPS_PITCH Pitch-change support
WAVECAPS_PLAYBACKRATE Playback-rate-change support
WAVECAPS_VOLUME Volume-control support
WAVECAPS_LRVOLUME Individual volume-control support for both left and right channels

The dwSupport member is a logical OR of the flags listed in the preceding table.
For more information about playback volume levels, see Changing Waveform Playback Volume_win32_Changing_Waveform_Playback_Volume. For more information about pitch and playback rates, see Changing Pitch and Playback Rate_win32_Changing_Pitch_and_Playback_Rate.
+$#A Opening Waveform Output Devices
Use the waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function to open a waveform output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.
+$#A Selecting a Waveform Output Device
Some multimedia computers have multiple waveform output devices. Unless you know you want to open a specific waveform output device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function chooses the device in the system best capable of playing the given data format.
+$#A Specifying Waveform Data Formats
When you call the waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function to open a device driver for playback or determine whether the driver supports a particular data format, use the lpFormat parameter to specify a pointer to a structure containing the requested waveform data format.
The WAVEFORMAT!AL(`_win32_WAVEFORMAT',3,`No_SDK') structure specifies format information common to all types of waveform data formats. Currently the only format supported is pulse code modulated (PCM), but in the future other types such as adaptive differential pulse code modualtion (ADPCM) may be supported.
In addition to the general information in the WAVEFORMAT!AL(`_win32_WAVEFORMAT',3,`No_SDK') structure, specific information is needed to describe a PCM waveform data format completely. For PCM waveform data, the PCMWAVEFORMAT!AL(`_win32_PCMWAVEFORMAT',3,`No_SDK') structure includes a WAVEFORMAT structure along with an additional member containing PCM-specific information.
+$#A Using the PCMWAVEFORMAT Structure
For PCM audio data, use the PCMWAVEFORMAT!AL(`_win32_PCMWAVEFORMAT',3,`No_SDK') structure to specify the data format. The following example shows how to set up a PCMWAVEFORMAT structure for 11.025 kilohertz (kHz) 8-bit mono and for 44.1 kHz 16-bit stereo. After setting up PCMWAVEFORMAT, the example calls the IsFormatSupported function to verify that the PCM waveform output device supports the format. The source code for IsFormatSupported is given in an example in "Determining Nonstandard Format Support."
UINT wReturn;
PCMWAVEFORMAT pcmWaveFormat;

/* Set up PCMWAVEFORMAT for 11 kHz 8-bit mono. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
pcmWaveFormat.wf.nChannels = 1;
pcmWaveFormat.wf.nSamplesPerSec = 11025L;
pcmWaveFormat.wf.nAvgBytesPerSec = 11025L;
pcmWaveFormat.wf.nBlockAlign = 1;
pcmWaveFormat.wBitsPerSample = 8;

/* See if format is supported by any device in system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)
MessageBox(hMainWnd, "11 kHz 8-bit mono is supported.",
"", MB_ICONINFORMATION);
else if (wReturn == WAVERR_BADFORMAT)
MessageBox(hMainWnd, "11 kHz 8-bit mono NOT supported.",
"", MB_ICONINFORMATION);
else
MessageBox(hMainWnd, "Error opening waveform device.",
"Error", MB_ICONEXCLAMATION);

/* Set up PCMWAVEFORMAT for 44.1 kHz 16-bit stereo. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
pcmWaveFormat.wf.nChannels = 2;
pcmWaveFormat.wf.nSamplesPerSec = 44100L;
pcmWaveFormat.wf.nAvgBytesPerSec = 176400L;
pcmWaveFormat.wf.nBlockAlign = 4;
pcmWaveFormat.wBitsPerSample = 32;

/* See if format is supported by any device in the system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)
MessageBox(hMainWnd, "44.1 kHz 16-bit stereo is supported.",
"", MB_ICONINFORMATION);
else if (wReturn == WAVERR_BADFORMAT)
MessageBox(hMainWnd, "44.1 kHz 16-bit stereo NOT supported.",
"", MB_ICONINFORMATION);
else
MessageBox(hMainWnd, "Error opening waveform device.",
"Error", MB_ICONEXCLAMATION);

+$#A Getting Format Information from a WAVE File
The easiest way to get waveform-format information from a WAVE file is by using the multimedia file input and output (I/O) services. To do this, use the mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK') function to locate the "fmt" chunk containing the format information and the mmioRead!AL(`_win32_mmioRead',3,`No_SDK') function to read the format chunk directly into the proper format structure. (Chunks are the basic building blocks of RIFF files.) The following example illustrates this technique. For more information about multimedia file I/O, see Multimedia File Input/Output_win32_Multimedia_File_Input_Output.
void GetFormatInfo(void)
{
HMMIO hmmio;
MMCKINFO mmckinfoParent;
MMCKINFO mmckinfoSubchunk;
DWORD dwFmtSize;
HANDLE hFormat;
WAVEFORMAT *pFormat;
.ifdef REF
.
.
.
.endif

/* Open the given file for reading using buffered I/O. */
.ifdef REF
...
.endif
/*
* Locate a "RIFF" chunk with a "WAVE" form type
* to make sure it's a WAVE file.
*/
.ifdef REF
...
.endif
/*
* Now, find the format chunk (form type "fmt "). It should be
* a subchunk of the "RIFF" parent chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK)) {
MessageBox(hwndApp, "WAVE file is corrupted.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}

/*
* Get the size of the format chunk;
* allocate and lock memory for it.
*/
dwFmtSize = mmckinfoSubchunk.cksize;
hFormat = LocalAlloc(LMEM_MOVEABLE, dwFmtSize);
if (hFormat == NULL) {
MessageBox(hwndApp, "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
pFormat = (WAVEFORMAT *) LocalLock(hFormat);
if (pFormat == NULL) {
MessageBox(hwndApp, "Failed to lock memory for format chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}

/* Read the format chunk. */
if (mmioRead(hmmio, (LPSTR)pFormat,
dwFmtSize) != (LRESULT)dwFmtSize) {
MessageBox(hwndApp, "Failed to read format chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}

/* Make sure it's a PCM file. */
if (pFormat->wFormatTag != WAVE_FORMAT_PCM) {
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
MessageBox(hwndApp, "The file is not a PCM file.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}

/*
* Make sure the system has a waveform output
* device capable of playing this format.
*/
if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat,
NULL, 0L, WAVE_FORMAT_QUERY)) {
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
MessageBox(hwndApp,
"The waveform device can't play this format.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
.ifdef REF
.
.
.
.endif
}

+$#A Writing Waveform Data
After successfully opening a waveform output device driver, you can begin waveform playback.
The waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK') function writes a data block to a waveform output device. Use the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure to specify the waveform data block you are sending. This structure contains a pointer to a locked data block, the length of the data block, and assorted flags.
After you send a data block to an output device by using waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK'), you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the system for the completion of each data block so that you know when to send additional blocks. For details about different techniques for monitoring data block completion, see Managing Audio Data Blocks_win32_Managing_Audio_Data_Blocks.
+$#A Example of Writing Waveform Data
The following example illustrates the steps required to allocate and set up a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') data structure and write a block of data to a waveform output device.
/*
* Global variables. Must be visible to window-procedure function
* so it can unlock and free the data block after it has been played.
*/
HANDLE hData = NULL; // handle of waveform data memory
HPSTR lpData = NULL; // pointer to waveform data memory

void WriteWaveData(void)
{
HWAVEOUT hWaveOut;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HMMIO hmmio;
UINT wResult;
HANDLE hFormat;
WAVEFORMAT *pFormat;
DWORD dwDataSize;
.ifdef REF
.
.
.
.endif
/* Open a waveform device for output using window callback. */
if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
(LPWAVEFORMAT)pFormat,
(LONG)hwndApp, 0L, CALLBACK_WINDOW)) {
MessageBox(hwndApp,
"Failed to open waveform output device.",
NULL, MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}

/*
* Allocate and lock memory for the waveform data. The memory
* for waveform data must be globally allocated with
* GMEM_MOVEABLE and GMEM_SHARE flags.
*/
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
if (!hData)
{
MessageBox(hwndApp, "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
if ((lpData = GlobalLock(hData)) == NULL) {
MessageBox(hwndApp, "Failed to lock memory for data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}

/* Read the waveform data subchunk. */
if(mmioRead(hmmio, (HPSTR) lpData, dwDataSize) != (LRESULT)dwDataSize) {
MessageBox(hwndApp, "Failed to read data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalUnlock(hData);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}

/*
* Allocate and lock memory for the header. This memory must
* also be globally allocated with GMEM_MOVEABLE and
* GMEM_SHARE flags.
*/
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (hWaveHdr == NULL) {
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(hwndApp, "Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}

lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL) {
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(hwndApp,
"Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}

/* After allocation, set up and prepare header. */
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwDataSize;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));

/*
* Now the data block can be sent to the output device. The
* waveOutWrite function returns immediately and waveform
* data is sent to the output device in the background.
*/
wResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
if (wResult != 0) {
waveOutUnprepareHeader(hWaveOut, lpWaveHdr,
sizeof(WAVEHDR));
GlobalUnlock( hData);
GlobalFree(hData);
MessageBox(hwndApp, "Failed to write block to device",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
.ifdef REF
.
.
.
.endif
}

+$#A PCM Waveform Data Format
The lpData member in the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure points to the waveform data samples. For 8-bit PCM data, each sample is represented by a single unsigned data byte. For 16-bit PCM data, each sample is represented by a 16-bit signed value. The following table summarizes the maximum, minimum, and midpoint values for PCM waveform data.
Data format Maximum value Minimum value Midpoint value
8-bit PCM 255 (0xFF) 0 128 (0x80)
16-bit PCM 32767 (0x7FFF) -32768 (0x8000) 0

The order of the data bytes varies between 8-bit and 16-bit, and between mono and stereo formats. The following tables show data packing for the first four words of different PCM waveform data formats.
Data packing for 8-bit mono PCM:
Sample 1 Sample 2 Sample 3 Sample 4
Channel 0 Channel 0 Channel 0 Channel 0

Data packing for 8-bit stereo PCM:
Sample 1 Sample 2
Channel 0 (left) Channel 1 (right) Channel 0 (left) "Channel 1 (right)"
Low-order byte High-order byte Low-order byte High-order byte

Data packing for 16-bit mono PCM:
Sample 1 Sample 2
Channel 0 low byte Channel 0 high byte Channel 0 low byte Channel 0 high byte

Data packing for 16-bit stereo PCM:
Sample 1
Channel 0 (left) byte Channel 0 (left) byte Channel 1 (right) byte Channel 1 (right) byte

+$#A Waveform Looping
Waveform looping is controlled by the dwLoops and dwFlags members in the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structures passed to the device with waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK'). Use the WHDR_BEGINLOOP and WHDR_ENDLOOP flags in the dwFlags member to specify the beginning and ending data blocks for looping.
To loop on a single block, specify both flags for the same block. To specify the number of loops, use the dwLoops member in the WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure for the first block in the loop.
You can stop waveform looping with the waveOutBreakLoop!AL(`_win32_waveOutBreakLoop',3,`No_SDK') function. The blocks making up the loop are played to the end before the loop is stopped.
+$#A Using Window Messages to Manage Waveform Playback
The following messages can be sent to a window-procedure function for managing waveform playback.
Message Description
MM_WOM_CLOSE!AL(`_win32_MM_WOM_CLOSE',3,`No_SDK') Sent when the device is closed using waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK').
MM_WOM_DONE!AL(`_win32_MM_WOM_DONE',3,`No_SDK') Sent when the device driver is finished with a data block sent using waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK').
MM_WOM_OPEN!AL(`_win32_MM_WOM_OPEN',3,`No_SDK') Sent when the device is opened using waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK').

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. For the MM_WOM_DONE!AL(`_win32_MM_WOM_DONE',3,`No_SDK') message, lParam specifies a pointer to a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure identifying the completed data block. The lParam parameter is unused for the MM_WOM_CLOSE!AL(`_win32_MM_WOM_CLOSE',3,`No_SDK') and MM_WOM_OPEN!AL(`_win32_MM_WOM_OPEN',3,`No_SDK') messages.
The most useful message is MM_WOM_DONE. When this message signals that playback of a data block is complete, you can clean up and free the data block. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_WOM_OPEN and MM_WOM_CLOSE messages.
The following example shows how to process the MM_WOM_DONE!AL(`_win32_MM_WOM_DONE',3,`No_SDK') message. The example assumes the application does not play multiple data blocks, so it can close the output device after playing a single data block.
/* WndProc--Main window-procedure function. */
LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
.ifdef REF
.
.
.

.endif
case MM_WOM_DONE:
/* A data block has been played and can now be freed. */
waveOutUnprepareHeader((HWAVEOUT) wParam,
(LPWAVEHDR) lParam, sizeof(WAVEHDR) );
GlobalUnlock(hData);
GlobalFree(hData);
waveOutClose((HWAVEOUT) wParam);

break;
}

return DefWindowProc(hWnd,msg,wParam,lParam);
}

+$# Using a Low-Level Callback Function to Manage Waveform Playback
The syntax of the low-level callback function for waveform output devices is as follows:
void CALLBACK waveOutCallback(hWaveOut, wMsg, dwInstance, dwParam1, dwParam2)
The following messages can be sent in the wMsg parameter of waveform output callback functions.
Message Description
WOM_CLOSE!AL(`_win32_MM_WOM_CLOSE',3,`No_SDK') Sent when the device is closed using the waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK') function.
WOM_DONE!AL(`_win32_MM_WOM_DONE',3,`No_SDK') Sent when the device driver is finished with a data block sent using the waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK') function.
WOM_OPEN!AL(`_win32_MM_WOM_OPEN',3,`No_SDK') Sent when the device is opened using the waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function.

These messages are similar to the messages sent to window-procedure functions, the parameters are different. A handle of the open waveform device is passed as a parameter to the callback function, along with the doubleword of instance data that was passed using waveOutOpen.
The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WOM_DONE message, dwParam1 specifies a pointer to a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure identifying the completed data block and dwParam2 is not used. For the WOM_OPEN!AL(`_win32_MM_WOM_OPEN',3,`No_SDK') and WOM_CLOSE!AL(`_win32_MM_WOM_CLOSE',3,`No_SDK') messages, neither of the parameters is used.
After the driver is finished with a data block, you can clean up and free the data block, as described in "Allocating and Preparing Audio Data Blocks." Because of the suggested restrictions on low-level audio callback functions, it is better to not do this from within the callback. See Using a Callback Function to Process Driver Messages!AL(`_win32_Using_a_Callback_Function_to_Process_Driver_Messages',3,`No_SDK') for details about suggested restrictions on using callback functions.
+$#A Getting the Current Playback Position
While a waveform is playing, you can monitor the current playback position within the waveform. The Win32 API provides the waveOutGetPosition!AL(`_win32_waveOutGetPosition',3,`No_SDK') function for this purpose.
This function takes three parameters: a handle of a waveform output device, a pointer to an MMTIME!AL(`_win32_MMTIME',3,`No_SDK') structure, and an unsigned integer specifying the size of the MMTIME structure.
For waveform devices, the preferred time format in which to represent the current position is samples. Thus the current position of a waveform device is specified as the number of samples for one channel from the beginning of the waveform.
To query the current position of a waveform device, set the wType member of the MMTIME!AL(`_win32_MMTIME',3,`No_SDK') structure to TIME_SAMPLES and pass the structure to waveOutGetPosition!AL(`_win32_waveOutGetPosition',3,`No_SDK').
+$#A Stopping, Pausing, and Restarting Playback
You can stop or pause playback while a waveform is playing. Once playback has been paused, you can restart it. The Win32 API provides the following functions for controlling waveform playback.
Function Description
waveOutPause!AL(`_win32_waveOutPause',3,`No_SDK') Pauses playback on a waveform output device.
waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK') Stops playback on a waveform output device and marks all pending data blocks as done.
waveOutRestart!AL(`_win32_waveOutRestart',3,`No_SDK') Resumes playback on a paused waveform output device.

Use the waveOutPause function to pause a waveform device that is currently playing. To resume playback on a paused device, use the waveOutRestart function. These functions take a single parameter: the waveform output device handle returned by the waveOutOpen function. Pausing a waveform device may not be instantaneous ¾ the driver can finish playing the current block before pausing playback.
Typically, as soon as the first waveform data block is sent by using the waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK') function, the waveform device begins playing. If you don't want the waveform to start playing immediately, call waveOutPause!AL(`_win32_waveOutPause',3,`No_SDK') before calling waveOutWrite. When you want to begin playing the waveform, call waveOutRestart!AL(`_win32_waveOutRestart',3,`No_SDK').
To stop a waveform from playing, use the waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK') function. This function differs from waveOutPause in that it also marks all pending data blocks as being done. You can not use waveOutRestart to restart a device that has been stopped by using waveOutReset. You must use waveOutWrite to send the first data block to resume playback on the device.
+$#A Closing Waveform Output Devices
After waveform playback is complete, call the waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK') function to close the waveform device. If waveOutClose is called while a waveform is playing, the close operation fails and the function returns an error code indicating that the device was not closed. If you don't want to wait for playback to end before closing the device, call the waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK') function before closing. This stops playback and allows the device to be closed. Be sure to clean up the preparation on all data blocks before closing the waveform output device.
+$#A Changing Waveform Playback Volume
Windows provides the following functions to query and set the volume level of waveform output devices.
Function Description
waveOutGetVolume!AL(`_win32_waveOutGetVolume',3,`No_SDK') Gets the volume level of the specified waveform device.
waveOutSetVolume!AL(`_win32_waveOutSetVolume',3,`No_SDK') Sets the volume level of the specified waveform device.

Not all waveform devices support volume changes. Some devices support individual volume control on both the left and right channels. For information about how to determine the volume-control capabilities of waveform devices, see Determining Capabilities of Waveform Output Devices_win32_Determining_Capabilities_of_Waveform_Output_Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure that your application opens an audio device before changing its volume. Also, be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value of which the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left (or mono) channel.
For devices that don't support left- and right-channel volume control, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Volume-level values range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same when increasing the volume level from 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.
The waveOutGetVolume!AL(`_win32_waveOutGetVolume',3,`No_SDK') function returns the volume as a doubleword value at an address specified by a pointer parameter.
+$#A Changing Pitch and Playback Rate
Some waveform output devices can vary the pitch and the playback rate of waveform data. Not all waveform devices support pitch and playback-rate changes. For information about how to determine if a particular waveform device supports pitch and playback-rate changes, see Determining Capabilities of Waveform Output Devices_win32_Determining_Capabilities_of_Waveform_Output_Devices.
Following are the differences between changing pitch and playback rate:
· Changing the playback rate is performed by the device driver and does not require specialized hardware. The sample rate is not changed, but the driver interpolates by skipping or synthesizing samples. For example, if the playback rate is changed by a factor of two, the driver skips every other sample.
· Changing the pitch requires specialized hardware. The playback rate and sample rate are not changed.

Windows provides the following functions to query and set waveform pitch and playback rates.
Function Description
waveOutGetPitch!AL(`_win32_waveOutGetPitch',3,`No_SDK') Gets the pitch for the specified waveform output device.
waveOutGetPlaybackRate!AL(`_win32_waveOutGetPlaybackRate',3,`No_SDK') Gets the playback rate for the specified waveform output device.
waveOutSetPitch!AL(`_win32_waveOutSetPitch',3,`No_SDK') Sets the pitch for the specified waveform output device.
waveOutSetPlaybackRate!AL(`_win32_waveOutSetPlaybackRate',3,`No_SDK') Sets the playback rate for the specified waveform output device.

The pitch and playback rates are changed by a factor specified with a fixed-point number packed into a doubleword value. The high-order 16 bits specify the integer part, and the low-order 16 bits specify the fractional part of the number. For example, the value 1.5 is represented as 0x00018000L. The value 0.75 is represented as 0x0000C000L. A value of 1.0 (0x00010000) means the pitch or playback rate is unchanged.
+$#A Recording Low-Level Waveform Audio
If the MCI waveform-recording services don't meet the needs of your application, you can handle waveform recording using the low-level waveform services.
+$#A Waveform Input Data Types
The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for waveform-input functions.
Type Description
HWAVEIN Handle of an open waveform input device.
WAVEINCAPS!AL(`_win32_WAVEINCAPS',3,`No_SDK') Data structure used to inquire about the capabilities of a particular waveform input device.
WAVEFORMAT!AL(`_win32_WAVEFORMAT',3,`No_SDK') Structure that specifies the data formats supported by a particular waveform input device. This structure is also used for waveform output devices.
WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') Structure that is a header for a block of waveform input data. This structure is also used for waveform output devices.

+$#A Querying Waveform Input Devices
Before recording a waveform, you should call the waveInGetDevCaps!AL(`_win32_waveInGetDevCaps',3,`No_SDK') function to determine the waveform input capabilities of the system. This function takes a pointer to a WAVEINCAPS!AL(`_win32_WAVEINCAPS',3,`No_SDK') structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the WAVEINCAPS structure provides information about the standard waveform formats that the device supports.
+$#A Opening Waveform Input Devices
Use the waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK') function to open a waveform input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.
+$#A Selecting a Waveform Input Device
Some multimedia computers will have multiple waveform input devices. Unless you know you want to open a specific waveform input device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK') function will choose the device in the system best able to record in the given data format.
+$#A Managing Waveform Recording
Once you open a waveform input device, you can begin recording waveform data. Waveform data is recorded into application-supplied buffers specified by a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure. This is the same structure used for waveform playback described in Writing Waveform Data_win32_Writing_Waveform_Data. Memory for the WAVEHDR structure and its accompanying data buffer must be allocated and prepared, as shown in Allocating and Preparing Audio Data Blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks.
Windows provides the following functions to manage waveform recording:
Function Description
waveInAddBuffer!AL(`_win32_waveInAddBuffer',3,`No_SDK') Sends a buffer to the device driver so it can be filled with recorded waveform data.
waveInReset!AL(`_win32_waveInReset',3,`No_SDK') Stops waveform recording and marks all pending buffers as done.
waveInStart!AL(`_win32_waveInStart',3,`No_SDK') Starts waveform recording.
waveInStop!AL(`_win32_waveInStop',3,`No_SDK') Stops waveform recording.

Use the waveInAddBuffer!AL(`_win32_waveInAddBuffer',3,`No_SDK') function to send data buffers to the device driver. As the buffers are filled with recorded waveform data, the application is notified with either a window message or with a callback message, depending on the flag specified when the device was opened.
Use the waveInStart!AL(`_win32_waveInStart',3,`No_SDK') function to begin recording. Before beginning to record, send at least one buffer to the driver or incoming data may be lost. To stop waveform recording, use the waveInStop!AL(`_win32_waveInStop',3,`No_SDK') function.
Before closing the device by using the waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK') function, call the waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK') function to mark any pending data blocks as being done.
+$#A Using Window Messages to Manage Waveform Recording
The following messages can be sent to a window-procedure function for managing waveform recording.
Message Description
MM_WIM_CLOSE!AL(`_win32_MM_WIM_CLOSE',3,`No_SDK') Sent when the device is closed using the waveInClose!AL(`_win32_waveInClose',3,`No_SDK') function.
MM_WIM_DATA!AL(`_win32_MM_WIM_DATA',3,`No_SDK') Sent when the device driver is finished with a data buffer sent using the waveInAddBuffer!AL(`_win32_waveInAddBuffer',3,`No_SDK') function.
MM_WIM_OPEN!AL(`_win32_MM_WIM_OPEN',3,`No_SDK') Sent when the device is opened using the waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK') function.

There is a wParam and lParam parameter associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. The lParam parameter is unused for the MM_WIM_CLOSE!AL(`_win32_MM_WIM_CLOSE',3,`No_SDK') and MM_WIM_OPEN!AL(`_win32_MM_WIM_OPEN',3,`No_SDK') messages.
For the MM_WIM_DATA!AL(`_win32_MM_WIM_DATA',3,`No_SDK') message, lParam specifies a pointer to a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure that identifies the data buffer. This data buffer may not be completely filled with waveform data, because recording can stop before the buffer is filled. Use the dwBytesRecorded member of the WAVEHDR structure to determine the amount of valid data present in the buffer.
The most useful message is MM_WIM_DATA. Unless you need to allocate memory or initialize variables, you probably don't need to use the MM_WIM_OPEN!AL(`_win32_MM_WIM_OPEN',3,`No_SDK') and MM_WIM_CLOSE!AL(`_win32_MM_WIM_CLOSE',3,`No_SDK') messages. When the device driver is finished with a data block, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks.
+$# Using a Low-Level Callback Function to Manage Waveform Recording
The syntax of the low-level callback function for waveform input devices is as follows.
void CALLBACK waveInCallback(hWaveIn, wMsg, dwInstance, dwParam1, dwParam2)
The following messages can be sent in the wMsg parameter of waveform input callback functions.
Message Description
WIM_CLOSE!AL(`_win32_MM_WIM_CLOSE',3,`No_SDK') Sent when the device is closed using the waveInClose!AL(`_win32_waveInClose',3,`No_SDK') function.
WIM_OPEN!AL(`_win32_MM_WIM_OPEN',3,`No_SDK') Sent when the device is opened using the waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK') function.

These messages resemble messages sent to window-procedure functions, but their parameters are different. A handle of an open waveform device is passed as a parameter to the callback function, along with the doubleword value of instance data that was passed using waveInOpen.
The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WIM_CLOSE and WIM_OPEN messages, these parameters are not used. For the WIM_DONE message, dwParam1 specifies a pointer to a WAVEHDR!AL(`_win32_WAVEHDR',3,`No_SDK') structure identifying the completed data block and dwParam2 is not used.
After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions of low-level audio callback functions, it is better not to do this within the callback function. It is better to set events or semaphores and do this outside the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages!AL(`_win32_Using_a_Callback_Function_to_Process_Driver_Messages',3,`No_SDK').
+$#A Playing MIDI Audio
To play MIDI files, use the MCI sequencer. If the MCI sequencer services don't meet the needs of your application, you can manage MIDI playback using the low-level MIDI services.
+$#A Low-Level Audio MIDI Output Data Types
The MMSYSTEM.H header file defines data types and function prototypes for all low-level audio functions. You must include this header file in any source module that uses these functions. MMSYSTEM.H defines the following data types for low-level MIDI output function:
Type Description
HMIDIOUT Handle of a MIDI output device.
MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') Structure is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIOUTCAPS!AL(`_win32_MIDIOUTCAPS',3,`No_SDK') Structure used to inquire about the capabilities of a particular MIDI output device.

+$#A Querying Low-Level MIDI Output Devices
Before playing MIDI audio, you should call the midiOutGetDevCaps!AL(`_win32_midiOutGetDevCaps',3,`No_SDK') function to determine the capabilities of the MIDI output hardware present in the system. This function takes a pointer to a MIDIOUTCAPS!AL(`_win32_MIDIOUTCAPS',3,`No_SDK') structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the MIDIOUTCAPS structure provides information on the device technology, the number of voices and notes supported, the MIDI channels that the device responds to, and features supported by the driver.
+$#A Determining the Technology of the Device
MIDI output devices can be either internal synthesizers or external MIDI output ports. The wTechnology member specifies the technology of the device. MMSYSTEM.H defines the following flags to identify device technology.
Flag Description
MOD_MIDIPORT The device is an external MIDI output port.
MOD_SQSYNTH Device is an internal square-wave synthesizer.
MOD_FMSYNTH Device is an internal FM synthesizer.
MOD_SYNTH Device is an internal synthesizer (generic).
MOD_MAPPER Device is the MIDI Mapper.

+$#A Determining Capabilities of Internal Synthesizers
If the device is an internal synthesizer, additional device information is available in the wVoices, wNotes, and wChannelMask members of the MIDIOUTCAPS!AL(`_win32_MIDIOUTCAPS',3,`No_SDK') structure. If the device is an external output port, these members are unused.
The wVoices member specifies the number of voices the device supports. Each voice can have a different sound or timbre. Voices are differentiated by MIDI channel. For example, a four-voice synthesizer uses four MIDI channels. The wNotes member specifies the polyphony of the device ¾ the maximum number of notes that can be played simultaneously. The wChannelMask member is a bit representation of the MIDI channels that the device responds to. For example, if the device responds to the first eight MIDI channels, wChannelMask is 0x00FF.
The dwSupport member of the MIDIOUTCAPS!AL(`_win32_MIDIOUTCAPS',3,`No_SDK') structure indicates whether the device driver supports volume changes and patch caching. MMSYSTEM.H defines the following flags for the dwSupport member.
Flag Description
MIDICAPS_VOLUME Indicates the driver supports volume control.
MIDICAPS_LRVOLUME Indicates the driver supports individual volume control for the left and right channels.
MIDICAPS_CACHE Indicates the driver supports patch caching.

Volume changes are only supported by internal synthesizer devices. External MIDI output ports don't support volume changes. For information about changing volume, see Changing Internal MIDI Synthesizer Volume!AL(`_win32_Changing_Internal_MIDI_Synthesizer_Volume',3,`No_SDK').
+$#A Opening Low-Level Audio MIDI Output Devices
Use the midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK') function to open a MIDI output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.
+$#A Sending MIDI Messages
Once you open a MIDI output device, you can begin sending it MIDI messages by using the following functions.
Function Description
midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') Sends a buffer of MIDI data to the specified MIDI output device. Use this function to send multiple MIDI events, including system-exclusive messages, to a MIDI device.
midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') Sends a MIDI message to a specified MIDI output device. This function can use running status by omitting the status byte.
midiOutReset!AL(`_win32_midiOutReset',3,`No_SDK') Turns off all notes on all channels for a specified MIDI output device. Any pending system-exclusive buffers are marked as done and returned to the application.

Use midiOutShortMsg to send any MIDI message (except for system-exclusive messages). This function takes an HMIDIOUT parameter specifying the MIDI output device to which to send the message, and a doubleword value for the MIDI message. The message is packed into the doubleword value as follows.
Word Byte Contents
High-order word High-order byte Not used
Low-order byte MIDI data 2 (optional)
Low-order word High-order Byte MIDI data 1 (optional)
Low-order byte MIDI status

The two MIDI data bytes are optional, depending on the MIDI status byte. The following example uses midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') to send a given MIDI event to a given MIDI output device.
/* Sends a given MIDI event to the given output device */

UINT sendMIDIEvent(hMidiOut, bStatus, bData1, bData2)
HMIDIOUT hMidiOut; // handle of the output device
BYTE bStatus; // MIDI status byte
BYTE bData1; // first MIDI data byte
BYTE bData2; // second MIDI data byte
{
union {
DWORD dwData;
BYTE bData[4];
} u;

/* Construct the MIDI message */
u.bData[0] = bStatus;
u.bData[1] = bData1;
u.bData[2] = bData2;
u.bData[3] = 0;

/* Send the message */
return midiOutShortMsg(hMidiOut, u.dwData);
}

Note MIDI output drivers are not required to verify data before sending it to an output port. It is up to applications to ensure that only valid data is sent using the midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') function.

+$#A Sending Buffered Messages
MIDI system-exclusive messages are the only MIDI messages that will not fit into a single doubleword value. System exclusive messages can be any length. Windows provides the midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') function for sending one or more messages, including system-exclusive messages, to MIDI output devices.
Use the MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure to specify MIDI system-exclusive data blocks. This structure contains a pointer to a locked data block, the data-block length, and assorted flags.
Memory for the MIDIHDR structure and the data block pointed to by lpData must be allocated and prepared, as shown in "Allocating and Preparing Audio Data Blocks."
After you send a system-exclusive data block using midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK'), you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the completion of each data block so you know when to send additional blocks. (If your application uses a callback function or callback window, it will receive the MOM_DONE message when the driver finishes with the data block.) For information on different techniques for monitoring data-block completion, see Managing Audio Data Blocks_win32_Managing_Audio_Data_Blocks.

Note Any MIDI status byte other than a system-real-time message will terminate a system-exclusive message. If you are using multiple data blocks to send a single system-exclusive message, do not send any MIDI messages other than system-real-time messages between data blocks.

+$#A Using Window Messages to Manage Buffered Playback
The following messages can be sent to a window-procedure function for managing MIDI system-exclusive playback.
Message Description
MM_MOM_CLOSE!AL(`_win32_MM_MOM_CLOSE',3,`No_SDK') Sent when the device is closed using the midiOutClose!AL(`_win32_midiOutClose',3,`No_SDK') function.
MM_MOM_DONE!AL(`_win32_MM_MOM_DONE',3,`No_SDK') Sent when the device driver is finished with a data block sent using the midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') function.
MM_MOM_OPEN!AL(`_win32_MM_MOM_OPEN',3,`No_SDK') Sent when the device is opened using the midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK') function.

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. For the MM_MOM_DONE message, lParam specifies a pointer to a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure identifying the completed data block. The lParam parameter is unused for the MM_MOM_CLOSE and MM_MOM_OPEN messages.
The most useful message is the MM_MOM_DONE!AL(`_win32_MM_MOM_DONE',3,`No_SDK') message. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_MOM_OPEN!AL(`_win32_MM_MOM_OPEN',3,`No_SDK') and MM_MOM_CLOSE!AL(`_win32_MM_MOM_CLOSE',3,`No_SDK') messages. When playback of a data block is completed, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks.
+$#A Using a Callback to Manage Buffered Playback
This syntax of the low-level callback function for MIDI output devices is as follows.
void CALLBACK midiOutCallback(hMidiOut, wMsg, dwInstance, dwParam1, dwParam2)
The following messages can be sent to the wMsg parameter of MIDI output callback functions.
Message Description
MOM_CLOSE!AL(`_win32_MOM_CLOSE',3,`No_SDK') Sent when the device is closed using the midiOutClose!AL(`_win32_midiOutClose',3,`No_SDK') function.
MOM_DONE!AL(`_win32_MOM_DONE',3,`No_SDK') Sent when the device driver is finished with a data block sent using the midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') function.
MOM_OPEN!AL(`_win32_MOM_OPEN',3,`No_SDK') Sent when the device is opened using the midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK') function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback function along with the doubleword value of instance data passed using midiOutOpen.
The callback function has two message-specific parameters: dwParam1 and dwParam2. For the MOM_OPEN and MOM_CLOSE messages, these parameters are not used. For the MOM_DONE message, dwParam1 specifies a pointer to a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure identifying the completed data block and dwParam2 is not used.
After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages!AL(`_win32_Using_a_Callback_Function_to_Process_Driver_Messages',3,`No_SDK').
+$#A Sending MIDI Messages Using Running-Status
The MIDI 1.0 Specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. You can send MIDI messages using running status by using the midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') function by packing the message into a doubleword value as follows.
Word Byte Contents
High-order word High-order byte Not used
Low-order byte Not used
Low-order word High-order Byte MIDI data 2 (optional)
Low-order byte MIDI data 1

+$#A Resetting Low-Level Audio MIDI Output
The midiOutReset!AL(`_win32_midiOutReset',3,`No_SDK') function will turn off all notes, and mark any pending system-exclusive buffers as done and return them to the application. It may be useful in an application that uses MIDI output to provide the user with the ability to reset MIDI output.

Note Terminating a system-exclusive message without sending an EOX (end-of-exclusive) byte may cause problems for the receiving device. The midiOutReset function does not send an EOX byte when it terminates a system-exclusive message; applications are responsible for doing this.

+$#A Changing Low-Level Audio Internal MIDI Synthesizer Volume
Windows provides the following functions to query and set the volume level of internal MIDI synthesizer devices.
Function Description
midiOutGetVolume!AL(`_win32_midiOutGetVolume',3,`No_SDK') Gets the volume level of the specified internal MIDI synthesizer device.
midiOutSetVolume!AL(`_win32_midiOutSetVolume',3,`No_SDK') Sets the volume level of the specified internal MIDI synthesizer device.

Not all MIDI output devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume change, see Determining Capabilities of Waveform Output Devices_win32_Determining_Capabilities_of_Waveform_Output_Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure your application opens an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restore the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value; the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left channel.
For devices that don't support individual volume changes on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.
When querying volume levels using midiOutGetVolume!AL(`_win32_midiOutGetVolume',3,`No_SDK'), the volume is returned in a doubleword location specified by a pointer parameter.
+$# Preloading Low-Level Audio Patches With Internal MIDI Synthesizers
Some internal MIDI synthesizer devices can't keep all of their patches loaded simultaneously. These devices must preload their patch data.
Windows provides the following functions to request that a synthesizer preload and cache specified patches.
Function Description
midiOutCachePatches!AL(`_win32_midiOutCachePatches',3,`No_SDK') Requests that an internal MIDI synthesizer device preload and cache specified melodic patches.
midiOutCacheDrumPatches!AL(`_win32_midiOutCacheDrumPatches',3,`No_SDK') Requests that an internal MIDI synthesizer device preload and cache specified key-based percussion patches.

The midiOutCachePatches function takes a pointer to a PATCHARRAY to indicate the patches to be cached.
Each element in the array corresponds to a patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if patch number 0 is used by physical MIDI channels 0 and 8, set element 0 of the array to 0x0101.
The midiOutCacheDrumPatches!AL(`_win32_midiOutCacheDrumPatches',3,`No_SDK') function takes a pointer to a KEYARRAY to indicate the key-based percussion patches to be cached.
Each element in the array corresponds to a key-based percussion patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if the percussion patch for key number 60 is used by physical MIDI channels 9 and 15, set element 60 of the array to 0x8200.
+$#A Using the MIDI Mapper with Low-Level MIDI Functions
The MIDI Mapper provides standard patch services for device-independent playback of MIDI files. Applications that use MIDI files for audio should use the MIDI Mapper. For information about the MIDI Mapper and about authoring device-independent MIDI files, see High-Level Audio_win32_High_Level_Audio.
To use the MIDI Mapper, open it by using the midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK') function with the wDeviceID parameter set to the constant MIDIMAPPER. You can then send MIDI messages to the MIDI Mapper by using the midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK') or midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK') function.
+$#A Recording Low-Level MIDI Audio
To record MIDI audio data, you must use low-level MIDI input functions. MCI does not currently provide a device handler for recording MIDI audio.
+$#A Low-Level Audio MIDI Input Data Types
The MMSYSTEM.H header file defines data types and function prototypes for all of the low-level audio functions. MMSYSTEM.H defines the following data types for low-level MIDI input functions.
Type Description
HMIDIIN A handle of a MIDI input device.
MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') A structure that is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIINCAPS!AL(`_win32_MIDIINCAPS',3,`No_SDK') A structure used to inquire about the capabilities of a MIDI input device.

+$#A Querying MIDI Input Devices for Low-Level Audio
Before recording MIDI audio, you should call the midiInGetDevCaps!AL(`_win32_midiInGetDevCaps',3,`No_SDK') function to determine the capabilities of the MIDI input hardware present in the system. This function takes a pointer to a MIDIINCAPS!AL(`_win32_MIDIINCAPS',3,`No_SDK') structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.
+$#A Opening Low-Level MIDI Input Devices
Use the midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK') function to open a MIDI input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.
+$#A Managing MIDI Recording of Low-Level Audio
Once you open a MIDI input device, you can begin recording MIDI data. Win32 provides the following functions for managing MIDI recording.
Function Description
midiInAddBuffer!AL(`_win32_midiInAddBuffer',3,`No_SDK') Sends a buffer to the device driver so it can be filled with recorded MIDI data.
midiInReset!AL(`_win32_midiInReset',3,`No_SDK') Stops MIDI recording and marks all pending buffers as done.
midiInStart!AL(`_win32_midiInStart',3,`No_SDK') Starts MIDI recording and resets the time stamp to zero.
midiInStop!AL(`_win32_midiInStop',3,`No_SDK') Stops MIDI recording.

Use the midiInAddBuffer function to send data buffers to the device driver for recording system-exclusive messages. Before you use midiInAddBuffer, you must allocate and prepare memory for the MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') data structure and the data block pointed to by the lpData member. For information about allocating and preparing this memory, see Allocating and Preparing Audio Data Blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks.
Use the midiInStart!AL(`_win32_midiInStart',3,`No_SDK') function to begin recording. To record system-exclusive messages, send at least one buffer to the driver with midiInAddBuffer!AL(`_win32_midiInAddBuffer',3,`No_SDK') before starting to record. As the buffers are filled with recorded data, the application is notified by one of the techniques discussed in "Managing Audio Data Blocks."
To stop recording, use the midiInStop!AL(`_win32_midiInStop',3,`No_SDK') function. Before closing the device by using the midiInClose!AL(`_win32_midiInClose',3,`No_SDK') function, call midiInReset!AL(`_win32_midiInReset',3,`No_SDK') to mark any pending data blocks as being done. Before freeing the memory used by a data block, call the midiInUnprepareHeader!AL(`_win32_midiInUnprepareHeader',3,`No_SDK') function to clean up the preparation performed previously.
You must use either a window-procedure function or a low-level callback function to receive MIDI data. If you want time-stamped data, you must use a low-level callback function.
To record system-exclusive messages, you must supply the device driver with data buffers. These buffers are specified by a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure. This is the same structure used for MIDI buffered playback as described in "Sending Buffered Messages." Memory for the MIDIHDR structure and its accompanying data buffer must be allocated and prepared as shown in Allocating and Preparing Audio Data Blocks_win32_Allocating_and_Preparing_Audio_Data_Blocks.
+$#A Using Window Messages to Manage MIDI Recording
The following messages can be sent to a window-procedure function for managing MIDI recording.
Message Description
MM_MIM_CLOSE!AL(`_win32_MM_MIM_CLOSE',3,`No_SDK') Sent when the device is closed using the midiInClose!AL(`_win32_midiInClose',3,`No_SDK') function.
MM_MIM_DATA!AL(`_win32_MM_MIM_DATA',3,`No_SDK') Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_ERROR!AL(`_win32_MM_MIM_ERROR',3,`No_SDK') Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_LONGDATA!AL(`_win32_MM_MIM_LONGDATA',3,`No_SDK') Sent when either a complete MIDI system-exclusive message is received, or when a data buffer sent using the midiInAddBuffer!AL(`_win32_midiInAddBuffer',3,`No_SDK') function is filled with system-exclusive data.
MM_MIM_LONGERROR!AL(`_win32_MM_MIM_LONGERROR',3,`No_SDK') Sent when an invalid MIDI system-exclusive message is received.
MM_MIM_OPEN!AL(`_win32_MM_MIM_OPEN',3,`No_SDK') Sent when the device is opened using the midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK') function.

A wParam and an lParam parameter are associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. The lParam parameter is unused for the MM_MIM_CLOSE and MM_MIM_OPEN messages.
For the MM_MIM_DATA!AL(`_win32_MM_MIM_DATA',3,`No_SDK') message, lParam specifies the received MIDI data. This data is packed into a doubleword value as follows.
Word Byte Contents
High-order word High-order byte
Low-order byte MIDI data 2 (optional)
Low-order word High-order Byte MIDI data 1 (optional)
Low-order byte MIDI status

For the MM_MIM_LONGDATA!AL(`_win32_MM_MIM_LONGDATA',3,`No_SDK') message, lParam specifies a pointer to a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure that identifies the data buffer for system-exclusive messages. The data buffer might not be completely filled ¾ you usually don't know the size of the system-exclusive messages before recording them, so you must allocate a buffer large enough for the largest expected message. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.
+$# Using a Low-Level Callback Function to Manage MIDI Recording
This syntax of the low-level callback function for MIDI input devices is as follows:
void CALLBACK midiInCallback(hMidiIn, wMsg, dwInstance, dwParam1, dwParam2)
The following messages can be sent in the wMsg parameter of MIDI input callback functions.
Message Description
MIM_CLOSE!AL(`_win32_MIM_CLOSE',3,`No_SDK') Sent when the device is closed using the midiInClose function.
MIM_DATA!AL(`_win32_MIM_DATA',3,`No_SDK') Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_ERROR!AL(`_win32_MIM_ERROR',3,`No_SDK') Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_LONGERROR!AL(`_win32_MIM_LONGERROR',3,`No_SDK') Sent when an invalid MIDI system-exclusive message is received.
MIM_LONGDATA!AL(`_win32_MIM_LONGDATA',3,`No_SDK') Sent when either a complete MIDI system-exclusive message is received, or when a data buffer is filled with system-exclusive data.
MIM_OPEN!AL(`_win32_MIM_OPEN',3,`No_SDK') Sent when the device is opened using the midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK') function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback, along with the doubleword value of instance data that was passed using the midiInOpen value.
The callback has two message-specific parameters: dwParam1 and dwParam2. For the MIM_OPEN!AL(`_win32_MIM_OPEN',3,`No_SDK') and MIM_CLOSE!AL(`_win32_MIM_CLOSE',3,`No_SDK') messages, these parameters are unused.
For the MIM_DATA!AL(`_win32_MIM_DATA',3,`No_SDK') message, dwParam1 specifies the received MIDI data and dwParam2 specifies a time stamp for the data. The data is packed into a doubleword value, as shown in the previous section on using window messages.
For the MIM_LONGDATA!AL(`_win32_MIM_LONGDATA',3,`No_SDK') message, dwParam1 specifies a pointer to a MIDIHDR!AL(`_win32_MIDIHDR',3,`No_SDK') structure that identifies the data buffer for system-exclusive messages. As with the MIM_DATA message, dwParam2 specifies a time stamp for the data. The data buffer may not be completely filled. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.
After the device driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. It is better to set events or semaphores and do this outside of the callback function. See Using a Callback Function to Process Driver Messages!AL(`_win32_Using_a_Callback_Function_to_Process_Driver_Messages',3,`No_SDK') for details about the suggested restrictions on using callback functions.
+$#A Receiving Time-Stamped MIDI Messages
Because of the delay between when the device driver receives a MIDI message and the time the application receives the message, MIDI input device drivers time-stamp the MIDI message with the time the message was received. MIDI time stamps are defined as the time the first byte of the message was received and are specified in milliseconds. The midiInStart!AL(`_win32_midiInStart',3,`No_SDK') function resets the time stamps for a device to zero.
As stated earlier, to receive time stamps with MIDI input, you must use a low-level callback function. The dwParam2 parameter of the callback function specifies the time stamp for data associated with the MIM_DATA!AL(`_win32_MIM_DATA',3,`No_SDK') and MIM_LONGDATA!AL(`_win32_MIM_LONGDATA',3,`No_SDK') messages.
+$#A Receiving Running-Status Messages
The MIDI 1.0 specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. All MIDI input device drivers are required to expand messages using running status to complete messages ¾ you always receive complete MIDI messages from a MIDI input device driver.
+$#A Auxiliary Audio Devices
Auxiliary audio devices are audio devices whose output is mixed with the MIDI and waveform output devices in a multimedia computer. An example of an auxiliary audio device is the compact disc audio output from a CD-ROM drive.
Control for auxiliary audio devices is provided by a software-controlled audio mixer. The mixer can reside on the motherboard of a multimedia computer, or it can be on an add-in sound card. The following illustration shows the conceptual audio-signal routing in a multimedia computer.
In this multimedia computer, there are two auxiliary audio devices: the CD-ROM and the external audio input. The external audio input is an input jack that allows the user to connect other audio devices.
+$#A Querying Low-Level Auxiliary Audio Devices
Not all multimedia systems have auxiliary audio support. You can use the auxGetNumDevs!AL(`_win32_auxGetNumDevs',3,`No_SDK') function to determine the number of controllable auxiliary devices present in a system.
To get information on a particular auxiliary audio device, use the auxGetDevCaps!AL(`_win32_auxGetDevCaps',3,`No_SDK') function. This function takes a pointer to an AUXCAPS!AL(`_win32_AUXCAPS',3,`No_SDK') structure that it fills with information about the capabilities of a given device.
+$#A Determining Auxiliary Audio Device Types
The MMSYSTEM.H header file defines the following flags for the wTechnology member of the AUXCAPS!AL(`_win32_AUXCAPS',3,`No_SDK') structure to determine the device type of an auxiliary audio device.
Flag Description
AUXCAPS_CDAUDIO The device is an internal CD-ROM drive.
AUXCAPS_AUXIN The device is an auxiliary audio input jack.

+$#A Determining Capabilities of Auxiliary Audio Devices
The dwSupport member of the AUXCAPS!AL(`_win32_AUXCAPS',3,`No_SDK') structure indicates whether the device driver supports volume changes. The MMSYSTEM.H header file defines the following flags for the dwSupport member.
Flag Description
AUXCAPS_VOLUME Volume-control support
AUXCAPS_LRVOLUME Individual volume-control support for both the left and right channels

+$#A Changing Auxiliary Audio-Device Volume
Windows provides the following functions to query and set the volume for auxiliary audio devices.
Function Description
auxGetVolume!AL(`_win32_auxGetVolume',3,`No_SDK') Gets the volume level of the specified auxiliary audio device.
auxSetVolume!AL(`_win32_auxSetVolume',3,`No_SDK') Sets the volume level of the specified auxiliary audio device.

Not all auxiliary audio devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume changes, see Determining Capabilities of Waveform Output Devices_win32_Determining_Capabilities_of_Waveform_Output_Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, you should open an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

The volume level is specified (as it was in the waveform and MIDI volume-control functions) in a doubleword value with the high-order 16 bits specifying the relative volume of the right channel and the low-order 16 bits specifying the relative volume of the left channel. For devices that don't support individual volume control on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored.
Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. This means the perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.
When querying volume levels with the auxGetVolume!AL(`_win32_auxGetVolume',3,`No_SDK') function, the volume is returned in a doubleword location specified by a pointer parameter.
+$#A Audio Clipboard Formats
In addition to the standard and nonstandard waveform formats previously presented in this section, there are two Clipboard formats that can be used to represent audio data: CF_WAVE and CF_RIFF. Use the CF_WAVE format to represent data in one of the standard formats, such as 11 kHz or 22 kHz PCM. Use the CF_RIFF format to represent more complex data formats that cannot be represented as standard wave files.
+$#A Low-Level Audio Functions
Following are the functions used with low-level audio services.
auxGetDevCaps!AL(`_win32_auxGetDevCaps',3,`No_SDK')auxGetNumDevs!AL(`_win32_auxGetNumDevs',3,`No_SDK')auxGetVolume!AL(`_win32_auxGetVolume',3,`No_SDK')auxOutMessage!AL(`_win32_auxOutMessage',3,`No_SDK')auxSetVolume!AL(`_win32_auxSetVolume',3,`No_SDK')midiInAddBuffer!AL(`_win32_midiInAddBuffer',3,`No_SDK')midiInClose!AL(`_win32_midiInClose',3,`No_SDK')midiInGetDevCaps!AL(`_win32_midiInGetDevCaps',3,`No_SDK')midiInGetErrorText!AL(`_win32_midiInGetErrorText',3,`No_SDK')midiInGetID!AL(`_win32_midiInGetID',3,`No_SDK')midiInGetNumDevs!AL(`_win32_midiInGetNumDevs',3,`No_SDK')midiInMessage!AL(`_win32_midiInMessage',3,`No_SDK')midiInOpen!AL(`_win32_midiInOpen',3,`No_SDK')midiInPrepareHeader!AL(`_win32_midiInPrepareHeader',3,`No_SDK')midiInReset!AL(`_win32_midiInReset',3,`No_SDK')midiInStart!AL(`_win32_midiInStart',3,`No_SDK')midiInStop!AL(`_win32_midiInStop',3,`No_SDK')midiInUnprepareHeader!AL(`_win32_midiInUnprepareHeader',3,`No_SDK')midiOutCacheDrumPatches!AL(`_win32_midiOutCacheDrumPatches',3,`No_SDK')midiOutCachePatches!AL(`_win32_midiOutCachePatches',3,`No_SDK')midiOutClose!AL(`_win32_midiOutClose',3,`No_SDK')midiOutGetDevCaps!AL(`_win32_midiOutGetDevCaps',3,`No_SDK')midiOutGetErrorText!AL(`_win32_midiOutGetErrorText',3,`No_SDK')midiOutGetID!AL(`_win32_midiOutGetID',3,`No_SDK')midiOutGetNumDevs!AL(`_win32_midiOutGetNumDevs',3,`No_SDK')midiOutGetVolume!AL(`_win32_midiOutGetVolume',3,`No_SDK')midiOutLongMsg!AL(`_win32_midiOutLongMsg',3,`No_SDK')midiOutMessage!AL(`_win32_midiOutMessage',3,`No_SDK')midiOutOpen!AL(`_win32_midiOutOpen',3,`No_SDK')midiOutPrepareHeader!AL(`_win32_midiOutPrepareHeader',3,`No_SDK')midiOutReset!AL(`_win32_midiOutReset',3,`No_SDK')midiOutSetVolume!AL(`_win32_midiOutSetVolume',3,`No_SDK')midiOutShortMsg!AL(`_win32_midiOutShortMsg',3,`No_SDK')midiOutUnprepareHeader!AL(`_win32_midiOutUnprepareHeader',3,`No_SDK')MidiProc_win32_MidiProcwaveInAddBuffer!AL(`_win32_waveInAddBuffer',3,`No_SDK')waveInClose!AL(`_win32_waveInClose',3,`No_SDK')waveInGetDevCaps!AL(`_win32_waveInGetDevCaps',3,`No_SDK')waveInGetErrorText!AL(`_win32_waveInGetErrorText',3,`No_SDK')waveInGetID!AL(`_win32_waveInGetID',3,`No_SDK')waveInGetNumDevs!AL(`_win32_waveInGetNumDevs',3,`No_SDK')waveInGetPosition!AL(`_win32_waveInGetPosition',3,`No_SDK')waveInMessage!AL(`_win32_waveInMessage',3,`No_SDK')waveInOpen!AL(`_win32_waveInOpen',3,`No_SDK')waveInPrepareHeader!AL(`_win32_waveInPrepareHeader',3,`No_SDK')waveInReset!AL(`_win32_waveInReset',3,`No_SDK')waveInStart!AL(`_win32_waveInStart',3,`No_SDK')waveInStop!AL(`_win32_waveInStop',3,`No_SDK')waveInUnprepareHeader!AL(`_win32_waveInUnprepareHeader',3,`No_SDK')waveOutBreakLoop!AL(`_win32_waveOutBreakLoop',3,`No_SDK')waveOutClose!AL(`_win32_waveOutClose',3,`No_SDK')waveOutGetDevCaps!AL(`_win32_waveOutGetDevCaps',3,`No_SDK')waveOutGetErrorText!AL(`_win32_waveOutGetErrorText',3,`No_SDK')waveOutGetID!AL(`_win32_waveOutGetID',3,`No_SDK')waveOutGetNumDevs!AL(`_win32_waveOutGetNumDevs',3,`No_SDK')waveOutGetPitch!AL(`_win32_waveOutGetPitch',3,`No_SDK')waveOutGetPlaybackRate!AL(`_win32_waveOutGetPlaybackRate',3,`No_SDK')waveOutGetPosition!AL(`_win32_waveOutGetPosition',3,`No_SDK')waveOutGetVolume!AL(`_win32_waveOutGetVolume',3,`No_SDK')waveOutMessage!AL(`_win32_waveOutMessage',3,`No_SDK')waveOutOpen!AL(`_win32_waveOutOpen',3,`No_SDK')waveOutPause!AL(`_win32_waveOutPause',3,`No_SDK')waveOutPrepareHeader!AL(`_win32_waveOutPrepareHeader',3,`No_SDK')waveOutReset!AL(`_win32_waveOutReset',3,`No_SDK')waveOutRestart!AL(`_win32_waveOutRestart',3,`No_SDK')waveOutSetPitch!AL(`_win32_waveOutSetPitch',3,`No_SDK')waveOutSetPlaybackRate!AL(`_win32_waveOutSetPlaybackRate',3,`No_SDK')waveOutSetVolume!AL(`_win32_waveOutSetVolume',3,`No_SDK')waveOutUnprepareHeader!AL(`_win32_waveOutUnprepareHeader',3,`No_SDK')waveOutWrite!AL(`_win32_waveOutWrite',3,`No_SDK')WaveProc_win32_WaveProc
+$#A Media Control Interface
The Media Control Interface (MCI) provides applications written for Microsoft® Windows® with device-independent capabilities for controlling media devices such as audio hardware, videodisc players, and animation players. This overview presents an introduction to MCI and shows how to begin using it in Windows-based applications.
+$#A About the Media Control Interface
MCI provides a device-independent, extensible interface for controlling virtually any type of media device. Because of the high level of device independence provided by MCI, you are encouraged to use MCI rather than low-level functions to access the multimedia capabilities of Windows.
Click on a topic below to view it:
· The Architecture of MCI_win32_The_Architecture_of_MCI
· The MCI Command Set_win32_The_MCI_Command_Set
· MCI Programming Interfaces_win32_The_MCI_Programming_Interfaces
· The Command-Message interface_win32_The_Command_Message_Interface
· The Command-String Interface_win32_The_Command_String_Interface (an introduction)

For more information about using MCI to control different types of audio devices, see High-Level Audio_win32_High_Level_Audio.
+$#A The Architecture of MCI
To provide extensibility, MCI is designed around an architecture that uses special MCI device drivers to interpret and execute MCI commands.
MCI device drivers can control media hardware directly or through the low-level multimedia functions provided by Windows. Most commonly used devices, such as waveform audio and Musical Instrument Digital Interface (MIDI) devices, are controlled through the low-level multimedia functions.
+$#A The MCI Programming Interfaces
MCI provides two programming interfaces: command string_win32_The_Command_String_Interface and command message_win32_The_Command_Message_Interface.
+$#A The Command-String Interface
The command-string interface allows you to use English-language commands to communicate with MCI devices. For example, the following command string plays a WAVE file named TIMPANI.WAV.
play timpani.wav

The command-string interface is designed for use with high-level programming and authoring environments such as Microsoft® Visual Basic® and Asymetrix® ToolBook®. Use the command-string interface for applications that provide a text-based interface to let users control MCI devices.
+$#A The Command-Message Interface
The command-message interface uses a message-passing paradigm to communicate with MCI devices. The following example performs the same operation as the command-string example in The Command-String Interface_win32_The_Command_String_Interface.
WORD wDeviceID;
MCI_PLAY_PARMS mciPlayParms;

mciSendCommand(wDeviceID, /* device identifier */
MCI_PLAY, /* command message */
0, /* flags */
(DWORD) (LPVOID) &mciPlayParms); /* structure */

The command-message interface is designed to be used by applications requiring a C-language interface to control multimedia devices. Use the command-message interface for applications that directly control multimedia devices.
+$#A The MCI Command Set
The MCI command set provides a generic core set of commands to control different types of media devices. For example, MCI uses the same command to begin playback of a waveform audio file, a videodisc track, and an animation sequence. Some devices have unique capabilities, such as the capability of an animation player to use a frame-based time format. For such devices, MCI provides extended commands that are unique to a particular type of device.
The commands in the command-string interface provide a good overview of the MCI command set. Each of these commands is represented by a similar command message in the command-message interface. For example, the close command string is the equivalent of the MCI_CLOSE command message.
Following are some commonly used commands.
Command Description
capability Requests information about the capabilities of a device.
close Closes a device after it has been used.
info Requests information about a device (for example, a description of the hardware associated with the device).
open Opens and initializes a device.
pause Pauses playing or recording on a device.
play Begins playing on a device.
record Begins recording on a device.
resume Resumes playing or recording on a paused device.
seek Changes the current position in the media.
set Changes control settings, such as the time format the device is using.
status Requests information about the status of a device (for example, whether the device is playing or paused).
stop Stops playing or recording on a device.

+$#A About MCI Devices
MCI device drivers can be classified as either simple devices or compound devices. Simple devices do not require a data file for playback. Videodisc players and compact disc (CD) audio players are examples of simple devices. Compound devices require a data file for playback. MIDI sequencers and waveform audio players are examples of compound devices. The data file associated with a compound device is known as a device element. Examples of device elements are MIDI files and WAVE files.
+$#A Standard MCI Device Types
A device type identifies a class of MCI devices that responds to a common set of commands. Following are the currently defined device types.
Device type Description
animation Animation device
cdaudio CD audio player
dat Digital audio tape player
digitalvideo Digital video in a window (not based on graphics device interface)
other Undefined MCI device
overlay Overlay device (analog video in a window)
scanner Image scanner
sequencer MIDI sequencer
videodisc Videodisc player
waveaudio Audio device that plays digitized waveform files

+$#A MCI Device Names
For any given device type, there might be several MCI drivers that share the command set but operate on different data formats. For example, for animation devices there are several MCI drivers that use the same command set but use different file formats. To uniquely identify an MCI driver, MCI uses device names.
Device names are identified in the [mci32] section of the registry. This section identifies all MCI device drivers to Windows. The following is part of a typical [mci] section.
[mci32]
WaveAudio=mciwave.dll
Sequencer=mciseq.dll
AVIVideo=mciavi.dll
CDAudio=mcicda.dll

The key name (on the left side of the equal sign) is the device name. The value corresponding to the key name (on the right side of the equal sign) identifies the filename of the MCI driver. The device name is often the same as the device type for the driver, as is the case for the WaveAudio, Sequencer, and CDAudio devices in the preceding example. The AVIVideo device is a digital video device, but it uses a unique device name.
If an MCI driver is installed using a device name that already exists in the [mci32] section, Windows appends an integer to the device name of the new driver, creating a unique device name. In the preceding example, a driver installed using the cdaudio device name would be assigned device name cdaudio1. A subsequent cdaudio device would be assigned device name cdaudio2.
+$#A Opening MCI Devices
Before using an MCI device, you must initialize it by opening it. Opening a device loads its driver into memory (if it isn't already loaded) and establishes a device identifier to identify the device in subsequent commands. (The device identifier is not used in the command-string interface.) There are several ways to specify the device you want to open:
· With simple devices, you can open the device by specifying the device name.
· With compound devices, you can open the device by specifying the device name, the device element, or both.

For example, the following command string opens a CD audio device (a simple device) by specifying the device name.
open cdaudio

The next command string opens a waveform audio device (a compound device) by specifying the device name and a device element.
open bells.wav type waveaudio

You can also open a compound device by specifying only the device element, as shown in the following example.
open bells.wav

If you specify only a device element when opening a compound device, MCI uses the filename extension of the device element filename to determine which device to open. The registry contains an [mci extensions] section that associates filename extensions and corresponding MCI device types. The following is part of an [mci extensions] section.
[mci extensions]
Wav=WaveAudio
Mid=Sequencer
rmi=Sequencer
avi=avivideo

+$#A Using the Command-Message Interface
Applications that require a C-language interface to MCI should use the command-message interface. The following topics provide background and detailed information about using this interface.
About Command Messages_win32_About_Command_MessagesSummary of MCI Command Messages_win32_Summary_of_MCI_Command_MessagesSending MCI Command Messages_win32_Sending_MCI_Command_MessagesOpening an MCI Device_win32_Opening_an_MCI_DeviceClosing an MCI Device_win32_Closing_an_MCI_DeviceUsing the Wait and Notify Flags_win32_Using_the_Wait_and_Notify_FlagsUsing MCI_SYSINFO to Obtain MCI System Information_win32_Using_MCI_SYSINFO_to_Obtain_MCI_System_InformationAbout the Command-String Interface_win32_About_the_Command_String_InterfaceSending MCI Command Strings_win32_Sending_MCI_Command_StringsAdditional Information About Command Strings_win32_Additional_Information_About_Command_Strings
+$#A About Command Messages
MCI command messages consist of the following three elements:
· A constant message value
· A structure (parameter block) specifying additional parameters for the command
· A set of flags specifying options for the command and validating members in the structure

Commands are sent by using the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function, which has parameters for the message, flags, and a pointer to the structure. For more information about the mciSendCommand function, see Sending MCI Command Messages_win32_Sending_MCI_Command_Messages.
+$#A Command-Message Example
For an example of an MCI command message, consider MCI_PLAY!AL(`_win32_MCI_PLAY',3,`No_SDK'). The MMSYSTEM.H header file defines the MCI_PLAY command message, the MCI_NOTIFY, MCI_WAIT, MCI_FROM, and MCI_TO flags, and the MCI_PLAY_PARMS!AL(`_win32_MCI_PLAY',3,`No_SDK')_PARMS structure. The MCI_PLAY_PARMS structure has the following form.
typedef struct {
DWORD dwCallback;
DWORD dwFrom;
DWORD dwTo;
} MCI_PLAY_PARMS;

The MCI_WAIT and MCI_NOTIFY flags are option flags. MCI_WAIT tells the driver to wait until the command is complete before returning control to the application. MCI_NOTIFY tells the driver to notify the application when the command is complete. For more information on using these flags, see Using the Wait and Notify Flags_win32_Using_the_Wait_and_Notify_Flags.
The MCI_FROM and MCI_TO flags validate the dwFrom and dwTo members in the MCI_PLAY_PARMS!AL(`_win32_MCI_PLAY',3,`No_SDK')_PARMS structure. These members specify beginning and ending positions for the play operation. Both flags are optional. If either flag is specified, the driver takes the value from the corresponding member in the structure as a beginning or ending position; otherwise, the members are ignored.

Note Members in a structure are ignored unless corresponding flags are specified to validate the members.

+$#A Items in Query Commands
Two command messages that query devices for information, MCI_GETDEVCAPS!AL(`_win32_MCI_GETDEVCAPS',3,`No_SDK') and MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK'), extend the command-message paradigm by adding a dwItem member to the structure and defining item constants for the member. Item constants specify which item of information is being requested. For example, MMSYSTEM.H defines the MCI_STATUS_PARMS structure for the MCI_STATUS command message as follows.
typedef struct {
DWORD dwCallback;
DWORD dwReturn;
DWORD dwItem;
DWORD dwTrack;
} MCI_STATUS_PARMS;

If you use the MCI_STATUS_ITEM flag to validate the dwItem member, make sure that dwItem contains a constant defined by MMSYSTEM.H to indicate the exact status information requested. The MCI_STATUS command message uses the following constants for dwItem:
MCI_STATUS_CURRENT_TRACKMCI_STATUS_LENGTHMCI_STATUS_MEDIA_PRESENTMCI_STATUS_MODEMCI_STATUS_NUMBER_OF_TRACKSMCI_STATUS_POSITIONMCI_STATUS_READYMCI_STATUS_TIME_FORMAT
+$#A Summary of MCI Command Messages
MCI defines four classifications of command messages. The command messages and options in the following two classifications are defined as the minimum command set for any MCI driver:
· System command messages are handled directly by MCI rather than by the driver.
· Required command messages are handled by the driver. All drivers should support the required command messages and options.

Regardless of the specific driver you use, you should be able to assume that the command messages and options in the two preceding groups are available.
The command messages in the following two classifications are not supported by all drivers:
· Basic (or optional) command messages are used by some devices. If a device supports a basic command message, it must support a defined set of options for the command message.
· Extended command messages are specific to certain device types or drivers. They include new command messages (for example, the MCI_PUT and MCI_WHERE command messages for the animation device type) and extensions to existing command messages (for example, the MCI_ANIM_GETDEVCAPS_CAN_STRETCH option added to the MCI_STATUS command message for the animation device type).

If you need to use a basic or extended command message or option, you should query the device driver before trying to use the command message or option (that is, unless you are certain that the MCI driver you have used during development is the same one that will be available on the delivery system). The following tables summarize the specific command messages in each category.
MCI directly processes the following system command messages, rather than passing them to MCI device drivers.
Command Description
MCI_BREAK!AL(`_win32_MCI_BREAK',3,`No_SDK') Sets a break key for an MCI device.
MCI_SYSINFO!AL(`_win32_MCI_SYSINFO',3,`No_SDK') Returns information about MCI devices.

Required command messages must be supported by all MCI device drivers. The following table lists the required command messages.
Command Description
MCI_CLOSE!AL(`_win32_MCI_CLOSE',3,`No_SDK') Closes the device.
MCI_GETDEVCAPS!AL(`_win32_MCI_GETDEVCAPS',3,`No_SDK') Obtains the capabilities of a device.
MCI_INFO!AL(`_win32_MCI_INFO',3,`No_SDK') Obtains textual information from a device.
MCI_OPEN!AL(`_win32_MCI_OPEN',3,`No_SDK') Initializes the device.
MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') Obtains status information from the device.

Following are the basic command messages. The use of these command messages by a device is optional.
Command Description
MCI_LOAD!AL(`_win32_MCI_LOAD',3,`No_SDK') Loads data from a file.
MCI_PAUSE!AL(`_win32_MCI_PAUSE',3,`No_SDK') Stops playing.
MCI_PLAY!AL(`_win32_MCI_PLAY',3,`No_SDK') Starts transmitting output data.
MCI_RECORD!AL(`_win32_MCI_RECORD',3,`No_SDK') Starts recording data.
MCI_RESUME!AL(`_win32_MCI_RESUME',3,`No_SDK') Resumes playing or recording on a paused device.
MCI_SAVE!AL(`_win32_MCI_SAVE',3,`No_SDK') Saves data to a file.
MCI_SEEK!AL(`_win32_MCI_SEEK',3,`No_SDK') Seeks forward or backward.
MCI_SET!AL(`_win32_MCI_SET',3,`No_SDK') Sets the operating state of the device.
MCI_STATUS!AL(`_win32_MCI_STATUS',3,`No_SDK') Obtains status information about the device. (Supplements the required MCI_STATUS command message with options for devices using linear media with identifiable positions.)
MCI_STOP!AL(`_win32_MCI_STOP',3,`No_SDK') Stops playing or recording.

Some MCI devices have additional command messages or add options to existing command messages. Although some extended command messages apply only to a specific device driver, most of them apply to all drivers of a particular device type. For example, the sequencer command set extends the MCI_SET command message to add time formats that are needed by MIDI sequencers.
Unless you are certain that the specific MCI driver you use during development will be available on the delivery system, do not assume that the device supports the extended command messages or options. You can use the MCI_GETDEVCAPS!AL(`_win32_MCI_GETDEVCAPS',3,`No_SDK') command message to determine whether a specific feature is supported. Applications should be ready to handle an MCIERR_UNSUPPORTED_FUNCTION return value.
The following extended command messages are available with certain device types.
Command Message Device types Description
MCI_CUE!AL(`_win32_MCI_CUE',3,`No_SDK') waveaudio Prepares for playing or recording.
MCI_DELETE!AL(`_win32_MCI_DELETE',3,`No_SDK') waveaudio Deletes a data segment from the media element.
MCI_ESCAPE!AL(`_win32_MCI_ESCAPE',3,`No_SDK') videodisc Sends custom information to a device.
MCI_FREEZE!AL(`_win32_MCI_FREEZE',3,`No_SDK') overlay Disables video acquisition to the frame buffer.
MCI_PUT!AL(`_win32_MCI_PUT',3,`No_SDK') animation overlay Defines the source, destination, and frame windows.
MCI_REALIZE!AL(`_win32_MCI_REALIZE',3,`No_SDK') animation Tells the device to select and realize its palette into a display context of the displayed window.
MCI_SPIN!AL(`_win32_MCI_SPIN',3,`No_SDK') videodisc Starts the disc spinning or stops the disc from spinning.
MCI_STEP!AL(`_win32_MCI_STEP',3,`No_SDK') animation, videodisc Steps the play one or more frames forward or reverse.
MCI_UNFREEZE!AL(`_win32_MCI_UNFREEZE',3,`No_SDK') overlay Enables the frame buffer to acquire video data.
MCI_UPDATE!AL(`_win32_MCI_UPDATE',3,`No_SDK') animation Repaints the current frame into the display context.
MCI_WHERE!AL(`_win32_MCI_WHERE',3,`No_SDK') animation, overlay Obtains the rectangle specifying the source, destination, or frame area.
MCI_WINDOW!AL(`_win32_MCI_WINDOW',3,`No_SDK') animation, overlay Controls display window options such as caption text.

+$#A Sending MCI Command Messages
Windows provides the following functions to send command messages to devices and to query devices for error information.
Function Description
mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') Sends a command message to an MCI device.
mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK') Returns the error string corresponding to an error number.

+$#A The MCI Device Identifier
MCI returns a device identifier when you open a device by using the MCI_OPEN!AL(`_win32_MCI_OPEN',3,`No_SDK') command message. Use this identifier to identify the opened device when sending subsequent commands.
MMSYSTEM.H defines a special constant, MCI_ALL_DEVICE_ID, to indicate that a command message is to be sent to all of the MCI devices that an application has opened. You can use MCI_ALL_DEVICE_ID with any command message that does not return information. For instance, the following example closes all of the MCI devices that have been opened by an application.
DWORD dwReturn;

/*
* Closes all MCI devices opened by this application.
* Waits until devices are closed before returning.
*/
if (dwReturn = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE,
MCI_WAIT, NULL))
/* Error, unable to close all devices. */
return FALSE;

Note Although using the MCI_ALL_DEVICE_ID identifier is a convenient way to broadcast a command to all your devices, don't rely on it to synchronize devices; the timing between messages can vary.

+$#A Opening an MCI Device
Before using a device, you must initialize it by using the MCI_OPEN!AL(`_win32_MCI_OPEN',3,`No_SDK') command message. The variations of this command message make it one of the most complex of all MCI commands.
+$#A The MCI_OPEN_PARMS Structure
Like all MCI command messages, MCI_OPEN!AL(`_win32_MCI_OPEN',3,`No_SDK') has an associated structure. The default structure for MCI_OPEN is the MCI_OPEN_PARMS!AL(`!ALink(_win32_MCI_OPEN, 3, No_SDK)_PARMS',3,`No_SDK') structure. Certain devices, such as waveaudio, animation, and overlay devices, have extended structures to accommodate additional optional parameters. Unless you need to use these additional parameters, you can use the MCI_OPEN_PARMS structure with any MCI device.
MCI uses the wDeviceID member of this structure to return the device identifier to the application. You should check the return value of the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function before using the device identifier, to be sure that it is valid. A nonzero return value indicates that an error occurred during the opening of the device process.

Note You can also determine the device identifier by specifying the device name in a call to the mciGetDeviceID!AL(`_win32_mciGetDeviceID',3,`No_SDK') function.

Other members used in opening a device correspond to the following flags for the command message.
Flag Meaning
MCI_OPEN_ALIAS The lpstrAlias member of the structure contains a pointer to a device alias.
MCI_OPEN_ELEMENT The lpstrElementName member of the structure contains a pointer to the element name.
MCI_OPEN_SHAREABLE The device or element was opened as shareable.
MCI_OPEN_TYPE The lpstrDeviceType member of the structure contains a pointer to the device-type identifier.
MCI_OPEN_TYPE_ID The lpstrDeviceType member of the structure contains an integer device-type identifier.

+$#A Opening Simple Devices
To open a simple device such as a CD audio or videodisc device, use the lpstrDeviceType member in the MCI_OPEN_PARMS!AL(`!ALink(_win32_MCI_OPEN, 3, No_SDK)_PARMS',3,`No_SDK') structure to identify the device to open. There are three ways to identify devices:
· Specify a pointer to a null-terminated string containing the device name.
· Specify a device-type constant.
· Specify the actual name of the device driver.

Regardless of which method you use to identify the device, you must specify the MCI_OPEN_TYPE flag to validate the lpstrDeviceType member. For instance, the following example opens a CD audio device by specifying the device name.
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;

/* Open a compact disc device by specifying the device name. */

mciOpenParms.lpstrDeviceType = "cdaudio";
if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE,
(DWORD)(LPVOID) &mciOpenParms))
/* Error, unable to open device. */
return FALSE;

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying a device-type constant. To be opened by means of a device-type constant, device drivers must be installed in the [mci] section of the registry by using the following device types.
Device type Device-type constant
animation MCI_DEVTYPE_ANIMATION
cdaudio MCI_DEVTYPE_CD_AUDIO
dat MCI_DEVTYPE_DAT
digitalvideo MCI_DEVTYPE_DIGITAL_VIDEO
other MCI_DEVTYPE_OTHER
overlay MCI_DEVTYPE_OVERLAY
scanner MCI_DEVTYPE_SCANNER
sequencer MCI_DEVTYPE_SEQUENCER
videodisc MCI_DEVTYPE_VIDEODISC
waveaudio MCI_DEVTYPE_WAVEFORM_AUDIO

If you open a device by specifying a device-type constant, you must specify the MCI_OPEN_TYPE_ID flag in addition to the MCI_OPEN_TYPE flag. The following example opens a CD audio device by specifying a device-type constant.
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;

/*
* Open a compact disc device by
* specifying a device-type constant.
*/

mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
if (dwReturn = mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID,
(DWORD)(LPVOID) &mciOpenParms))
/* Error, unable to open device. */
return FALSE;

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

When you open a device by specifying a device-type constant, MCI uses the high-order word of the lpstrDeviceType member to distinguish between multiple occurrences of the device type in the registry.
For example, if the registry lists entries for waveaudio1 and waveaudio2, the following statement sets the lpstrDeviceType member to specify the device identified by waveaudio2.
LPSTR lpstrDeviceType;

lpstrDeviceType = (LPSTR)MAKELONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 2);

Similarly, to specify the device identified by waveaudio1, set the high-order word of lpstrDeviceType to 0 or 1. (Setting the high-order word to 0 tells MCI to use an unnumbered device or the lowest-numbered device.)
You can also open a device by specifying the filename of the device driver in the lpstrDeviceType member of the MCI_OPEN_PARMS!AL(`!ALink(_win32_MCI_OPEN, 3, No_SDK)_PARMS',3,`No_SDK') structure. You don't need to specify the complete path or the filename extension ¾ MCI assumes that drivers are located in the Windows system directory and have a filename extension of .DRV.

Note Opening MCI devices by specifying the filename of the device driver may be useful during development and testing. However, this technique is not recommended for applications because it makes them device dependent. Applications that open MCI devices by filename may not work in all systems.

+$#A Opening Compound Devices
There are three ways to open a compound device:
· Specify only the device (by using the device name, a device-type constant, or the filename of the device driver).
· Specify both the device and the device element.
· Specify only the device element.

The first method ¾ specifying only the device ¾ is useful for determining the capabilities of a device by using the MCI_GETDEVCAPS command message. To actually control a media device, you must specify a device element when you open the device.
To specify a device element, fill the lpstrElementName member of the MCI_OPEN_PARMS!AL(`!ALink(_win32_MCI_OPEN, 3, No_SDK)_PARMS',3,`No_SDK') structure with a pointer to a null-terminated string containing the filename of the device element. You must also validate the lpstrElementName member by specifying the MCI_OPEN_ELEMENT flag in the dwFlags parameter of the mciSendCommand!AL(`_win32_mciSendCommand',3,`No_SDK') function.
You can specify the device in the same manner used for simple devices: by device name, by device-type constant, or by the filename of the driver. The following example opens the waveaudio device with a WAVE file named TIMPANI.WAV.
UINT wDeviceID;
DWORD dwReturn;
MCI_OPEN_PARMS mciOpenParms;

/*
* Open a waveaudio device by specifying
* the device name and device element.
*/

mciOpenParms.lpstrDeviceType = "waveaudio";
mciOpenParms.lpstrElementName = "timpani.wav";
if (dwReturn = mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)(LPVOID) &mciOpenParms))
/* Error, unable to open device. */
return FALSE;

/* Device opened successfully. Get the device ID. */
wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying just a device element. MCI uses the filename extension of the given device element along with the [mci extensions] section of the registry to select the device to open. For more information about opening devices by specifying only a device element, see Opening MCI Devices_win32_Opening_MCI_Devices.
+$#A Closing an MCI Device
The MCI_CLOSE!AL(`_win32_MCI_CLOSE',3,`No_SDK') command message releases access to a device or device element. MCI frees a device when all processes using a device have closed it. To help MCI manage devices, applications should explicitly close each device or device element when they are finished using it.

Note It is very important that applications close all opened MCI devices before terminating. You can close all devices with a single command message by specifying the MCI_ALL_DEVICE_ID constant as the device identifier.

+$#A Using the Wait and Notify Flags
Typically, MCI returns control to the application immediately, even if it takes several seconds to complete the action initiated by a command. For example, after a CD audio device receives a seek command, control returns to the application before the seek operation is complete. You can use the following two flags to request that MCI wait until a command is complete before returning control to the application, or that MCI notify the application when the command is complete.
Flag Description
MCI_NOTIFY Directs the device to return control immediately and post the MM_MCINOTIFY message when the requested action is complete.
MCI_WAIT Directs the device to wait until the requested action is complete before returning control to the application.

+$#A Using the Notify Flag
The MCI_NOTIFY flag directs MCI to post the MM_MCINOTIFY message when the device completes an action. The application specifies the handle of the destination window for the message in the dwCallBack member of the structure sent with the command message. Every structure associated with a command message contains this member.

Note MCI can have only one pending notification message for each device.

When MCI posts the MM_MCINOTIFY!AL(`_win32_MM_MCINOTIFY',3,`No_SDK') message, it sets the low-order word of the lParam parameter of the window procedure to the identifier of the device initiating the callback. The wParam parameter of the window procedure contains one of the following constants specifying the notification status of the callback.
Flag Meaning
MCI_NOTIFY_SUCCESSFUL The conditions required for initiating the callback are satisfied and the command finished without interruption.
MCI_NOTIFY_SUPERSEDED MCI had a notification pending and received another notify request. MCI resets the callback conditions to correspond to the notify request of the new command.
MCI_NOTIFY_ABORTED The application sent a new command that prevented the callback conditions set by a previous command from being satisfied. If an application interrupts a command that requested notification, MCI does not inform the window procedure of the notify command being aborted.
MCI_NOTIFY_FAILURE A device error occurred while a device was executing the command. For example, MCI posts this message when a hardware error occurs during a play command.

+$#A Using the Wait Flag and Break Command Message
Specifying the MCI_WAIT flag with a command message causes MCI to wait until the command has finished before returning control to the application. Using the wait flag with commands that take a long time essentially disables a system from receiving any user input until the command is complete.
MCI supports a break key to interrupt the wait operation and return control to the application. The break key does not interrupt the command, but by returning control to the application, it allows the application to monitor user input.
By default, MCI defines the break key as CTRL+BREAK. Applications can redefine this key by using the MCI_BREAK!AL(`_win32_MCI_BREAK',3,`No_SDK') command message. MCI_BREAK uses the MCI_BREAK_PARMS!AL(`_win32_MCI_BREAK',3,`No_SDK')_PARMS structure.
The following flags validate members in the MCI_BREAK_PARMS structure:
Flag Description
MCI_BREAK_HWND Validates the hwndBreak member, which specifies the window that must have the focus to enable break detection.
MCI_BREAK_KEY Validates the nVirtKey member, which specifies the virtual-key code to be used for the break key.
MCI_BREAK_OFF Disables any existing break key.

+$#A Using MCI_SYSINFO to Obtain MCI System Information
The MCI_SYSINFO!AL(`_win32_MCI_SYSINFO',3,`No_SDK') command message obtains system information about MCI devices. MCI handles this message without relaying it to any MCI device. MCI returns the system information in the MCI_SYSINFO_PARMS!AL(`_win32_MCI_SYSINFO',3,`No_SDK')_PARMS structure.
The MCI_SYSINFO command message has the following flags.
Flag Meaning
MCI_SYSINFO_QUANTITY MCI returns the number of devices of a given type listed in the [mci] section of the registry. When this flag is used with the MCI_SYSINFO_OPEN flag, the number of open devices is returned.
MCI_SYSINFO_NAME MCI returns the name of a device that satisfies the query. When this flag is used with the MCI_SYSINFO_OPEN flag, only the names of open devices are returned.
MCI_SYSINFO_OPEN MCI returns the number or names of open devices.
MCI_SYSINFO_INSTALLNAME MCI returns the name listed in the registry entry used to install the device.

You use the MCI_SYSINFO_QUANTITY flag to determine the number of devices for a particular type listed in the [mci] section of the registry. For this flag, set the wDeviceType member of the structure to the device type. When requesting the number of devices, set the wDeviceID parameter to NULL. MCI returns the number of devices as a doubleword in the lpstrReturn member of the structure. If you combine the MCI_SYSINFO_QUANTITY flag with the MCI_SYSINFO_OPEN flag, MCI returns the number of devices of that type opened by the process.
Once you have the number of devices, you can enumerate the names of the devices by using the MCI_SYSINFO_NAME flag. To get information by using this flag, you must create a buffer for the return name. In the structure, specify a pointer to the buffer, the size of the buffer, the index number corresponding to the device (1 is the first device), and the device type. This information is entered in the lpstrReturn, dwRetSize, dwNumber, and wDeviceType members. MCI returns the device name in the buffer. When requesting the name of a particular device, set the wDeviceID parameter to NULL. To restrict the names to open devices, use the MCI_SYSINFO_OPEN flag with MCI_SYSINFO_NAME.
To retrieve information on all devices in the system, you can assign the MCI_ALL_DEVICE_ID constant to the wDeviceID parameter. When you use this identifier, MCI ignores the contents of the wDeviceType member and returns information on all MCI devices listed in the registry. When you use the MCI_SYSINFO_OPEN flag with the MCI_ALL_DEVICE_ID identifier, MCI returns information about the devices opened by the task.
You can retrieve the installation name of an open device by using the MCI_SYSINFO_INSTALLNAME flag. To get the installation name, you must create a buffer for the return name. In the structure, specify a pointer to the buffer and the size of the buffer in the lpstrReturn and dwRetSize members. MCI places the device that corresponds to the wDeviceID parameter name in the buffer.
+$#A About the Command-String Interface
This section presents an introduction to the MCI command-string interface. MCI provides the following functions for working with command strings.
Function Description
mciSendString!AL(`_win32_mciSendString',3,`No_SDK') Sends a command string to an MCI device driver. This function also has parameters for callback functions and return strings.
mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK') Returns the error string corresponding to an error number.

+$#A Sending MCI Command Strings
Use the mciSendString!AL(`_win32_mciSendString',3,`No_SDK') function to send command strings to a device. The following statement passes the "play to 500" command string to a waveaudio device driver.
DWORD dwErrorCode;

dwErrorCode = mciSendString("play waveaudio to 500", NULL, 0, 0L);

If the mciSendString function succeeds, it returns zero. If the function fails, it returns an error code. To get extended error information, use the mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK') function.
+$#A Additional Information About Command Strings
The MCITEST application contains examples of using the mciSendString!AL(`_win32_mciSendString',3,`No_SDK') and mciGetErrorString!AL(`_win32_mciGetErrorString',3,`No_SDK') functions. You can use this application to experiment with MCI devices and command strings.

+$#A Multimedia File Input/Output
Most multimedia applications require file input/output (I/O) ¾ the ability to create, read, and write disk files. The multimedia file I/O services provided by the Microsoft® Win32® application programming interface (API) offer buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible with custom I/O procedures.
+$#A About the Multimedia File Input/Output Services
Most applications need only the basic file I/O services and the RIFF file I/O services. Applications sensitive to file I/O performance, such as applications that stream data from a CD-ROM in real time, can optimize performance by using services to directly access the file I/O buffer. Applications that access custom storage systems can provide their own I/O procedure that reads and writes elements of the storage system. A storage system is a method of physically storing data in a file, such as a file-archival system or a database storage system.
This section covers the following topics:
· Using basic file I/O services
· Performing buffered file I/O
· Working with RIFF files
· Directly accessing a file I/O buffer
· Using memory files
· Writing a custom I/O procedure

The multimedia file I/O services provide support for the following file I/O operations:
· Basic unbuffered and buffered file I/O
· RIFF file I/O
· Direct access to the file I/O buffer
· Memory files
· Custom storage system I/O using application-supplied I/O procedures

+$#A Comparison With Other File I/O
You might ask why you need another set of file I/O services, when you already have the services of the C run-time and Win32 API libraries. The multimedia file I/O services offer the following advantages over other file I/O services:
· They are a part of the system software, so they don't increase the size of your application, like linking to the C run-time libraries
· They provide more functionality than the Win32 services, such as support for buffered I/O, RIFF files, memory files, and custom storage systems

In addition, the multimedia file I/O services are optimized for performance-intensive applications.
+$#A Function Prefixes
All multimedia file I/O function names begin with the mmio prefix. Similarly, all multimedia file I/O message names begin with the MMIOM_ prefix.
+$#A Data Types
The MMSYSTEM.H header file defines data types and function prototypes for all multimedia file I/O functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H depends on declarations made in WINDOWS.H, so you must first include the WINDOWS.H header file. MMSYSTEM.H defines the following data types for the multimedia file I/O functions.
Type Description
FOURCC A four-character code identifying an element of a RIFF file.
HMMIO A handle of an open file.
MMCKINFO!AL(`_win32_MMCKINFO',3,`No_SDK') A structure containing information about a chunk in a RIFF file.
MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') A structure used to maintain the current state of a file accessed using the multimedia file I/O services.
MMIOPROC A custom multimedia file I/O procedure.

+$#A Performing Basic File Input/Output
Using the basic I/O services is similar to using the C run-time file I/O services. Files must be opened before they can be read or written. After reading or writing, the file must be closed. You can change the current read/write location by seeking to a specified position in an open file. The following table lists the basic file I/O functions.
Function Description
mmioClose!AL(`_win32_mmioClose',3,`No_SDK') Closes an open file.
mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') Opens a file for reading or writing or both and returns a handle of the open file.
mmioRead!AL(`_win32_mmioRead',3,`No_SDK') Reads a specified number of bytes from an open file.
mmioSeek!AL(`_win32_mmioSeek',3,`No_SDK') Changes the current position for reading or writing in an open file.
mmioWrite!AL(`_win32_mmioWrite',3,`No_SDK') Writes a specified number of bytes to an open file.

These file I/O functions provide the core of the multimedia file I/O services ¾ you can use them for buffered and unbuffered I/O, as well as for I/O to RIFF files, memory files, and custom storage systems.
+$#A Opening a Multimedia File
Before doing any I/O operations to a file, you must first open the file by using the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function. The mmioOpen function returns a file handle that you use to identify the open file when calling other file I/O functions.

Note Do not use HMMIO file handles with Windows or C run-time file I/O functions.

There are options you can use with the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function for operations beyond basic file I/O. By specifying an MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure with the lpmmioinfo parameter you can open memory files, specify a custom I/O procedure, or supply a buffer for buffered I/O. These topics are discussed later in this section. First, this section discusses the most basic use of mmioOpen ¾ opening files for basic unbuffered file I/O.
To open a file for basic I/O operations, set the lpmmioinfo parameter of mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') to NULL. For example, the following example opens a file named C:\SAMPLES\SAMPLE1.TXT for reading, and checks the return value for errors.
HMMIO hFile;
.ifdef REF
.
.
.
.endif
if ((hFile = mmioOpen("C:\\SAMPLES\\SAMPLE1.TXT",
NULL, MMIO_READ)) != NULL) /* File opened successfully */
return TRUE;
else
/* File cannot be opened. */
return FALSE;

+$#A Options for Opening a File
When you open a file, you must specify whether you are opening the file for reading, writing, or both reading and writing. In addition, you can specify other options, such as to create or delete a new file. Use the dwFlags parameter of mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') to specify options for opening a file.
Following are the basic options for opening a file using mmioOpen:
Flag Description
MMIO_READ Opens a file for reading only.
MMIO_WRITE Opens a file for writing only.
MMIO_READWRITE Opens a file for reading and writing.
MMIO_CREATE Creates a new file (if the file already exists, it truncates it to zero length).
MMIO_DELETE Deletes a file.
MMIO_ALLOCBUF Opens a file for buffered I/O.

The MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags are read/write privilege flags. These flags are mutually exclusive ¾ specify only one when opening a file. If you don't specify one of these flags, mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') opens the file for reading only.
The MMIO_CREATE and MMIO_DELETE flags are also mutually exclusive. You can specify one of the read/write privilege flags with the MMIO_CREATE flag. You can't specify any additional flags with the MMIO_DELETE flag.
For information on using the MMIO_ALLOCBUF flag, see Performing Buffered File Input/Output_win32_Performing_Buffered_File_Input_Output.
In addition to the basic options for opening a file, there are sharing options you can use for opening files so they can be opened and accessed by more than one process. Following are the sharing options for opening a file using mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK').
Flag Description
MMIO_COMPAT Opens a file in compatibility mode.
MMIO_EXCLUSIVE Opens a file in exclusive mode.
MMIO_DENYWRITE Opens a file and denies other processes write access to the file.
MMIO_DENYREAD Opens a file and denies other processes read access to the file.
MMIO_DENYNONE Opens a file without denying other processes read or write access to the file.

+$#A Creating and Deleting Files
To create a new file, specify the MMIO_CREATE option with the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function. For example, the following example creates a new file and opens it for both reading and writing.
HMMIO hFile;
.ifdef REF
.
.
.
.endif
hFile = mmioOpen("NEWFILE.TXT",
NULL, MMIO_CREATE | MMIO_READWRITE);
if (hFile != NULL)
/* File created successfully. */
return TRUE;
else
/* File could not be created. */
return FALSE;

Note If the file you are creating already exists, it will be truncated to zero length.

To delete a file, specify the MMIO_DELETE flag with the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function. Once you delete a file, it can't be recovered. If the file deletion is the result of a request from a user, always query the user to be sure the user wants to delete the specified file before you actually delete it.
+$#A Reading and Writing a File
To read and write to open files, use the mmioRead!AL(`_win32_mmioRead',3,`No_SDK') and mmioWrite!AL(`_win32_mmioWrite',3,`No_SDK') functions. Each of these functions takes an HMMIO file handle, a pointer to a buffer, and a parameter specifying the number of bytes to read or write.
For an example using mmioRead to read from a file, see Example of RIFF File I/O_win32_Example_of_RIFF_File_I_O.
+$#A Seeking to a New Position in a Multimedia File
The current position or file pointer in a file is the location where the next read or write operation will occur. To change the current position in an open file, use the mmioSeek!AL(`_win32_mmioSeek',3,`No_SDK') function.
The return value is the new position, specified in bytes from the beginning of the file. If an error occurs, the return value is -1. If you seek to an invalid location in a file, such as past the end of the file, mmioSeek may not return an error, but subsequent I/O operations can fail.
+$#A Performing Buffered File Input/Output
Most of the overhead in file I/O involves accessing the media (the physical device). If you are reading or writing many small blocks of information, the media device can spend a lot of time seeking the physical location on the media for each read or write operation. In this case, better performance is achieved by using buffered file I/O. With buffered I/O, the file I/O manager maintains an intermediate buffer larger than the blocks of information you are reading or writing. It only accesses the media when the buffer must be filled from or written to the disk.
+$#A Deciding When to Use Buffered File I/O
It's difficult to provide exact metrics telling you when you need to use buffered I/O. It depends on how many read and write operations you perform on a file, and on the size of these read and write operations. A general guideline is to use buffered I/O if you are doing many small I/O operations (less than 2 kilobytes each). But this rule isn't absolute ¾ it's best to understand exactly how your program uses file I/O and experiment to optimize file I/O for your program's requirements.
+$#A Opening a File for Buffered File I/O
The multimedia file I/O manager provides several ways to set up and use buffered file I/O. The main distinction between these different approaches is whether the file I/O manager or the application allocates the buffer ¾ either the file I/O manager or the application can allocate the I/O buffer. Unless you want to access the I/O buffer directly or open a memory file, let the file I/O manager allocate the buffer. For more information about accessing an I/O buffer directly and using memory files, see Directly Accessing a File Input/Output Buffer_win32_Directly_Accessing_a_File_Input_Output_Buffer and Performing File Input/Output on Memory Files_win32_Performing_File_Input_Output_on_Memory_Files.
A buffer allocated by the file I/O manager is called an internal buffer. To open a file for buffered I/O using an internal buffer, specify the MMIO_ALLOCBUF flag with the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function when you open the file. Once a file is opened for buffered I/O, the buffer is essentially transparent to the application. You can read, write, and seek the same way as with unbuffered I/O.
+$#A I/O Buffer Control Functions
The multimedia file I/O services also include some functions that give you more control over the file I/O buffer. Using the following functions, you can force the contents of an I/O buffer to be written to disk, enable buffered I/O on a file opened for unbuffered I/O, change the size of an I/O buffer, and supply your own I/O buffer.
Function Description
mmioFlush!AL(`_win32_mmioFlush',3,`No_SDK') Writes the contents of the I/O buffer to disk.
mmioSetBuffer!AL(`_win32_mmioSetBuffer',3,`No_SDK') Changes the size of the I/O buffer, and allows applications to supply their own buffer.

+$#A Flushing an I/O Buffer
Flushing an I/O buffer means writing the contents of the buffer to disk. You don't have to call the mmioFlush!AL(`_win32_mmioFlush',3,`No_SDK') function to flush an I/O buffer ¾ the buffer is automatically flushed when you close a file using the mmioClose!AL(`_win32_mmioClose',3,`No_SDK') function. If you don't close a file immediately after writing to it, you flush the buffer to ensure the information is written to disk.

Note If you run out of disk space, mmioFlush may fail, even if the preceding mmioWrite!AL(`_win32_mmioWrite',3,`No_SDK') calls were successful. Similarly, mmioClose may fail when it is flushing its I/O buffer.

+$#A Changing the Size of the Internal I/O Buffer
The default size of the internal I/O buffer is 8K. If this size is not adequate, you can use the mmioSetBuffer!AL(`_win32_mmioSetBuffer',3,`No_SDK') function to change the size of the buffer. You can also use mmioSetBuffer to enable buffering on a file opened for unbuffered I/O.
The following example opens a file named SAMPLE.TXT for unbuffered I/O, and then enables buffered I/O with an internal 16K buffer.
HMMIO hFile;
.ifdef REF
.
.
.
.endif
if ((hFile = mmioOpen("SAMPLE.TXT", NULL, MMIO_READ)) != NULL) {
/* File opened successfully; request an I/O buffer */
if (mmioSetBuffer(hFile, NULL, 16384L, 0))
/* Buffer cannot be allocated. */
return FALSE;
else
/* Buffer allocated successfully. */
return TRUE;
}

else
/* File cannot be opened. */
return FALSE;

+$#A Supplying Your Own I/O Buffer
You can also use mmioSetBuffer!AL(`_win32_mmioSetBuffer',3,`No_SDK') to supply your own buffer for use as a memory file. For details on using memory files, see Performing File Input/Output on Memory Files_win32_Performing_File_Input_Output_on_Memory_Files.
+$#A Working With RIFF Files
The preferred format for multimedia files is the Resource Interchange File Format (RIFF). The RIFF format is a tagged-file structure.
The multimedia file I/O services provide the following functions to support file I/O to RIFF files.
Function Description
mmioAscend!AL(`_win32_mmioAscend',3,`No_SDK') Ascends out of a RIFF file chunk to the next chunk in the file.
mmioCreateChunk!AL(`_win32_mmioCreateChunk',3,`No_SDK') Creates a new chunk in a RIFF file.
mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK') Descends into a RIFF file chunk beginning at the current file position, or searches for a specified chunk.
mmioFOURCC!AL(`_win32_mmioFOURCC',3,`No_SDK') Converts four individual characters into a four-character code.
mmioStringToFOURCC!AL(`_win32_mmioStringToFOURCC',3,`No_SDK') Converts a null-terminated string into a four-character code.

These functions work with the basic buffered and unbuffered file I/O services ¾ you can open, read, and write RIFF files the same as other file types.
+$#A About RIFF Files
The basic building block of a RIFF file is called a chunk. Each chunk consists of the following fields:
· A four-character code specifying the chunk identifier
· A doubleword specifying the size of the data field in the chunk
· A data field

The only chunks allowed to contain other chunks (subchunks) are those with a chunk identifier of "RIFF" or "LIST". The first chunk in a RIFF file must be a "RIFF" chunk. All other chunks in the file are subchunks of the "RIFF" chunk.
+$#A "RIFF" Chunks
"RIFF" chunks include an additional field in the first four bytes of the data field. This additional field provides the form type of the field. The form type is a four-character code identifying the format of the data stored in the file. For example, Microsoft waveform audio files (WAVE files) have a form type of "WAVE". The following illustration shows a "RIFF" chunk containing two subchunks.
+$#A "LIST" Chunks
"LIST" chunks also include an additional field in the first four bytes of the data field. This additional field contains the list type of the field. The list type is a four-character code identifying the contents of the list. For example, a "LIST" chunk with a list type of "INFO" can contain "ICOP" and "ICRD" chunks providing copyright and creation date information. The following illustration shows a "RIFF" chunk containing a "LIST" chunk and one other subchunk (the "LIST" chunk contains two subchunks).
+$#A Generating Four-Character Codes
A four-character code is a 32-bit quantity representing a sequence of one to four ASCII alphanumeric characters, padded on the right with blank characters. The data type for a four-character code is FOURCC. Use the mmioFOURCC!AL(`_win32_mmioFOURCC',3,`No_SDK') function to convert four characters to a four-character code, as shown in the following example, which generates a four-character code for "WAVE":
FOURCC fourccID;
.ifdef REF
.
.
.
.endif
fourccID = mmioFOURCC('W', 'A', 'V', 'E');

To convert a null-terminated string into a four-character code, use the mmioStringToFOURCC!AL(`_win32_mmioStringToFOURCC',3,`No_SDK') function, as shown in the following example, which also generates a four-character code for "WAVE":
FOURCC fourccID;
.ifdef REF
.
.
.
.endif
fourccID = mmioStringToFOURCC("WAVE", 0);

The second parameter in mmioStringToFOURCC!AL(`_win32_mmioStringToFOURCC',3,`No_SDK') specifies options for converting the string to a four-character code. If you specify the MMIO_TOUPPER flag, mmioStringToFOURCC converts all alphabetic characters in the string to uppercase. This is useful when you need to specify a four-character code to identify a custom I/O procedure (four-character codes identifying file-extension names must be all uppercase).
+$#A Creating RIFF Chunks
To create a new chunk, use the mmioCreateChunk!AL(`_win32_mmioCreateChunk',3,`No_SDK') function to write a chunk header at the current position in an open file.
The following example creates a new chunk with a chunk ID of "RIFF" and a form type of "RDIB".
HMMIO hmmio;
MMCKINFO mmckinfo;
.ifdef REF
.
.
.
.endif
mmckinfo.fccType = mmioFOURCC('R', 'D', 'I', 'B');
mmioCreateChunk(hmmio, &mmckinfo, MMIO_CREATERIFF);

If you're creating a "RIFF" or "LIST" chunk, you must specify the form type in the fccType field of the MMCKINFO!AL(`_win32_MMCKINFO',3,`No_SDK') structure. In the previous example, the form type is "RDIB".
If you know the size of the data field in a new chunk, you can set the cksize member in the MMCKINFO structure when you create the chunk. This value will be written to the data size field in the new chunk. If this value is not correct when you call the mmioAscend!AL(`_win32_mmioAscend',3,`No_SDK') function to mark the end of the chunk, it will be rewritten automatically to reflect the correct size of the data field.
After you create a new chunk using mmioCreateChunk!AL(`_win32_mmioCreateChunk',3,`No_SDK'), the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). If the chunk is a "RIFF" or "LIST" chunk, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk).
+$#A Navigating RIFF Files
RIFF files consist of nested chunks of data. Multimedia file I/O services include two functions you can use to navigate between chunks in a RIFF file: mmioAscend!AL(`_win32_mmioAscend',3,`No_SDK') and mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK'). You might think of these functions as high-level seek functions. When you descend into a chunk, the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). For "RIFF" and "LIST" chunks, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk). When you ascend out of a chunk, the file position is set to the location following the end of the chunk.
+$#A Descending Into a Chunk
The mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK') function descends into a chunk or searches for a chunk, beginning at the current file position.
The mmioDescend function fills an MMCKINFO!AL(`_win32_MMCKINFO',3,`No_SDK') structure with information on the chunk. This information includes the chunk identifier, the size of the data field, and the form type, or list type if the chunk is a "RIFF" or "LIST" chunk.
+$#A Searching for a Chunk
To search for a chunk in an open RIFF file, specify the MMIO_FINDCHUNK flag in the wFlags parameter of mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK'). Set the ckid member of the MMCKINFO!AL(`_win32_MMCKINFO',3,`No_SDK') structure referenced by lpck to the four-character code of the chunk you want to search for.
If you are searching for a "RIFF" or "LIST" chunk, you don't need to set the ckid member of the MMCKINFO structure ¾ mmioDescend sets this member for you. Set the fccType member to the four-character code of the form type or list type of the chunk.
The following example searches for a "RIFF" chunk with a form type of "WAVE" to verify that the file that has just been opened is a WAVE waveform audio file.
HMMIO hmmio;
MMCKINFO mmckinfoParent;
MMCKINFO mmckinfoSubchunk;
.ifdef REF
.
.
.
.endif
/*
* Locate a "RIFF" chunk with a "WAVE" form type
* to make sure the file is a WAVE file.
*/
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent,
NULL, MMIO_FINDRIFF))
/* The file is not a WAVE file. */
return FALSE;
else
/* The file is a WAVE file. */
return TRUE;

If the chunk you are searching for is a subchunk enclosed by a parent chunk (as are all chunks other than "RIFF" chunks), you should identify the parent chunk with the lpckParent parameter. In this case, mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK') will only search within the specified parent chunk.
The following example searches for the "fmt " chunk in the "RIFF" chunk descended into by the previous example.
/*
* Find the format chunk (form type "fmt "); it should be
* a subchunk of the "RIFF" parent chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK)) {
/* Error, cannot find the "fmt " chunk. */
return FALSE;
}
else {
/* "fmt " chunk found. */
return TRUE;
}

If you do not specify a parent chunk, the current file position should be at the beginning of a chunk before you call mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK') to search for a chunk. If you do specify a parent chunk, the current file position can be anywhere in the parent chunk.
If the search for a subchunk fails, the current file position is undefined. You can use the mmioSeek!AL(`_win32_mmioSeek',3,`No_SDK') function and the dwDataOffset field of the MMCKINFO!AL(`_win32_MMCKINFO',3,`No_SDK') structure for the enclosing parent chunk to seek back to the beginning of the parent chunk, as in the following example:
mmioSeek(hmmio, mmckinfoParent.dwDataOffset + 4, SEEK_SET);

Since the dwDataOffset member specifies the offset to the beginning of the data portion of the chunk, you must seek four bytes past dwDataOffset to set the file position to be after the form type.
+$#A Ascending Out of a Chunk
After you descend into a chunk and read the data in the chunk, you can move the file position to the beginning of the next chunk by ascending out of the chunk by using the mmioAscend!AL(`_win32_mmioAscend',3,`No_SDK') function.
For example, the following statement ascends out of the "fmt " subchunk descended into by the previous example, illustrating searching for a subchunk:
/* Ascend out of the "fmt " subchunk.
*/
mmioAscend(hmmio, &mmckinfoSubchunk, 0);

+$#A Example of RIFF File I/O
The following example shows how to open a RIFF file for buffered I/O, as well as how to descend, ascend, and read RIFF chunks.
/* ReversePlay--Plays a WAVE waveform audio file backwards. */
void ReversePlay(void)
{
char szFileName[128]; /* filename of file to open */
HMMIO hmmio; /* file handle for open file */
MMCKINFO mmckinfoParent; /* parent chunk information structure */
MMCKINFO mmckinfoSubchunk; /* subchunk information structure */
DWORD dwFmtSize; /* size of "fmt" chunk */
DWORD dwDataSize; /* size of "data" chunk */
WAVEFORMAT *pFormat; /* pointer to memory for "fmt" chunk */
.ifdef REF
...
.endif

/* Get the filename from the edit control. */
.ifdef REF
...
.endif

/*
* Open the given file for reading with buffered I/O
* using the default internal buffer.
*/
hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);

if(hmmio == NULL){
Error("Failed to open file.");
return;
}

/*
* Locate a "RIFF" chunk with a "WAVE" form type
* to make sure the file is a WAVE file.
*/
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL,
MMIO_FINDRIFF)) {
Error("This is not a WAVE file.");
mmioClose(hmmio, 0);
return;
}

/*
* Find the "fmt " chunk (form type "fmt "); it must be
* a subchunk of the "RIFF" parent chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK)) {
Error("WAVE file has no \"fmt\" chunk");
mmioClose(hmmio, 0);
return;
}

/*
* Get the size of the "fmt " chunk--allocate and lock memory for it.
*/
dwFmtSize = mmckinfoSubchunk.cksize;
.ifdef REF
...
.endif
/* Read the "fmt " chunk. */
if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LRESULT)dwFmtSize){
Error("Failed to read format chunk.");
.ifdef REF
...
.endif
mmioClose(hmmio, 0);
return;
}

/* Ascend out of the "fmt " subchunk. */
mmioAscend(hmmio, &mmckinfoSubchunk, 0);

/*
* Find the data subchunk. The current file position
* should be at the beginning of the data chunk.
*/
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,
MMIO_FINDCHUNK)) {
Error("WAVE file has no data chunk.");
.ifdef REF
...
.endif
mmioClose(hmmio, 0);
return;
}

/* Get the size of the data subchunk. */
dwDataSize = mmckinfoSubchunk.cksize;
if (dwDataSize == 0L) {
Error("The data chunk contains no data.");
.ifdef REF
...
.endif
mmioClose(hmmio, 0);
return;
}

/* Open a waveform output device. */
.ifdef REF
...
.endif

/* Allocate and lock memory for the waveform data. */
.ifdef REF
...
.endif

/* Read the waveform data subchunk. */
if(mmioRead(hmmio, (HPSTR) lpData,
dwDataSize) != (LRESULT)dwDataSize) {
Error("Failed to read data chunk.");
.ifdef REF
...
.endif
mmioClose(hmmio, 0);
return;
}

/* Close the file. */
mmioClose(hmmio, 0);

/* Reverse the sound and play it. */
.ifdef REF
...
.endif
}

+$#A Directly Accessing a File Input/Output Buffer
Applications that are performance sensitive, such as applications that must stream data in real time from a CD-ROM, can optimize file I/O performance by accessing the file I/O buffer directly. Exercise care if you choose to do this ¾ by accessing the file I/O buffer directly, you bypass some of the safeguards and error checking provided by the file I/O manager.
The multimedia file I/O services provide the following functions to support direct I/O buffer access.
Function Description
mmioAdvance!AL(`_win32_mmioAdvance',3,`No_SDK') Fills and/or flushes the I/O buffer of a file set up for direct I/O buffer access.
mmioGetInfo!AL(`_win32_mmioGetInfo',3,`No_SDK') Retrieves information on the file I/O buffer of a file opened for buffered I/O.
mmioSetInfo!AL(`_win32_mmioSetInfo',3,`No_SDK') Changes information on the file I/O buffer of a file opened for buffered I/O.

To access a file I/O buffer directly, open the file for buffered I/O as described in "Performing Buffered File I/O." You can use the internal file I/O buffer or supply your own buffer by using the mmioSetBuffer!AL(`_win32_mmioSetBuffer',3,`No_SDK') function.
+$#A Getting Information on the File I/O Buffer
Use the mmioGetInfo!AL(`_win32_mmioGetInfo',3,`No_SDK') function to get information on a file I/O buffer, such as the buffer size and address.
+$#A Reading and Writing the File I/O Buffer
There are three members in the MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure used for reading and writing the file I/O buffer: pchNext, pchEndRead, and pchEndWrite. The pchNext field points to the next location in the buffer to read or write. You must increment pchNext as you read and write the buffer. The pchEndRead field identifies the last valid character you can read from the buffer. Likewise, pchEndWrite identifies the last location in the buffer you can write. To be precise, both pchEndRead and pchEndWrite point to the memory location following the last valid data in the buffer.
+$#A Advancing the File I/O Buffer
When you reach the end of the file I/O buffer, you must advance the buffer to fill it from the disk (if you are reading), and flush it to the disk (if you are writing). Use the mmioAdvance!AL(`_win32_mmioAdvance',3,`No_SDK') function to advance a file I/O buffer.
The mmioAdvance function advances the buffer by (pchNext - cchBuffer) bytes. All data in the buffer before pchNext is assumed to have been read already, while all data from pchNext to pchEndRead will be considered unread. So, when mmioAdvance is called, all data before pchNext will be overwritten. The buffer must not be treated as an array and pchNext must be incremented until pchNext equals pchEndRead.
The following illustrations show how the file I/O buffer is advanced as a file is read or written.
+$#A Using the mmioAdvance Function
To fill a file I/O buffer from disk, call mmioAdvance!AL(`_win32_mmioAdvance',3,`No_SDK') with the MMIO_READ flag. If there is not enough data remaining in the file to fill the buffer, the pchEndRead member in the MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure points to the location following the last valid byte in the buffer.
To flush a buffer to disk, first set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure. Then call mmioAdvance with the MMIO_WRITE flag.
+$#A Example of Accessing a File I/O Buffer
The following example is based on the ReversePlay example function discussed earlier in this section. In this example, direct-buffer access is used to read waveform data from a file.
HMMIO hmmio;
MMIOINFO mmioinfo;
DWORD dwDataSize;
DWORD dwCount;
HPSTR hptr;
.ifdef REF
.
.
.
.endif
/* Get information on the file I/O buffer. */

if (mmioGetInfo(hmmio, &mmioinfo, 0)) {
Error("Failed to get I/O buffer info");
mmioClose(hmmio, 0);
return FALSE;
}

/*
* Read the entire file by directly reading the file I/O buffer.
* When the end of the I/O buffer is reached, advance the buffer.
*/
for (dwCount = dwDataSize, hptr = lpData; dwCount > 0; dwCount--) {
/* Check to see if the I/O buffer must be advanced. */

if (mmioinfo.pchNext == mmioinfo.pchEndRead) {
if(mmioAdvance(hmmio, &mmioinfo, MMIO_READ)) {
Error("Failed to advance buffer.");
mmioClose(hmmio, 0);
return FALSE;
}
}

/* Get a character from the buffer. */

*hptr++ = *mmioinfo.pchNext++;
}

/* End direct buffer access and close the file. */

mmioSetInfo(hmmio, &mmioinfo, 0);
mmioClose(hmmio, 0);

+$#A Ending Direct Access of a File I/O Buffer
When you finish accessing a file I/O buffer, pass the MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure filled by the mmioGetInfo!AL(`_win32_mmioGetInfo',3,`No_SDK') function to the mmioSetInfo!AL(`_win32_mmioSetInfo',3,`No_SDK') function to stop direct-buffer access. If you wrote to the buffer, set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure before calling mmioSetInfo. Otherwise, the buffer will not be flushed to disk.
+$#A Performing File Input/Output on Memory Files
The multimedia file I/O services let you to treat a block of memory as a file. This can be useful if you already have a file image in memory. Memory files let you reduce the number of special-case conditions in your code because, for I/O purposes, you can treat file memory images as if they were disk-based files. You can also use memory files with the Clipboard.
+$#A Opening Memory Files
Like I/O buffers, memory files can use memory allocated by the application or by the file I/O manager. In addition, memory files can be either expandable or non-expandable. When the file I/O manager reaches the end of an expandable memory file, it expands the memory file by a predefined increment.
To open a memory file, use mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') with the szFileName parameter set to NULL and the MMIO_READWRITE flag set in the dwOpenFlags parameter. Set the lpmmioinfo parameter to point to an MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure as follows:
· Set the pIOProc member to NULL.
· Set the fccIOProc member to FOURCC_MEM.
· Set the pchBuffer member to point to the memory block. To request that the file I/O manager allocate the memory block, set pchBuffer to NULL.
· Set the cchBuffer member to the initial size of the memory block.
· Set the adwInfo[0] member to the minimum expansion size of the memory block. For a non-expandable memory file, set adwInfo[0] to NULL.
· Set all other members to zero.

+$#A Allocating Memory for Memory Files
There are no restrictions on allocating memory for use as a non-expandable memory file. You can use static memory or stack memory, or you can use locally allocated or globally allocated memory. For expandable memory files, you must use memory allocated using the GlobalAlloc_win32_GlobalAlloc function and locked using the GlobalLock_win32_GlobalLock function.
+$#A Using Custom Input/Output Procedures
Multimedia file I/O services use I/O procedures to handle the physical input and output associated with reading and writing to different types of storage systems, such as file-archival systems and database-storage systems. There are predefined I/O procedures for standard Microsoft® Windows® files and for memory files. In the future, there will be a predefined I/O procedure for accessing elements of compound files. Compound files consist of a number of individual files, called file elements, bound together in one physical file.
You can supply a custom I/O procedure for accessing a unique storage system such as a database or file archive. This I/O procedure is private to your application.
The multimedia file I/O services provide the following functions to support custom I/O procedures:
Function Description
mmioInstallIOProc!AL(`_win32_mmioInstallIOProc',3,`No_SDK') Installs, removes, or locates an I/O procedure.
mmioSendMessage!AL(`_win32_mmioSendMessage',3,`No_SDK') Sends a custom message to the I/O procedure associated with a file.

· Opening a file using a custom I/O procedure_win32_Opening_a_File_Using_a_Custom_I_O_Procedure
· Writing an I/O procedure_win32_Writing_an_I_O_Procedure
· Installing an I/O procedure_win32_Installing_an_I_O_Procedure

+$#A Opening a File Using a Custom I/O Procedure
Before learning about how to write an I/O procedure, it's helpful to understand how to use one. To open a file using a custom I/O procedure, use the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function as you would to open any other file. Use a plus sign (+) in the filename to separate the name of the physical file from the name of the element of the file you want to open. For example, the following statement opens a file element named "element" from a file named FILENAME.ARC:
mmioOpen("filename.arc+element", NULL, MMIO_READ);

When the file I/O manager encounters a plus sign in a filename, it looks at the preceding filename extension to determine which I/O procedure to associate with the file. In the previous example, the file I/O manager will attempt to use the I/O procedure associated with the .ARC filename extension. If no I/O procedure is installed, the mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK') function returns an error.
+$#A Writing an I/O Procedure
An I/O procedure is a message-processing function supplied by an application. An I/O procedure function has the following syntax:
LRESULT IOProc(lpmmioinfo, wMsg, lParam1, lParam2)
The lpmmioinfo parameter specifies a pointer to an MMIOINFO!AL(`_win32_MMIOINFO',3,`No_SDK') structure associated with the file being accessed.
The wMsg parameter is of type UINT and specifies the message being sent by the file I/O manager to the I/O procedure.
The lParam1 parameter is of type LPARAM and specifies 32 bits of message-specific information.
The lParam2 parameter is of type LPARAM and specifies 32 bits of message-specific information.
The return value is message-specific. If the I/O procedure does not recognize a message, it should return zero.
+$#A I/O Procedure Messages
I/O procedures must respond to the MMIOM_CLOSE, MMIOM_OPEN, MMIOM_READ, MMIOM_WRITE, MMIOM_SEEK, and MMIOM_WRITEFLUSH messages. Each of these messages has two parameters.
You can also create custom messages and send them to your I/O procedure using the mmioSendMessage function. If you define your own messages, be sure they are defined at or above the MMIOM_USER message. For example, the following example defines a message named MMIOM_MYMESSAGE:
#define MMIOM_MYMESSAGE MMIOM_USER + 0

+$#A Using the MMIOINFO Structure
In addition to processing messages, an I/O procedure must maintain the lDiskOffset member in the MMIOINFO structure referenced by the lpmmioinfo parameter. The lDiskOffset member must always contain the file offset to the location that the next MMIOM_READ or MMIOM_WRITE message will access. The offset is specified in bytes and is relative to the beginning of the file. The I/O procedure can use the adwInfo[] member to maintain any required state information. The I/O procedure should not modify any other members in the MMIOINFO structure.
+$#A Installing an I/O Procedure
Use mmioInstallIOProc to install, remove, or locate an I/O procedure.
For example, to install an I/O procedure associated with the filename extension ARC, use the following statement:
mmioInstallIOProc (mmioFOURCC('A', 'R', 'C', ' '),
(LPMMIOPROC)lpmmioproc, MMIO_INSTALLPROC);

Note Be sure to remove any I/O procedures you've installed before closing your application.

+$#A Installing an I/O Procedure Using MmioOpen
When you install an I/O procedure by using the mmioInstallIOProc function, the procedure remains installed until you remove it. The I/O procedure will be used for any file you open if the file has the appropriate filename extension. You can also temporarily install an I/O procedure by using the mmioOpen function. In this case, the I/O procedure is only used with a file opened by mmioOpen and is removed when the file is closed by using the mmioClose function.
To specify an I/O procedure when you open a file using mmioOpen, use the lpmmioinfo parameter to reference an MMIOINFO structure as follows:
· Set the fccIOProc member to NULL.
· Set the pIOProc member to the procedure-instance address of the I/O procedure.
· Set all other members to zero (unless you are opening a memory file, or directly reading or writing to the file I/O buffer).

+$#A File Input/Output Functions
Following are the functions used with file input/output.
mmioAdvance!AL(`_win32_mmioAdvance',3,`No_SDK')mmioAscend!AL(`_win32_mmioAscend',3,`No_SDK')mmioClose!AL(`_win32_mmioClose',3,`No_SDK')mmioCreateChunk!AL(`_win32_mmioCreateChunk',3,`No_SDK')mmioDescend!AL(`_win32_mmioDescend',3,`No_SDK')mmioFlush!AL(`_win32_mmioFlush',3,`No_SDK')mmioFOURCC!AL(`_win32_mmioFOURCC',3,`No_SDK')mmioGetInfo!AL(`_win32_mmioGetInfo',3,`No_SDK')mmioInstallIOProc!AL(`_win32_mmioInstallIOProc',3,`No_SDK')mmioOpen!AL(`_win32_mmioOpen',3,`No_SDK')MMIOProc_win32_MMIOProcmmioRead!AL(`_win32_mmioRead',3,`No_SDK')mmioRename!AL(`_win32_mmioRename',3,`No_SDK')mmioSeek!AL(`_win32_mmioSeek',3,`No_SDK')mmioSendMessage!AL(`_win32_mmioSendMessage',3,`No_SDK')mmioSetBuffer!AL(`_win32_mmioSetBuffer',3,`No_SDK')mmioSetInfo!AL(`_win32_mmioSetInfo',3,`No_SDK')mmioStringToFOURCC!AL(`_win32_mmioStringToFOURCC',3,`No_SDK')mmioWrite!AL(`_win32_mmioWrite',3,`No_SDK')mmsystemGetVersion_win32_mmsystemGetVersion
+$#A Introduction to Multimedia Services
Microsoft® Windows® provides developers with high-level and low-level services for developing multimedia applications that use the extended capabilities of a multimedia personal computer.
+$#A About Windows Multimedia Services
Windows provides the following multimedia services:
· A Media Control Interface (MCI) for controlling media devices.
· Extensible string-based and message-based interfaces for communicating with MCI device drivers.
· MCI device drivers for playing and recording waveform audio, playing Musical Instrument Digital Interface (MIDI) files, and playing compact disc audio from a CD-ROM drive.
· Low-level application programming interface (API) support for multimedia-related services.
· Low-level support for playing and recording audio with waveform and MIDI audio devices.
· Low-level support for precision timer services.
· Multimedia file input and output (I/O) services that provide buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible by means of custom I/O procedures.
· A MIDI Mapper supporting standard MIDI patch services. This allows MIDI files to be authored independently of users' MIDI synthesizer setups.
· Control Panel options that let users change display drivers, set up a screen saver, install multimedia device drivers, assign waveform sounds to system alerts, and configure the MIDI Mapper.

This overview covers the following topics:
· The software architecture_win32_Architecture_of_Windows_Multimedia_Services of the multimedia services
· The design philosophy_win32_Windows_Multimedia_Design_Philosophy and implementation_win32_Building_a_Multimedia_Application of the multimedia services
· Basic information about developing applications that use the multimedia services

+$#A Architecture of Windows Multimedia Services
Although the multimedia services are provided by a number of files, the overall architecture can be viewed as consisting of just three software modules:
· The WINMM library, which provides the MCI services and low-level multimedia support functions
· Multimedia device drivers, which provide communication between the low-level WINMM functions and multimedia devices, such as waveform, MIDI, and timer hardware
· Drivers for MCI, which provide high-level control of media devices

+$#A Windows Multimedia Design Philosophy
The architecture of the multimedia services is designed around the concepts of extensibility and device independence. Extensibility allows the software architecture to accommodate advances in technology easily, without changes to the architecture itself. Device independence makes it easy to develop multimedia applications that will run on a wide range of hardware providing different levels of multimedia support. Three design elements of the system software provide extensibility and device independence:
· A translation layer (WINMM) that isolates applications from device drivers and centralizes device-independent code
· Run-time linking that allows the translation layer to link to the drivers it needs
· A well-defined and consistent driver interface that minimizes special-case code and makes the installation and upgrade process easier

+$#A Building a Multimedia Application
Before writing a multimedia application, you should be familiar with programming in the Windows environment.
When using the multimedia services, you must include the MMSYSTEM.H header file in all source-code files that call multimedia functions. This header file depends on declarations made in the WINDOWS.H header file, so you must first include WINDOWS.H. The MMSYSTEM.H header file provides function prototypes as well as definitions of multimedia data types and constants.
In addition to linking the normal Windows libraries, you must also link to the WINMM.LIB import library when linking an application that uses multimedia functions.

+$#A Multimedia Timer and Joystick Services
This overview shows you how to add joystick input capabilities to your application and how to control event timing.
+$#A About Multimedia Timers
The joystick services_win32_Joystick_Services can receive control signals from up to two joysticks. Timer services_win32_Timer_Services provide improved timer resolution and accuracy. Intel®-based systems have an accuracy of up to 16 milliseconds and MIPS®-based systems have an accuracy of up to 10 milliseconds.
The functions and data structures associated with the joystick and timer-interrupt services are defined in MMSYSTEM.H and WINMM.LIB.
+$#A Timer Services
The multimedia timer services provided with the Microsoft® Win32® application programming interface (API) let applications schedule timed periodic or one-time asynchronous events. Unlike the timer services that post WM_TIMER_win32_WM_TIMER messages to a message queue, the multimedia timer services call a callback function.
+$#A Timer Data Types
The MMSYSTEM.H header file defines data types and function prototypes for timer functions. You must include this header file in any source-code module that uses timer services. MMSYSTEM.H defines the following new data types.
Type Description
MMTIME!AL(`_win32_MMTIME',3,`No_SDK') A structure for representing time in one of several formats.
TIMECAPS!AL(`_win32_TIMECAPS',3,`No_SDK') A structure for querying timer capabilities.

+$#A Using Timer Services
Timer services let an application request and receive timer messages at application-specified intervals. Real-time multimedia applications can use the following functions to control the pace of data and synchronized presentations.
Function Description
timeBeginPeriod!AL(`_win32_timeBeginPeriod',3,`No_SDK') Establishes the minimum timer resolution an application will use.
timeEndPeriod!AL(`_win32_timeEndPeriod',3,`No_SDK') Clears a minimum timer resolution previously set using timeBeginPeriod!AL(`_win32_timeBeginPeriod',3,`No_SDK').
timeGetDevCaps!AL(`_win32_timeGetDevCaps',3,`No_SDK') Returns information about the capabilities of the timer services.
timeGetTime!AL(`_win32_timeGetTime',3,`No_SDK') Returns the system time in milliseconds.
timeGetSystemTime!AL(`_win32_timeGetSystemTime',3,`No_SDK') Fills an MMTIME!AL(`_win32_MMTIME',3,`No_SDK') structure with the system time in milliseconds.
timeSetEvent!AL(`_win32_timeSetEvent',3,`No_SDK') Creates a timer event that executes a specific action at a specific time or at periodic intervals.
timeKillEvent!AL(`_win32_timeKillEvent',3,`No_SDK') Cancels a timer event previously created using timeSetEvent!AL(`_win32_timeSetEvent',3,`No_SDK').

+$#A Getting the System Time
An application can get the current system time using the timeGetTime!AL(`_win32_timeGetTime',3,`No_SDK') or timeGetSystemTime!AL(`_win32_timeGetSystemTime',3,`No_SDK') function. The system time is the count of milliseconds since the Microsoft® Windows® operating system was started. The timeGetTime function returns the system time, and the timeGetSystemTime function fills an MMTIME!AL(`_win32_MMTIME',3,`No_SDK') structure with the system time.
+$#A Determining Maximum and Minimum Event Periods
You can use the timeGetDevCaps!AL(`_win32_timeGetDevCaps',3,`No_SDK') function to determine the minimum and maximum timer-event periods provided by the timer services. These values vary across computers and can vary depending on the mode in which Windows is running.
+$#A Establishing Minimum Timer Resolution
Before starting timer events, your application must establish the minimum timer resolution that it intends to use. It must clear this value after finishing with the timer-event services.
Use the timeBeginPeriod!AL(`_win32_timeBeginPeriod',3,`No_SDK') and timeEndPeriod!AL(`_win32_timeEndPeriod',3,`No_SDK') functions to set and clear the minimum timer-event resolution for your application. You must match a call to timeBeginPeriod with a corresponding call to timeEndPeriod, specifying the same minimum resolution in both calls. An application can make multiple timeBeginPeriod calls, as long as each call is matched with a call to timeEndPeriod.
+$#A Starting Timer Events
To initialize and start timer events, use the timeSetEvent!AL(`_win32_timeSetEvent',3,`No_SDK') function. This function returns a timer identifier that can be used to stop or identify timer events.
The timeSetEvent function returns a timer identifier if successful or NULL if unsuccessful. Interrupt timers are a scarce resource, and periodic timers with resolution less than 100 milliseconds consume a significant portion of CPU time. For periodic timers, you must pair calls to timeSetEvent with calls to timeKillEvent!AL(`_win32_timeKillEvent',3,`No_SDK'). For more information, see Canceling a Timer Event!AL(`_win32_Canceling_a_Timer_Event',3,`No_SDK').
The lpFunction parameter contains the address of the function to be called when the timer event takes place.
Since the callback function is accessed at interrupt time, it must adhere to strict programming guidelines. Timer-callback functions follow the same programming guidelines as callback functions for the low-level audio services. For information about writing low-level audio callback functions, see Low-Level Audio_win32_Low_Level_Audio. For more information about timer callback functions, see TimeProc!AL(`_win32_TimeProc',3,`No_SDK').
+$#A Canceling a Multimedia Timer Event
You can cancel an active timer event at any time. Be sure to cancel any outstanding timers before freeing the module containing the callback function. To cancel a timer event, use the timeKillEvent!AL(`_win32_timeKillEvent',3,`No_SDK') function.
+$#A Using Timer Callbacks
This topic describes how an application can use the timer services. First, the application calls the timeGetDevCaps!AL(`_win32_timeGetDevCaps',3,`No_SDK') function to determine the minimum and maximum resolution supported by the timer services. Before setting up any timer events, the application uses the timeBeginPeriod!AL(`_win32_timeBeginPeriod',3,`No_SDK') function to establish the minimum timer resolution it will use, as shown in the following example:
#define TARGET_RESOLUTION 1 /* Try for 1-millisecond accuracy. */

TIMECAPS tc;
UINT wTimerRes;

if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
/* Error; application can't continue. */
}

wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION),
tc.wPeriodMax);

timeBeginPeriod(wTimerRes);

To start the timer event, the application specifies the amount of time before the callback occurs, the required resolution, the address of the callback function, and user data to supply with the callback. The application can use a function like the following to start a one-time timer event.
UINT SetTimerCallback(NPSEQ npSeq, /* Sequencer data */
UINT msInterval) /* Event interval */
{
npSeq->wTimerID = timeSetEvent(
msInterval, /* Delay */
wTimerRes, /* Resolution (global variable) */
(LPTIMECALLBACK) OneShotTimer, /* Callback function */
(DWORD) npSeq, /* User data */
TIME_ONESHOT); /* Event type (one-time) */

if (npSeq->wTimerID != 0)
return TIMERR_NOCANDO;
else
return TIMERR_NOERROR;
}

The following callback function is used for the one-shot timer.
void PASCAL OneShotTimer(UINT wTimerID, UINT msg,
DWORD dwUser, DWORD dw1, DWORD dw2)
{
NPSEQ npSeq; /* Pointer to sequencer data */

npSeq = (NPSEQ) dwUser;
npSeq->wTimerID = 0; /* Invalidate unused timer id */

TimerIntRoutine(npSeq); /* Handle interrupt-time tasks */

UNREFERENCED_PARAMETER(dw1);
UNREFERENCED_PARAMETER(dw2);
UNREFERENCED_PARAMETER(msg);
UNREFERENCED_PARAMETER(wTimerID);
}

Before freeing the dynamic-link library (DLL) that contains the callback function, the application cancels any outstanding timers. To cancel one timer event, it can call the following function.
void DestroyTimer(NPSEQ npSeq)
{
if (npSeq->wTimerID) { /* If timer event is pending */
timeKillEvent(npSeq->wTimerID); /* Cancel the event */
npSeq->wTimerID = 0;
}
}

Finally, to cancel the minimum timer resolution it established, the application calls timeEndPeriod!AL(`_win32_timeEndPeriod',3,`No_SDK') as follows.
timeEndPeriod(wTimerRes);

+$#A Joystick Services
The joystick is an input device that provides position information. It is an additional supported input device, not a replacement for the mouse. All absolute-position devices, including touch screens, digitizing tablets, and light pens, can use the joystick services to provide position and button information to applications.
The joystick services are loaded when Windows is started. The joystick services can monitor two joysticks, each with two- or three-axis movement, and up to four buttons. Applications access the joystick services through the set of functions described in this section.

Note The driver for the IBM® Game Adapter supports two 2-axis joysticks or one 3-axis joystick.

+$#A Joystick Data Types
The MMSYSTEM.H header file defines new data types and function prototypes for joystick functions. MMSYSTEM.H defines the following new data types.
Type Description
JOYCAPS!AL(`_win32_JOYCAPS',3,`No_SDK') A structure that contains joystick capability information.
JOYINFO!AL(`_win32_JOYINFO',3,`No_SDK') A structure that contains joystick position and button information.

+$#A Using Joystick Services
Joystick services include functions to query each joystick for its capabilities, to poll each joystick for position and button information, and to receive messages in response to joystick events. Your application can use the following joystick functions to accept input from one or two joysticks.
Function Description
joyGetNumDevs!AL(`_win32_joyGetNumDevs',3,`No_SDK') Returns the number of joysticks supported by the joystick services.
joyGetDevCaps!AL(`_win32_joyGetDevCaps',3,`No_SDK') Returns joystick capabilities.
joyGetPos!AL(`_win32_joyGetPos',3,`No_SDK') Returns joystick position and button information.
joySetCapture!AL(`_win32_joySetCapture',3,`No_SDK') Causes joystick input to be sent to a specified window at regular intervals or when the joystick state changes.
joyReleaseCapture!AL(`_win32_joyReleaseCapture',3,`No_SDK') Releases the joystick captured using joySetCapture!AL(`_win32_joySetCapture',3,`No_SDK').
joyGetThreshold!AL(`_win32_joyGetThreshold',3,`No_SDK') Returns the movement threshold of a joystick.
joySetThreshold!AL(`_win32_joySetThreshold',3,`No_SDK') Sets the movement threshold of a joystick.

+$#A Determining Joystick Capabilities
The various joysticks in use today can support two or three axes and a variety of button configurations. Also, joysticks support different ranges of motion and polling frequencies. Joystick drivers can support either one or two joysticks. Two functions allow you to determine the capabilities of the joystick services and joystick devices installed on a system.
+$#A Getting the Joystick Driver Capabilities
You can use the joyGetNumDevs!AL(`_win32_joyGetNumDevs',3,`No_SDK') function to determine the number of joystick devices supported by the joystick services. This function returns the number of joysticks, or zero if there is no joystick support. The value returned is not necessarily the number of joysticks attached to the system. To determine whether a joysticf supportek is attached, call the joyGetPos!AL(`_win32_joyGetPos',3,`No_SDK') function for the device. The function returns JOYERR_UNPLUGGED if the specified device is disconnected.
The following example determines whether the joystick services are available and then determines if a joystick is attached to one of the ports:
JOYINFO joyinfo;
UINT wNumDevs, wDeviceID;
BOOL bDev1Attached, bDev2Attached;

if((wNumDevs = joyGetNumDevs()) == 0)
return ERR_NODRIVER;

bDev1Attached =
joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED;

bDev2Attached =
wNumDevs == 2 &&
joyGetPos(JOYSTICKID2,&joyinfo) != JOYERR_UNPLUGGED;

/* Decide which joystick to use */
if(bDev1Attached || bDev2Attached)
wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2;
else
return ERR_NODEVICE;

+$#A Getting the Joystick Capabilities
You can use the joyGetDevCaps!AL(`_win32_joyGetDevCaps',3,`No_SDK') function to obtain the specific capabilities of each joystick attached to a given system. This function fills a JOYCAPS!AL(`_win32_JOYCAPS',3,`No_SDK') structure with the range of each axis on the joystick, the number of buttons, and the maximum and minimum polling frequency.
+$#A Methods for Checking Joystick Status
An application can receive information from the joystick in one of two ways:
· By processing joystick messages from a captured joystick
· By polling the joystick directly

The message-processing method can be simpler to use; your application is sent messages that indicate the position of the stick and the state of the buttons (pressed or released).
+$#A Capturing Joystick Messages to a Window Function
You can capture joystick input to a window function; your application then receives joystick messages at specified intervals or when the user manipulates the joystick. The messages are described in the following topic.
Only one application can capture joystick messages from a given joystick. Capturing joystick messages does not, however, prevent your application (or other applications) from polling the joystick using joyGetPos!AL(`_win32_joyGetPos',3,`No_SDK'). If joyGetPos is called while joystick input is captured, joystick events occurring close to the time of the joyGetPos call may not be accurately reported to the capture window.
The joySetCapture!AL(`_win32_joySetCapture',3,`No_SDK') function captures joystick input to a window function you specify. To release the joystick, call the joyReleaseCapture!AL(`_win32_joyReleaseCapture',3,`No_SDK') function.
To capture messages from two joysticks attached to the system, you must call joySetCapture!AL(`_win32_joySetCapture',3,`No_SDK') twice, once for each joystick. Your window then receives separate messages for each device.
You cannot capture an unplugged joystick. The joySetCapture function returns zero if successful; it returns JOYERR_UNPLUGGED if the specified device is unplugged.

Note The joystick services set up a Windows timer event with each call to joySetCapture.

Set the period parameter for joySetCapture to a value that falls within the minimum and maximum resolution range for the joystick. To determine the minimum and maximum resolution of the joystick, call the joyGetDevCaps!AL(`_win32_joyGetDevCaps',3,`No_SDK') function, which fills the wPeriodMin and wPeriodMax members in the JOYCAPS!AL(`_win32_JOYCAPS',3,`No_SDK') structure.
If the specified period value is outside the range of valid resolution values for the joystick, the joystick services use the minimum or maximum resolution value, whichever is closer to the specified period.
+$#A Processing Multimedia Timer Joystick Messages
The following joystick messages can be sent to a window function. Numerals 1 and 2 in these messages correspond to the joystick initiating the message. MM_JOY1 messages are sent to the window function if your application requests input from the first joystick, and MM_JOY2 messages are sent if your application requests input from the second joystick. All messages report nonexistent buttons as released.
Messages Description
MM_JOY1MOVE!AL(`_win32_MM_JOY1MOVE',3,`No_SDK'), MM_JOY2MOVE!AL(`_win32_MM_JOY2MOVE',3,`No_SDK') Report a change in the x-axis and/or y-axis position of the joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1ZMOVE!AL(`_win32_MM_JOY1ZMOVE',3,`No_SDK'), MM_JOY2ZMOVE!AL(`_win32_MM_JOY2ZMOVE',3,`No_SDK') Report a change in the z-axis position of a 3-axis joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1BUTTONUP!AL(`_win32_MM_JOY1BUTTONUP',3,`No_SDK'), MM_JOY2BUTTONUP!AL(`_win32_MM_JOY2BUTTONUP',3,`No_SDK'), MM_JOY1BUTTONDOWN!AL(`_win32_MM_JOY1BUTTONDOWN',3,`No_SDK'), MM_JOY2BUTTONDOWN!AL(`_win32_MM_JOY2BUTTONDOWN',3,`No_SDK') Report that a joystick button has been pressed or released. The wParam parameter contains one JOY_BUTTONCHG bit flag specifying which button changed state and a combination of JOY_BUTTON bit flags specifying the current button states. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.

The joystick services use the following bit flags, passed in the wParam parameter of the window function, to report the state of the joystick buttons.
Flag Description
JOY_BUTTON1 Set when button 1 is pressed.
JOY_BUTTON1CHG Set when button 1 has changed state.
JOY_BUTTON2 Set when button 2 is pressed.
JOY_BUTTON2CHG Set when button 2 has changed state.
JOY_BUTTON3 Set when button 3 is pressed.
JOY_BUTTON3CHG Set when button 3 has changed state.
JOY_BUTTON4 Set when button 4 is pressed.
JOY_BUTTON4CHG Set when button 4 has changed state.

The MM_JOY*MOVE messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick.
The MM_JOY*BUTTONUP and MM_JOY*BUTTONDOWN messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick. Also, they use JOY_BUTTONCHG flags to indicate which button changed state, thereby generating the message.
For example, if the user presses and holds buttons 1 and 2 and then moves the stick, a window function may receive the following messages.
Message wParam Flags
MM_JOY1BUTTONDOWN JOY_BUTTON1 | JOY_BUTTON1CHG
MM_JOY1BUTTONDOWN JOY_BUTTON1 | JOY_BUTTON2 | JOY_BUTTON2CHG
MM_JOY1MOVE JOY_BUTTON1 | JOY_BUTTON2

A window function may receive the following messages when the user presses and releases button 3 without moving the stick.
Message wParam Flags
MM_JOY1BUTTONDOWN JOY_BUTTON3 | JOY_BUTTON3CHG
MM_JOY1BUTTONUP JOY_BUTTON3CHG

+$#A Releasing the Joystick
When your application no longer needs to receive periodic joystick messages, it should release the joystick by using the joyReleaseCapture!AL(`_win32_joyReleaseCapture',3,`No_SDK') function. If your application does not release the joystick before ending, the joystick is released shortly after the capture window is destroyed.
+$#A Setting the Movement Threshold
You can change the movement threshold of the joystick by calling the joySetThreshold!AL(`_win32_joySetThreshold',3,`No_SDK') function. The movement threshold is the number of device units that the stick must be moved before an MM_JOY*MOVE message is sent to the window that has captured the device.
+$#A Polling the Joystick
You can poll the joystick for position and button information. For example, an application may poll the joystick to get baseline position values; the Joystick Control Panel application uses this technique when calibrating the joystick. The joyGetPos!AL(`_win32_joyGetPos',3,`No_SDK') function allows you to poll the joystick for position and button information.

Note Calling joyGetPos while joystick input is captured can prevent the joystick services from accurately reporting joystick events to the capture window.

+$#A Using Joystick Messages
The remainder of this topic presents examples from a simple joystick game that performs the useful function of shooting holes in the desktop: it gets position and button-state information from the joystick services and, when a user presses the joystick buttons, plays waveform resources and paints bullet holes on the screen.
Most of the joystick-control code is in the main window function. In the following WM_CREATE case of the message handler, the application captures input from joystick 1.
case WM_CREATE:
if(joySetCapture(hWnd, JOYSTICKID1, NULL, FALSE)) {
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(hWnd, "Couldn't capture the joystick.", NULL,
MB_OK | MB_ICONEXCLAMATION);
PostMessage(hWnd,WM_CLOSE,0,0L);
}
break;

In response to the MM_JOY1MOVE!AL(`_win32_MM_JOY1MOVE',3,`No_SDK') messages, the application changes the position of the cursor and, if either button is pressed, draws a hole in the desktop.
case MM_JOY1MOVE :
if((UINT) wParam & (JOY_BUTTON1 | JOY_BUTTON2))
DrawFire(hWnd);
DrawSight(lParam); /* Calculate new cursor position */
break;

In response to the MM_JOY1BUTTONDOWN!AL(`_win32_MM_JOY1BUTTONDOWN',3,`No_SDK') messages, the application uses sndPlaySound!AL(`_win32_sndPlaySound',3,`No_SDK') to play a waveform audio file.
case MM_JOY1BUTTONDOWN :
if((UINT) wParam & JOY_BUTTON1) {
sndPlaySound(lpButton1,
SND_LOOP | SND_ASYNC | SND_MEMORY);
DrawFire(hWnd);
}
else if(wParam & JOY_BUTTON2) {
sndPlaySound(lpButton2,
SND_ASYNC | SND_MEMORY | SND_LOOP);
DrawFire(hWnd);
}
break;

By specifying the SND_LOOP and SND_ASYNC flags with sndPlaySound, the JOYTOY application repeats the waveform playback until the button is released.
When a button is released, the window function receives a MM_JOY1BUTTONUP!AL(`_win32_MM_JOY1BUTTONUP',3,`No_SDK') message, and handles it with the following statement.
case MM_JOY1BUTTONUP:
sndPlaySound(NULL, 0);
break;

This sequence stops the waveform-audio playback.
+$#A Multimedia Timer and Joystick Functions
Following are the functions used with joystick services.
joyGetDevCaps!AL(`_win32_joyGetDevCaps',3,`No_SDK')joyGetNumDevs!AL(`_win32_joyGetNumDevs',3,`No_SDK')joyGetPos!AL(`_win32_joyGetPos',3,`No_SDK')joyGetThreshold!AL(`_win32_joyGetThreshold',3,`No_SDK')joyReleaseCapture!AL(`_win32_joyReleaseCapture',3,`No_SDK')joySetCapture!AL(`_win32_joySetCapture',3,`No_SDK')joySetThreshold!AL(`_win32_joySetThreshold',3,`No_SDK')
Following are the functions used with timer services.
timeBeginPeriod!AL(`_win32_timeBeginPeriod',3,`No_SDK')timeEndPeriod!AL(`_win32_timeEndPeriod',3,`No_SDK')timeGetDevCaps!AL(`_win32_timeGetDevCaps',3,`No_SDK')timeGetSystemTime!AL(`_win32_timeGetSystemTime',3,`No_SDK')timeGetTime!AL(`_win32_timeGetTime',3,`No_SDK')timeKillEvent!AL(`_win32_timeKillEvent',3,`No_SDK')TimeProc!AL(`_win32_TimeProc',3,`No_SDK')timeSetEvent!AL(`_win32_timeSetEvent',3,`No_SDK')


Atoms.rtf

BONJOUR

Copyright © 2014 by J2M for Micro Programmation Informatique


WINDOW Main.rtf

Legal Information

Information in this online help system is subject to change without notice and does not represent a commitment on the part of Microsoft Corporation. The software and/or files described in this online help system are furnished under a license agreement or nondisclosure agreement. The software and/or files may be used or copied only in accordance with the terms of the agreement. The purchaser may make one copy of the software for backup purposes. No part of this online help system may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or information and retrieval systems, for any purpose other than the purchaser's personal use, without the written permission of Microsoft Corporation.
Microsoft may have patents or pending patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property rights except as expressly provided in any written license agreement from Microsoft.
Copyright © 1992-1995 Microsoft Corporation. All rights reserved.
Microsoft, MS, MS-DOS, Win32, Win32s, and Windows are registered trademarks, and Windows NT is a trademark of Microsoft Corporation.
Portions of this documentation are provided under license from Digital Equipment Corporation. Copyright © 1990, 1992 Digital Equipment Corporation. All rights reserved.
DEC, DECnet and PATHWORKS are trademarks of Digital Equipment Corporation.


Windows.rtf

+$#AK Windows

A window in an application written for the Microsoft® Windows® operating system is a rectangular area of the screen where the application displays output and receives input from the user. A window shares the screen with other windows, including those from other applications. Only one window at a time can receive input from the user. The user can use the mouse, keyboard, or other input device to interact with this window and the application that owns it.

+$#A About Windows

Windows are the only means a Windows-based application has to interact with the user and accomplish tasks, so one of the first tasks of a Windows-based application is to create a window. This overview describes the elements of the Microsoft® Win32® application programming interface (API) that applications use to create and use windows; manage relationships between windows; and size, move, and display windows.

+$#A Desktop Window

When Windows starts, it automatically creates the desktop window. The desktop window is a system-defined window that paints the background of the screen and serves as the base for all windows displayed by all Win32-based applications.

The desktop window uses a bitmap stored in a bitmap file (with the .BMP filename extension) to paint the background of the screen. The pattern created by the bitmap is called the desktop wallpaper. By default, the desktop window uses the bitmap from a file specified in the registry as the desktop wallpaper.

A system configuration application, such as a Control Panel application, can change the desktop wallpaper by using the SystemParametersInfo function with the wAction parameter set to SPI_SETDESKWALLPAPER and the lpvParam parameter specifying a bitmap filename. SystemParametersInfo then loads the bitmap from the specified file, uses the bitmap to paint the background of the screen, and enters the new filename in the registry. For more information about bitmaps, see Bitmaps. For more information about the registry, see Registry.

+$#A Application Windows

Every Win32-based application creates at least one window, called the main window, that serves as the main window for the application. This window serves as the primary interface between the user and the application. Most applications also create many other windows, either directly or indirectly, to perform tasks related to the main window. Each window plays a part in displaying output and receiving input from the user.

+$#A Components of an Application Window

An application window can include a title bar, a menu bar, a System menu (also called the Control menu), a minimize box, a maximize box, a sizing border, a client area, a horizontal scroll bar, and a vertical scroll bar. An application's main window typically includes all of these components. The following illustration shows these components in a typical main window.

{ewc msdncd, EWGraphic, group10814 0 /a "SDK_02.bmp"}

The title bar displays an application-defined icon and line of text; typically, the text specifies the name of the application or indicates the purpose of the window. An application specifies the icon and text when creating the window. The title bar also makes it possible for the user to move the window by using a mouse or other pointing device.

Most applications include a menu that lists the commands supported by the application. Items in the menu bar represent the main categories of commands. Choosing an item from the menu bar typically opens a pop-up menu whose items correspond to the tasks within a given category. By selecting a command, the user directs the application to carry out a task.

The System-menu box is a bitmap that, when clicked, displays the System menu. The System menu is a menu created and managed by Windows. It contains a standard set of menu items that, when chosen by the user, set a window's size or position, close the application, or open the Task List. For more information about menus and the System menu, see Menus (Windows NT).

The maximize and minimize boxes are bitmaps that, when clicked, affect a window's size and position. When the user clicks the maximize box, Windows enlarges the window to the size of the screen and positions the window, so it covers the entire screen. At the same time, Windows replaces the maximize box with the restore box. The restore box is a bitmap that, when clicked, restores the window to its previous size and position.

When the user clicks the minimize box, Windows reduces the window to the size of an icon, positions the window at the bottom of the screen, and displays the window's icon at the position of the window. An icon is a 32- by 32-pixel bitmap that represents a window. For more information about icons, see Icons.

The sizing border is an area around the perimeter of the window that enables the user to size the window by using a mouse or other pointing device.

The client area is the part of a window where the application displays output, such as text or graphics. For example, a desktop publishing application displays the current page of a document in the client area. The application must provide a function, called a window procedure, to process input to the window and display output in the client area. For more information about window procedures, see Window Procedures.

The horizontal and vertical scroll bars convert mouse or keyboard input into values that an application uses to shift the contents of the client area either horizontally or vertically. For example, a word-processing application that displays a lengthy document typically provides a vertical scroll bar to enable the user to page up and down through the document.

The title bar, menu bar, System menu, minimize and maximize boxes, sizing border, and scroll bars are referred to collectively as the window's nonclient area. Windows manages most aspects of the nonclient area; the application manages everything else about the window. In particular, the application manages the appearance and behavior of the client area.

+$#A Controls, Dialog Boxes, and Message Boxes

An application uses several types of windows in addition to its main window, including controls, dialog boxes, and message boxes.

A control is a window that an application uses to obtain a specific piece of information from the user, such as the name of a file to open or the desired point size of a text selection. Applications also use controls to obtain information needed to control a particular feature of an application. For example, a word-processing application typically provides a control to let the user turn wordwrapping on and off.

Controls are always used in conjunction with another window ⎯ typically, a dialog box. A dialog box is a window that contains one or more controls. An application uses a dialog box to prompt the user for input needed to complete a command. For example, an application that includes a command to open a file would display a dialog box that includes controls in which the user specifies a path and filename.

A message box is a window that displays a note, caution, or warning to the user. For example, a message box can inform the user of a problem the application has encountered while performing a task.

Dialog boxes and message boxes do not typically use the same set of window components as does a main window. Most have a title bar, a System menu, a border (nonsizing), and a client area, but they typically do not have a menu bar, minimize and maximize buttons, or scroll bars. For more information about dialog boxes and message boxes, see Dialog Boxes.

+$#A Window Creation

An application creates its main window by using the CreateWindow or CreateWindowEx function and providing information Windows requires to define the attributes of the window. CreateWindowEx has a parameter, dwExStyle, that CreateWindow does not have; otherwise, the functions are identical. In fact, CreateWindow simply calls CreateWindowEx, setting the dwExStyle parameter to zero. For this reason, the remainder of this topic refers only to CreateWindowEx.

The Win32 API provides additional functions ⎯ including DialogBox, CreateDialog, and MessageBox ⎯ for creating special-purpose windows such as dialog boxes and message boxes. For more information about these functions, see Dialog Boxes.

+$#A Window Attributes

An application must provide the following information when creating a window:

• Window class

• Window name

• Window style

• Parent or owner window

• Size

• Location

• Position

• Child-window identifier or menu handle

• Instance handle

• Creation data

These attributes are described in the following sections.

Window Class

Every window belongs to a window class. An application must register a window class before creating any windows of that class. The window class defines most aspects of a window's appearance and behavior. The chief component of a window class is the window procedure, a function that receives and processes all input and requests sent to the window. Windows provides the input and requests in the form of messages. For more information about window classes, window procedures, or messages, see Window Classes, Window Procedures, or Messages and Message Queues.

Window Name

A window can have a name. A window name (also called window text) is a text string that identifies a window for the user. A main window, dialog box, or message box typically displays its window name in its title bar, if present. For a control, the appearance of the window name depends on the control's class. A button, edit control, or static control displays its window name within the rectangle occupied by the control. A list box, combo box, or static control does not display its window name.

An application uses the SetWindowText function to change the window name after creating the window. It uses the GetWindowTextLength and GetWindowText functions to retrieve the current window-name text from a window.

Window Style

Every window has one or more window styles. A window style is a named constant that defines an aspect of the window's appearance and behavior that is not specified by the window's class. For example, the SCROLLBAR class creates a scroll bar control, but the SBS_HORZ and SBS_VERT styles determine whether a horizontal or vertical scroll bar control is created. A few window styles apply to all windows, but most apply to windows of specific window classes. Windows and, to some extent, the window procedure for the class, interpret the styles.

Parent or Owner Window

A window can have a parent window. A window that has a parent is called a child window. The parent window provides the coordinate system used for positioning a child window. Having a parent window affects aspects of a window's appearance; for example, a child window is clipped so that no part of the child window can appear outside the borders of its parent window. A window that has no parent, or whose parent is the desktop window, is called a top-level window. An application uses the EnumWindows function to obtain the handle of each of its top-level windows. EnumWindows passes the handle of each top-level window, in turn, to an application-defined callback function (a function called by Windows).

A window can own, or be owned by, another window. An owned window always appears in front of its owner window, is hidden when its owner window is minimized, and is destroyed when its owner window is destroyed.

Location, Size, and Position in the Z Order

Every window has a location, size, and position in the Z order. The location is the coordinates of the window's upper left corner, relative to the upper left corner of the screen or, in the case of a child window, the upper left corner of the parent's client area. A window's size is its width and height measured in pixels. A window's position in the Z order is the position of the window in a stack of overlapping windows.

Child-Window Identifier or Menu Handle

A child window can have a child-window identifier, a unique, application-defined value associated with the child window. Child-window identifiers are especially useful in applications that create multiple child windows. When creating a child window, an application specifies the identifier of the child window. After creating the window, the application can change the window's identifier by using the SetWindowLong function, or it can retrieve the identifier by using the GetWindowLong function.

Every window, except a child window, can have a menu. An application can include a menu by providing a menu handle either when registering the window's class or when creating the window.

Instance Handle

Every Win32-based application has an instance handle associated with it. Windows provides the instance handle to an application when the application starts. Because it can run multiple copies of the same application, Windows uses instance handles internally to distinguish one instance of an application from another. The application must specify the instance handle in many different windows, including those that create windows.

Creation Data

Every window can have application-defined creation data associated with it. When the window is first created, Windows passes a pointer to the data on to the window procedure of the window being created. The window procedure uses the data to initialize application-defined variables.

+$#A Main Window Creation

Every Win32-based application must have a WinMain function as its entry point. WinMain performs a number of tasks, including registering the window class for the main window and creating the main window. WinMain registers the main window class by calling the RegisterClass function, and it creates the main window by calling the CreateWindowEx function.

Portability Issue The entry point need not be named WinMain.

Windows does not automatically display the main window after creating it; instead, an application must use the ShowWindow function to display the main window. After creating the main window, the application's WinMain function calls ShowWindow, passing it two parameters: the handle of the main window and a flag specifying whether the main window should be minimized or maximized when it is first displayed. Normally, the flag can be set to any of the constants beginning with the SW_ prefix that are defined in the Win32 header files. However, when ShowWindow is called to display the application's main window, the flag must be set to SW_SHOWDEFAULT. This flag tells Windows to display the window as directed by the program that launched the application.

+$#A Window-Creation Messages

When creating any window, Windows sends messages to the window procedure for the window. Windows sends the WM_NCCREATE message after creating the window's nonclient area and the WM_CREATE message after creating the client area. The window procedure receives both messages before Windows displays the window. Both messages include a pointer to a CREATESTRUCT structure that contains all the information specified in the CreateWindowEx function. Typically, the window procedure performs initialization tasks upon receiving these messages.

When creating a child window, Windows sends the WM_PARENTNOTIFY message to the parent window after sending the WM_NCCREATE and WM_CREATE messages. It also sends other messages while creating a window. The number and order of these messages depend on the window class and style and on the function used to create the window. These messages are described in other topics in this help file.

+$#A Multithread Applications

A Win32-based application can have multiple threads of execution, and each thread can create windows. An application can use the EnumThreadWindows function to enumerate the windows created by a particular thread. This function passes the handle of each thread window, in turn, to an application-defined callback function. The GetWindowThreadProcessId function returns the identifier of the thread that created a particular window.

+$#A Window Handles

After creating a window, the creation function returns a window handle that uniquely identifies the window. An application uses this handle in other functions to direct their actions to the window. A window handle has the HWND data type; an application must use this type when declaring a variable that holds a window handle.

The Win32 API includes several special constants that can replace a window handle in certain functions. These constants begin with the HWND_ prefix. For example, an application can use HWND_TOP or HWND_BOTTOM in the SetWindowPos function to move a window to the top or bottom of the Z order.

Although the NULL constant is not a window handle, an application can use it in some functions to specify that no window is affected. For example, specifying NULL for the CreateWindowEx function's hwndParent parameter creates a window that has no parent or owner. Some functions may return NULL instead of a handle, indicating that the given action applies to no window.

An application can use the FindWindow function to discover whether a window with the specified class name or window name exists in the system. If such a window exists, FindWindow returns the handle of the window. The IsWindow function determines whether a window handle identifies a valid, existing window.

+$#A Window Styles

The Win32 API provides general window styles and class-specific window styles. The general windows styles are constants that begin with the WS_ prefix; they can be combined to form different types of windows, including main windows, dialog boxes, and child windows. The class-specific window styles define the appearance and behavior of windows belonging to the predefined control classes such as edit controls and list boxes. This section describes the general window styles.

An application usually sets a window's styles when creating the window. It can also set the styles after creating the window by using the SetWindowLong function.

+$#A Overlapped Window

An overlapped window is a top-level window that has a title bar, border, and client area; it is meant to serve as an application's main window. It can also have a System menu, minimize and maximize boxes, and scroll bars. An overlapped window used as a main window typically includes all of these components.

By specifying the WS_OVERLAPPED or WS_OVERLAPPEDWINDOW style in the CreateWindowEx function, an application can create an overlapped window. If created with the WS_OVERLAPPED style, the window has a title bar and border. If created with the WS_OVERLAPPEDWINDOW style, the window has a title bar, sizing border, System menu, and minimize and maximize boxes.

+$#A Pop-up Window

A pop-up window is a special type of overlapped window used for dialog boxes, message boxes, and other temporary windows that appear outside an application's main window. Title bars are optional for pop-up windows; otherwise, pop-up windows are the same as overlapped windows of the WS_OVERLAPPED style.

An application creates a pop-up window by specifying the WS_POPUP style in CreateWindowEx. To include a title bar, the WS_CAPTION style must also be specified. An application can use the WS_POPUPWINDOW style to create a pop-up window that has a border and a System menu. The WS_CAPTION style must be combined with the WS_POPUPWINDOW style to make the System menu visible.

+$#A Child Window

A child window has the WS_CHILD style and is confined to the client area of its parent window. An application typically uses child windows to divide the client area of a parent window into functional areas. An application creates a child window by specifying the WS_CHILD style in the CreateWindowEx function.

A child window must have a parent window. The parent window can be an overlapped window, a pop-up window, or even another child window. An application specifies the parent window when calling CreateWindowEx. If the application specifies the WS_CHILD style in CreateWindowEx but does not specify a parent window, Windows does not create the window.

A child window has a client area but no other features, unless they are explicitly requested. An application can request a title bar, a System menu, minimize and maximize boxes, a border, and scroll bars for a child window, but a child window cannot have a menu. If the application specifies a menu handle, either when it registers the child's window class or creates the child window, the menu handle is ignored.

Positioning

Windows always positions the child window relative to the upper left corner of the parent window's client area. No part of a child window ever appears outside the borders of its parent window. If an application creates a child window that is larger than the parent window or positions a child window so that some or all of the child window extends beyond the borders of the parent, Windows clips the child window; that is, the portion outside the parent window's client area is not displayed. Actions that affect the parent window can also affect the child window, as follows.

Parent window | Child window
Destroyed | Destroyed before the parent window is destroyed.
Hidden | Hidden before the parent window is hidden. A child window is visible only when the parent window is visible.
Moved | Moved with the parent window's client area. The child window is responsible for painting its client area after the move.
Shown | Shown after the parent window is shown.

Clipping

Windows does not automatically clip a child window from the parent window's client area. This means the parent window draws over the child window if it carries out any drawing in the same location as the child window. Windows does, however, clip the child window from the parent window's client area if the parent window has the WS_CLIPCHILDREN style. If the child window is clipped, the parent window cannot draw over it.

A child window can overlap other child windows in the same client area. A child window that shares the same parent window as one or more other child windows is called a sibling window. Sibling windows can draw in each other's client area, unless one of the child windows has the WS_CLIPSIBLINGS style. If a child window does have this style, any portion of its sibling window that lies within the child window is clipped.

If a window has either the WS_CLIPCHILDREN or WS_CLIPSIBLINGS style, a slight loss in performance occurs. Each window takes up system resources, so an application should not use child windows indiscriminately. For best performance, an application that needs to logically divide its main window should do so in the window procedure of the main window rather than by using child windows.

Relationship to Parent Window

An application can change the parent window of an existing child window by calling the SetParent function. In this case, Windows removes the child window from the client area of the old parent window and moves it to the client area of the new parent window. If SetParent specifies a NULL handle, the desktop window becomes the new parent window. In this case, the child window is drawn on the desktop, outside the borders of any other window. The GetParent function retrieves the handle of a child window's parent window.

The parent window relinquishes a portion of its client area to a child window, and the child window receives all input from this area. The window class need not be the same for each of the child windows of the parent window. This means that an application can fill a parent window with child windows that look different and carry out different tasks. For example, a dialog box can contain many types of controls, each one a child window that accepts different types of data from the user.

A child window has only one parent window, but a parent can have any number of child windows. Each child window, in turn, can have child windows. In this chain of windows, each child window is called a descendant window of the original parent window. An application uses the IsChild function to discover whether a given window is a child window or a descendant window of a given parent window.

The EnumChildWindows function enumerates the child windows of a parent window. Then, EnumChildWindows passes the handle of each child window to an application-defined callback function. Descendant windows of the given parent window are also enumerated.

Messages

Windows passes a child window's input messages directly to the child window; the messages are not passed through the parent window. The only exception is if the child window has been disabled by the EnableWindow function. In this case, Windows passes any input messages that would have gone to the child window to the parent window instead. This permits the parent window to examine the input messages and enable the child window, if necessary.

A child window can have a unique integer identifier. Child window identifiers are important when working with control windows. An application directs a control's activity by sending it messages. The application uses the control's child window identifier to direct the messages to the control. In addition, a control sends notification messages to its parent window. A notification message includes the control's child window identifier, which the parent uses to identify which control sent the message. An application specifies the child-window identifier for other types of child windows by setting the hmenu parameter of the CreateWindowEx function to a value rather than a menu handle.

+$#A Window Border

The Win32 API provides the following border styles.

Style | Description
WS_BORDER | Creates a window with a thin-line border.
WS_DLGFRAME | Creates a window with a double border, a style typically used with dialog boxes. A window with this style cannot have a title bar.
WS_EX_DLGMODALFRAME | Creates a window with a double border. Unlike the WS_DLGFRAME style, an application can also specify the WS_CAPTION style to create a title bar for the window.
WS_EX_STATICEDGE | Creates a window with a three-dimensional border style intended to be used for items that do not accept user input.
WS_THICKFRAME | Creates a window with a sizing border.

A window with the WS_OVERLAPPED or WS_POPUPWINDOW style has the WS_BORDER style by default. One of the other border styles must be combined with the WS_OVERLAPPED or WS_POPUPWINDOW style to give an overlapped window a different border style.

If no border style is specified for a window with the WS_POPUP or WS_CHILD style, the system creates a borderless window. An application can use borderless child windows to divide the parent window's client area while keeping the divisions invisible to the user.

+$#A Nonclient-Area Components

The nonclient area of a window can include a title bar, System menu, minimize and maximize boxes, sizing border, and horizontal and vertical scroll bars. An application can create a window with one or more of these components by specifying the following styles in the CreateWindowEx function.

Style | Description
WS_CAPTION | Creates a window that has a title bar (includes the WS_BORDER style).
WS_HSCROLL | Creates a window that has a horizontal scroll bar.
WS_MAXIMIZEBOX | Creates a window that has a maximize box.
WS_MINIMIZEBOX | Creates a window that has a minimize box.
WS_SYSMENU | Creates a window that has a System-menu box in its title bar. The WS_CAPTION style must also be specified.
WS_VSCROLL | Creates a window that has a vertical scroll bar.

+$#A Initial State

The following styles determine whether a window is enabled or disabled, visible or invisible, and minimized or maximized.

Style | Description
WS_DISABLED | Creates a window that is initially disabled. A disabled window cannot receive input from the user.
WS_MAXIMIZE | Creates a window that is initially maximized.
WS_MINIMIZE | Creates a window that is initially minimized.
WS_VISIBLE | Creates a window that is initially visible.

+$#A Parent and Child Styles

The following styles affect the clipping relationship between a parent window and its child windows, and between a child window and its sibling windows.

Style | Description
WS_CLIPCHILDREN | Excludes the area occupied by child windows when drawing within the parent window. Use this style when creating the parent window.
WS_CLIPSIBLINGS | Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of one child window, to draw within the client area of another neighboring child window.

+$#A Dialog Box Styles

A dialog box includes a built-in keyboard interface that processes the direction and TAB keys. The user can use these keys instead of the mouse to navigate among the controls in the dialog box. The following styles affect how the built-in keyboard interface processes the direction and TAB keys.

Style | Description
WS_GROUP | Specifies the first control of a group of controls. The user can change the keyboard focus from one control in the group to the next by using the direction keys. The next control with the WS_GROUP style ends the group and starts the next group.
WS_TABSTOP | Specifies a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control of the WS_TABSTOP style.

+$#A Extended Styles

The following styles can be specified in the dwExStyle parameter of the CreateWindowEx function.

Style | Description
WS_EX_ACCEPTFILES | Specifies that a window created with this style accepts drag-drop files.
WS_EX_CONTEXTHELP | Includes a question mark in the title bar of the window. When the user clicks the question mark, the cursor changes to a question mark with a pointer. If the user then clicks a child window, the child receives a WM_HELP message. The child window should pass the message to the parent window procedure, which should call the WinHelp function using the HELP_WM_HELP command. The Help application displays a pop-up window that typically contains help for the child window.
WS_EX_CONTROLPARENT | Allows the user to navigate among the child windows of the window by using the TAB key.
WS_EX_DLGMODALFRAME | Creates a window with a double border. Unlike the WS_DLGFRAME style, an application can also specify the WS_CAPTION style to create a title bar for the window.
WS_EX_NOPARENTNOTIFY | Specifies that a child window created with this style will not send the WM_PARENTNOTIFY message to its parent window when either created or destroyed.
WS_EX_TOPMOST | Specifies that a window created with this style should be placed above all non-topmost windows and stay above them even when the window is deactivated.
WS_EX_TOOLWINDOW | Creates a tool window; that is, a window intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the task bar or in the window that appears when the user presses ALT+TAB.

+$#A Owned Windows

An overlapped or pop-up window can be owned by another overlapped or pop-up window. Being owned places several constraints on a window.

• An owned window is always above its owner in the Z order.

• Windows automatically destroys an owned window when its owner is destroyed.

• An owned window is hidden when its owner is minimized.

Only an overlapped or pop-up window can be an owner window; a child window cannot be one. An application creates an owned window by specifying the owner's window handle as the hwndParent parameter of CreateWindowEx when it creates a window with the WS_OVERLAPPED or WS_POPUP style. The hwndParent parameter must identify an overlapped or pop-up window. If hwndParent identifies a child window, Windows assigns ownership to the top-level parent window of the child window. After creating an owned window, an application cannot transfer ownership of the window to another window.

Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.

An application can use the GetWindow function with the GW_OWNER flag to retrieve the handle of a window's owner.

+$#A Disabled Windows

A window can be disabled. A disabled window receives no keyboard or mouse input, but it can receive messages from other windows, from other applications, and from Windows. An application typically disables a window to prevent the user from using the window. For example, an application may disable a push button in a dialog box to prevent the user from choosing it. An application can enable a disabled window at any time; enabling a window restores normal input.

By default, a window is enabled when created. An application can specify the WS_DISABLED style, however, to disable a new window. An application enables or disables an existing window by using the EnableWindow function. Windows sends a WM_ENABLE message to a window when its enabled state is about to change. An application can determine whether a window is enabled by using the IsWindowEnabled function.

When a child window is disabled, Windows passes the child's mouse input messages to the parent window. The parent uses the messages to determine whether to enable the child window. For more information about mouse input, see Mouse Input.

Only one window at a time can receive keyboard input; that window is said to have the keyboard focus. If an application uses the EnableWindow function to disable a keyboard-focus window, the window loses the keyboard focus in addition to being disabled. EnableWindow then sets the keyboard focus to NULL, meaning no window has the focus. If a child window, or other descendant window, has the keyboard focus, the descendant window loses the focus when the parent window is disabled. For more information about the keyboard focus, see Keyboard Input.

+$#A Foreground and Background Windows

Each process can have multiple threads of execution, and each thread can create windows. The thread that created the window with which the user is currently working is called the foreground thread, and the window is called the foreground window. All other threads are background threads, and the windows created by background threads are called background windows.

Each thread has a priority level that determines the amount of CPU time the thread receives. Although an application can set the priority level of its threads, normally the foreground thread has a slightly higher priority level than the background threads. Because it has a higher priority, the foreground thread receives more CPU time than the background threads. The foreground thread has a normal base priority of 9; a background thread has a normal base priority of 7.

The user sets the foreground window by clicking a window, or by using the ALT+TAB or ALT+ESC key combination. An application sets the foreground window by using the SetForegroundWindow function. If the new foreground window is a top-level window, Windows activates it; otherwise, it activates the associated top-level window. An application retrieves the handle of the foreground window by using the GetForegroundWindow function.

+$#A Show State

At any one given time, a window may be active or inactive; hidden or visible; and minimized, maximized, or restored. These qualities are referred to collectively as the window's show state.

+$#A Active Window

An active window is the top-level window of the application with which the user is currently working. To allow the user to easily identify the active window, Windows places it at the top of the Z order and changes the color of its title bar and border to the system-defined active window colors. Only a top-level window can be an active window. When the user is working with a child window, Windows activates the top-level parent window associated with the child window.

Only one top-level window in the system is active at a time. The user activates a top-level window by clicking it (or one of its child windows), or by using the ALT+ESC or ALT+TAB key combination. An application activates a top-level window by calling the SetActiveWindow function. A number of other functions can cause Windows to activate a different top-level window, including SetWindowPos, DeferWindowPos, SetWindowPlacement, and DestroyWindow. Although an application can activate a different top-level window at any time, to avoid confusing the user, it should do so only in response to a user action. An application uses the GetActiveWindow function to retrieve the handle of the active window.

When the activation changes from a top-level window of one application to the top-level window of another, Windows sends a WM_ACTIVATEAPP message to both applications, notifying them of the change. When the activation changes to a different top-level window in the same application, Windows sends both windows a WM_ACTIVATE message.

+$#A Visibility

A window can be either visible or hidden. Windows displays a visible window on the screen. It hides a hidden window by not drawing it. If a window is visible, the user can supply input to the window and view the window's output. If a window is hidden, it is effectively disabled. A hidden window can process messages from Windows or from other windows, but it cannot process input from the user or display output. An application sets a window's visibility state when creating the window. Later, the application can change the visibility state.

A window is visible when the WS_VISIBLE style is set for the window. By default, the CreateWindowEx function creates a hidden window unless the application specifies the WS_VISIBLE style. Typically, an application sets the WS_VISIBLE style after it has created a window to keep details of the creation process hidden from the user. For example, an application may keep a new window hidden while it customizes the window's appearance. If the WS_VISIBLE style is specified in CreateWindowEx, Windows sends the WM_SHOWWINDOW message to the window after creating the window, but before displaying it.

An application can determine whether a window is visible by using the IsWindowVisible function. An application can show (make visible) or hide a window by using the ShowWindow, SetWindowPos, DeferWindowPos, or SetWindowPlacement function. These functions show or hide a window by setting or removing the WS_VISIBLE style for the window. They also send the WM_SHOWWINDOW message to the window before showing or hiding it.

When an owner window is minimized, Windows automatically hides the associated owned windows. Similarly, when an owner window is restored, Windows automatically shows the associated owned windows. In both cases, Windows sends the WM_SHOWWINDOW message to the owned windows before hiding or showing them. Occasionally, an application may need to hide the owned windows without having to minimize or hide the owner. In this case, the application uses the ShowOwnedPopups function. This function sets or removes the WS_VISIBLE style for all owned windows and sends the WM_SHOWWINDOW message to the owned windows before hiding or showing them. Hiding an owner window has no effect on the visibility state of the owned windows.

When a parent window is visible, its associated child windows are also visible. Similarly, when the parent window is hidden, its child windows are also hidden. Minimizing the parent window has no effect on the visibility state of the child windows; that is, the child windows are minimized along with the parent, but the WS_VISIBLE style is not changed.

Even if a window has the WS_VISIBLE style, the user may not be able to see the window on the screen; other windows may completely overlap it or it may have been moved beyond the edge of the screen. Also, a visible child window is subject to the clipping rules established by its parent-child relationship. If the window's parent window is not visible, it will also not be visible. If the parent window moves beyond the edge of the screen, the child window also moves because a child window is drawn relative to the parent's upper left corner. For example, a user may move the parent window containing the child window far enough off the edge of the screen that the user may not be able to see the child window, even though the child window and its parent window both have the WS_VISIBLE style.

+$#A Minimized, Maximized, and Restored Windows

A maximized window is a window that has the WS_MAXIMIZE style. By default, Windows enlarges a maximized window so that it fills the screen or, in the case of a child window, the parent window's client area. Although a window's size can be set to the same size of a maximized window, a maximized window is slightly different. Windows automatically moves the window's title bar to the top of the screen or to the top of the parent window's client area. Also, Windows disables the window's sizing border and the window-positioning capability of the title bar (so that the user cannot move the window by dragging the title bar).

A minimized window is a window that has the WS_MINIMIZE style. By default, Windows reduces a minimized window to the size of an icon and moves the minimized window to the lower part of the screen or the lower part of the parent window's client area. The lower part of the screen or client area is sometimes called the icon area. Windows moves a minimized window into the first available position in the icon area, unless the application specifies another position. A restored window is a window that has been returned to its preminimized or premaximized size and position.

If an application specifies the WS_MAXIMIZE or WS_MINIMIZE style in the CreateWindowEx function, the window is initially maximized or minimized. After creating a window, an application can use the CloseWindow function to minimize the window. The ArrangeIconicWindows function arranges the application's minimized top-level windows along the bottom of the screen, or it arranges a parent window's minimized child windows along the bottom of the parent window.

The ShowWindow function can minimize, maximize, or restore a window. It can also set the window's visibility and activation states. The SetWindowPlacement function includes the same functionality as ShowWindow, but it can override the window's default minimized, maximized, and restored positions.

The IsZoomed and IsIconic functions determine whether a given window is maximized or minimized, respectively. The GetWindowPlacement function retrieves the minimized, maximized, and restored positions for the window, and also determines the window's show state.

When Windows receives a command to maximize or restore a minimized window, Windows sends the window a WM_QUERYOPEN message. If the window procedure returns FALSE, Windows ignores the Maximize or Restore command.

Windows automatically sets the size and position of a maximized window to the system-defined defaults for a maximized window. To override these defaults, an application can either call the SetWindowPlacement function or process the WM_GETMINMAXINFO message that is received by a window when Windows is about to maximize the window. WM_GETMINMAXINFO includes a pointer to a MINMAXINFO structure containing values Windows uses to set the maximized size and position. Replacing these values overrides the defaults.

C

Créé avec HelpNDoc Personal Edition: Produire des livres électroniques facilement


Window Classes.rtf

+$#AK Window Classes

Every window is a member of a window class. A window class is a set of attributes that Microsoft® Windows® uses as a template to create a window in an application. This overview describes the types of window classes, how Windows locates them, and the elements that define the default behavior of windows that belong to them.

+$#A About Window Classes

Each window class has an associated window procedure shared by all windows of the same class. The window procedure processes messages for all windows of that class and therefore controls their behavior and appearance. For more information about window procedures, see Window Procedures.

An application must register a window class before it can create a window of that class. Registering a window class associates a window procedure, class styles, and other class attributes with a class name. When an application specifies a class name in the CreateWindow or CreateWindowEx function, the operating system creates a window with the window procedure, styles, and other attributes associated with that class name.

+$#A Types of Window Classes

There are three types of window classes: system global, application global, and application local. These types differ in scope and in when and how they are registered and destroyed.

+$#A System Global Classes

When Windows starts, it registers a set of system global classes for controls, including buttons, combo boxes, list boxes, scroll bars, edit controls, and static controls.

Any application can use a system global class at any time. Because Windows registers system global classes on behalf of all applications, an application cannot destroy any of these classes.

+$#A Application Global Classes

An application global class is a window class registered by a dynamic-link library (DLL) and available to all applications in the system. For example, you can register a window class that defines a custom control as an application global class so that all applications can create instances of the custom control.

In Windows, all window classes are process specific. An application can create a global class by creating the window class in a DLL and listing the name of the DLL in the registry under the appropriate keys.

Whenever a process starts, the system loads the specified DLL in the context of the newly started process before calling the main function in that process. The DLL must register the class during its initialization procedure and must specify the CS_GLOBALCLASS style. (For more information about class styles, see Class Styles.) After a class has been registered, any application can use it to create any number of windows belonging to that class.

Windows does not automatically destroy a class when the DLL that registered it is unloaded. For this reason, the Windows exit procedure of the DLL should call the UnregisterClass function to remove the class.

+$#A Application Local Classes

An application local class is any window class that an application registers for its exclusive use. Although an application can register any number of local classes, most applications register only one. This window class supports the window procedure of the application's main window.

Registering a local class is similar to registering an application global class, except that the CS_GLOBALCLASS style is not specified in the style member of the WNDCLASSEX structure.

Windows destroys a local class when the application that registered it closes. An application can also use the UnregisterClass function to remove a local class and free the storage associated with it.

+$#A How Windows Locates a Class

Windows maintains a list of WNDCLASSEX structures for each of the three types of window classes. When an application calls the CreateWindow or CreateWindowEx function to create a window with a specified class, Windows uses the following procedure to locate the class:

1. Windows searches the list of application local classes for a class with the specified name. Note that several application instances can use the same name to register local classes. Windows uses instance handles to differentiate among local classes that have the same name.

2. If the name is not in the application local class list, Windows searches the list of application global classes.

3. If the name is not in the application global class list, Windows searches the list of system global classes.

All windows created by the application use this procedure, including windows created by Windows on the application's behalf, such as dialog boxes. It is possible to override system global classes without affecting other applications. That is, an application can register an application local class having the same name as a system global class. This replaces the system global class in the context of the application but does not prevent other applications from using the system global class.

+$#A Class Ownership

The owner of a class is the application or DLL that registered the class. Windows determines class ownership from the hInstance member of the WNDCLASSEX structure passed to the RegisterClassEx function when the class is registered. For Windows DLLs, the hInstance member must be the instance handle of the DLL. The class is destroyed when the owner closes or is unloaded. For this reason, the application must destroy all windows using the class before it closes or the DLL is unloaded.

+$#A Elements of a Window Class

The elements of a window class define the default behavior of windows belonging to the class. The application that registers a window class assigns elements to the class by setting appropriate members in a WNDCLASSEX structure and passing the structure to the RegisterClassEx function. The GetClassInfoEx and GetClassLong functions retrieve information about a given window class. The SetClassLong function changes elements of a local or global class that the application has already registered.

Although a complete window class consists of many elements, Windows requires only that an application supply a class name, the window-procedure address, and an instance handle. Use the other elements to define default attributes for windows of the class, such as the shape of the cursor and the content of the menu for the window. You must initialize any unused members of the WNDCLASSEX structure to zero or NULL. The window class elements are as follows.

Element | Purpose
Class name | Distinguishes the class from other registered classes.
Window-procedure address | Points to the function that processes all messages sent to windows in the class and defines the behavior of the window.
Instance handle | Identifies the application or DLL that registered the class.
Class cursor | Defines the mouse cursor that Windows displays for a window of the class.
Class icon | Defines the large icon.
Small class icon | Defines the small icon (Windows version 4.0 and higher).
Class background brush | Defines the color and pattern that fill the client area when the window is opened or painted.
Class menu | Specifies the default menu for windows that do not explicitly define a menu.
Class styles | Defines how to update the window after moving or resizing it, how to process double-clicks of the mouse, how to allocate space for the device context, and other aspects of the window.
Extra class memory | Specifies the amount of extra memory, in bytes, that Windows should reserve for the class. All windows in the class share the extra memory and can use it for any application-defined purpose. Windows initializes this memory to zero.
Extra window memory | Specifies the amount of extra memory, in bytes, that Windows should reserve for each window belonging to the class. The extra memory can be used for any application-defined purpose. Windows initializes this memory to zero.

+$#A Class Name

Every window class needs a class name to distinguish one class from another. Assign a class name by setting the lpszClassName member of the WNDCLASSEX structure to the address of a null-terminated string that specifies the name. Because window classes are process specific in Windows, window class names need to be unique only within the same process. Also, because class names occupy space in the system's private atom table, you should keep class name strings as short a possible.

The GetClassName function retrieves the name of the class to which a given window belongs.

+$#A Window-Procedure Address

Every class needs a window-procedure address to define the entry point of the window procedure used to process all messages for windows in the class. Windows passes messages to the procedure when it requires the window to carry out tasks, such as painting its client area or responding to input from the user. An application assigns a window procedure to a class by copying its address to the lpfnWndProc member of the WNDCLASSEX structure. For more information about window procedures, see Window Procedures.

+$#A Instance Handle

Every window class requires an instance handle to identify the application or DLL that registered the class. As a multitasking system, Windows lets several applications or DLLs run at the same time, so it requires instance handles to keep track of all of them. Windows assigns a handle to each copy of a running application or DLL.

Multiple instances of the same application or DLL all use the same code segment, but each has its own data segment. Windows uses an instance handle to identify the data segment that corresponds to a particular instance of an application or DLL.

Windows passes an instance handle to an application or DLL when the application starts. The application or DLL assigns this instance handle to the class by copying it to the hInstance member of the WNDCLASSEX structure.

+$#A Class Cursor

The class cursor defines the shape of the cursor when it is in the client area of a window in the class. Windows automatically sets the cursor to the given shape when the cursor enters the window's client area and ensures it keeps that shape while it remains in the client area. To assign a cursor shape to a window class, load a predefined cursor shape by using the LoadCursor function and then assign the returned cursor handle to the hCursor member of the WNDCLASSEX structure. Alternatively, provide a custom cursor resource and use the LoadCursor function to load it from the application's resources.

Windows does not require a class cursor. If an application sets the hCursor member of the WNDCLASSEX structure to NULL, a class cursor is not defined. Windows assumes the window sets the cursor shape each time the cursor moves into the window. A window can set the cursor shape by calling the SetCursor function whenever the window receives the WM_MOUSEMOVE message. For more information about cursors, see Cursors.

+$#A Class Icons

A class icon is a picture that the system uses to represent a window of a particular class. A class can have one or two icons, depending on the operating system version. You can use the GetVersionEx function to retrieve the version number.

Class Icons in Version 4.0 or Later

With version 4.0 or later, an application can have two class icons ⎯ one large and one small. The system displays a window's large class icon in the task-switch window that appears when the user presses ALT+TAB, and in the large icon views of the task bar and explorer. The small class icon appears in a window's title bar and in the small icon views of the task bar and explorer.

To assign a large and small icon to a window class, specify the handles of the icons in the hIcon and hIconSm members of the WNDCLASSEX structure. The icon dimensions must conform to required dimensions for large and small class icons. For a large class icon, you can determine the required dimensions by specifying the SM_CXICON and SM_CYICON values in a call to the GetSystemMetrics function. For a small class icon, specify the SM_CXSMICON and SM_CYSMICON values. For information about creating icons and loading icon resources, see Icons.

If an application sets the hIcon and hIconSm members of the WNDCLASSEX structure to NULL, the system uses the default application icon as the large and small class icons for the window class. If you specify a large class icon but not a small one, the system creates a small class icon based on the large one. However, if you specify a small class icon but not a large one, the system uses the default application icon as the large class icon and the specified icon as the small class icon.

You can override the large or small class icon for a particular window by using the WM_SETICON message. You can retrieve the current large or small class icon by using the WM_GETICON message.

Class Icons in Versions Earlier Than 4.0

In operating system versions earlier than 4.0, the class icon defines the icon used when a window of the given class is minimized. To assign an icon to a window class, specify an icon handle in the hIcon member of the WNDCLASS structure. If an application sets the hIcon member of WNDCLASS to NULL, a class icon is not defined. In this case, Windows sends the WM_ICONERASEBKGND message to a window of the class whenever it must paint the icon background. If the window does not process the WM_ICONERASEBKGND message, Windows draws an image of the contents of the window's client area when the window is minimized. For more information about drawing minimized windows, see Painting and Drawing.

+$#A Class Background Brush

A class background brush prepares the client area of a window for subsequent drawing by the application. Windows uses the brush to fill the client area with a solid color or pattern, thereby removing all previous images from that location whether they belonged to the window or not. Windows notifies a window that its background should be painted by sending the WM_ERASEBKGND message to the window. For more information about brushes, see Brushes.

To assign a background brush to a class, create a brush by using the appropriate GDI functions and assign the returned brush handle to the hbrBackground member of the WNDCLASSEX structure.

Instead of creating a brush, an application can set the hbrBackground member to one of the standard system color values. For a list of the standard system color values, see SetSysColors.

To use a standard system color, the application must increase the background-color value by one. For example, COLOR_BACKGROUND + 1 is the system background color. Alternatively, you can use the GetSysColorBrush function to retrieve the handle of a brush that corresponds to a standard system color, and then specify the handle in the hbrBackground member of the WNDCLASSEX structure.

Windows does not require that a window class have a class background brush. If this parameter is set to NULL, the window must paint its own background whenever it receives the WM_ERASEBKGND message.

+$#A Class Menu

A class menu defines the default menu to be used by the windows in the class if no explicit menu is given when the windows are created. A menu is a list of commands from which a user can choose actions for the application to carry out.

You can assign a menu to a class by setting the lpszMenuName member of the WNDCLASSEX structure to the address of a null-terminated string that specifies the resource name of the menu. The menu is assumed to be a resource in the given application. Windows automatically loads the menu when it is needed. Note that if the menu resource is identified by an integer and not by a name, the application can set the lpszMenuName member to that integer by applying the MAKEINTRESOURCE macro before assigning the value.

Windows does not require a class menu. If an application sets the lpszMenuName member of the WNDCLASSEX structure to NULL, Windows assumes the windows in the class have no menu bars. Even if no class menu is given, an application can still define a menu bar for a window when it creates the window.

Windows does not allow menu bars with child windows. If a menu is given for a class and a child window of that class is created, the menu is ignored. For more information about menus, see Menus (Windows NT).

+$#A Class Styles

The class styles define additional elements of the window class. Two or more styles can be combined by using the bitwise OR (|) operator. To assign a style to a window class, assign the style to the style member of the WNDCLASSEX structure. The class styles are as follows.

Style | Action
CS_BYTEALIGNCLIENT | Aligns the window's client area on a byte boundary (in the x direction) to enhance performance during drawing operations. This style affects the width of the window and its horizontal placement on the display.
CS_BYTEALIGNWINDOW | Aligns the window on a byte boundary (in the x direction) to enhance performance during operations that involve moving or sizing the window. This style affects the width of the window and its horizontal placement on the display.
CS_CLASSDC | Allocates one device context to be shared by all windows in the class. For more information about device contexts, see Class and Private Device Contexts and Device Contexts.
CS_DBLCLKS | Instructs Windows to send a double-click message to the window procedure when the user double-clicks the mouse while the cursor is within a window belonging to the class. For more information about double-clicks, see Mouse Input.
CS_GLOBALCLASS | Specifies that the window class is an application global class. For more information, see Application Global Classes.
CS_HREDRAW | Specifies that the entire window is to be redrawn if a movement or size adjustment changes the width of the client area.
CS_NOCLOSE | Disables the Close command on the System menu.
CS_OWNDC | Allocates a unique device context for each window in the class. For more information about device contexts, see Class and Private Device Contexts and Device Contexts.
CS_PARENTDC | Sets the clipping rectangle of the child window to that of the parent window so that the child can draw on the parent. A window with the CS_PARENTDC style bit receives a regular device context from the system's cache of device contexts. It does not give the child the parent's device context or device context settings. Specifying CS_PARENTDC enhances an application's performance. For more information about device contexts, see Class and Private Device Contexts and Device Contexts.
CS_SAVEBITS | Saves, as a bitmap, the portion of the screen image obscured by a window. Windows uses the saved bitmap to re-create the screen image when the window is removed. Windows displays the bitmap at its original location and does not send WM_PAINT messages to windows obscured by the window if other screen actions have not invalidated the stored image. Use this style for small windows that are displayed briefly and then removed before other screen activity takes place (for example, menus or dialog boxes). This style increases the time required to display the window, because the operating system must first allocate memory to store the bitmap.
CS_VREDRAW | Specifies that the entire window is to be redrawn if a movement or size adjustment changes the height of the client area.

+$#A Extra Class Memory

Windows maintains a WNDCLASSEX structure internally for each window class in the system. When an application registers a window class, it can direct Windows to allocate and append a number of additional bytes of memory to the end of the WNDCLASSEX structure. This memory is called extra class memory and is shared by all windows belonging to the class. Use the extra class memory to store any information pertaining to the class.

Because extra memory is allocated from the system's local heap, an application should use extra class memory sparingly. With operating system version 4.0 or later, the RegisterClassEx function fails if the amount of extra class memory requested is greater than 40 bytes. If an application requires more than 40 bytes, it should allocate its own memory and store a pointer to the memory in the extra class memory.

The SetClassWord and SetClassLong functions copy a value to the extra class memory. To retrieve a value from the extra class memory, use the GetClassWord and GetClassLong functions. The cbClsExtra member of the WNDCLASSEX structure specifies the amount of extra class memory to allocate. An application that doesn't use extra class memory must initialize the cbClsExtra member to zero.

+$#A Extra Window Memory

Windows maintains an internal data structure for each window. When registering a window class, an application can specify a number of additional bytes of memory, called extra window memory. When creating a window of the class, Windows allocates and appends the specified amount of extra window memory to the end of the window's structure. An application can use this memory to store window-specific data.

Because extra memory is allocated from the system's local heap, an application should use extra window memory sparingly. With operating system version 4.0 or later, the RegisterClassEx function fails if the amount of extra window memory requested is greater than 40 bytes. If an application requires more than 40 bytes, it should allocate its own memory and store a pointer to the memory in the extra window memory.

The SetWindowWord and SetWindowLong functions copy a value to the extra memory. The GetWindowWord and GetWindowLong functions retrieve a value from the extra memory. The cbWndExtra member of the WNDCLASSEX structure specifies the amount of extra window memory to allocate. An application that doesn't use the memory must initialize cbWndExtra to zero.

+$#A Class and Private Device Contexts

A device context is a special set of values that applications use for drawing in the client area of their windows. Windows requires a device context for each window on the display but allows some flexibility in how the operating system stores and treats that device context.

If no device-context style is explicitly given, Windows assumes each window uses a device context retrieved from a pool of contexts maintained by Windows. In such cases, each window must retrieve and initialize the device context before painting and free it after painting.

To avoid retrieving a device context each time it needs to paint inside a window, an application can specify the CS_OWNDC style for the window class. This class style directs Windows to create a private device context ⎯ that is, to allocate a unique device context for each window in the class. The application need only retrieve the context once and then use it for all subsequent painting. Although the CS_OWNDC style is convenient, use it carefully, because each device context uses a significant portion of system resources.

By specifying the CS_CLASSDC style, an application can create a class device context. A class device context is a rarely used feature that allows multiple windows created from the same window class within a process to use exactly the same device context for drawing.

An application can specify the CS_PARENTDC style to create child windows that inherit the device context of their parent window. This style allows a child window to draw in the client area of its parent.

An application can specify the CS_PARENTDC style to set the clipping rectangle of the child window to that of the parent window so the child can draw in the parent. A window with the CS_PARENTDC style receives a regular device context from the system's cache of device contexts. It does not give a child window's device context or device context settings to the parent window. Specifying CS_PARENTDC enhances an application's performance because Windows doesn't need to keep recalculating the visible region for each child window.

For more information about device contexts, see Device Contexts. For more information about painting, see Painting and Drawing.

+$#A Using Window Classes

In Windows, each application must register its own window classes. Your application can register an application local class at any time by using the RegisterClassEx function. You must define the window procedure in the application, fill the members of the WNDCLASSEX structure, and then pass a pointer to the structure to the RegisterClassEx function.

The following example shows how to register a local window class and use it to create your application's main window.

#include <windows.h>

/* Global variable */

HINSTANCE hinst;

/* Function prototypes */

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

InitApplication(HINSTANCE);

InitInstance(HINSTANCE, int);

LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

/* Application entry point */

int WINAPI WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hinstance;

HINSTANCE hPrevInstance;

LPSTR lpCmdLine;

int nCmdShow;

{

MSG msg;

if (!InitApplication(hinstance))

return FALSE;

if (!InitInstance(hinstance, nCmdShow))

return FALSE;

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

UNREFERENCED_PARAMETER(lpCmdLine);

}

BOOL InitApplication(hinstance)

HINSTANCE hinstance;

{

WNDCLASSEX wcx;

/*

* Fill in the window class structure with parameters

* that describe the main window.

*/

wcx.cbSize = sizeof(wcx); /* size of structure */

wcx.style = CS_HREDRAW |

CS_VREDRAW; /* redraw if size changes */

wcx.lpfnWndProc = MainWndProc; /* points to window proc. */

wcx.cbClsExtra = 0; /* no extra class memory */

wcx.cbWndExtra = 0; /* no extra window memory */

wcx.hInstance = hinstance; /* handle of instance */

wcx.hIcon = LoadIcon(NULL,

IDI_APPLICATION); /* predefined app. icon */

wcx.hCursor = LoadCursor(NULL,

IDC_ARROW); /* predefined arrow */

wcx.hbrBackground = GetStockObject(

WHITE_BRUSH); /* white background brush */

wcx.lpszMenuName = "MainMenu"; /* name of menu resource */

wcx.lpszClassName = "MainWClass"; /* name of window class */

wcx.hIconSm = LoadImage(hinstance, /* small class icon */

MAKEINTRESOURCE(5),

GetSystemMetrics(SM_CXSMICON),

GetSystemMetrics(SM_CYSMICON),

LR_DEFAULTCOLOR);

/* Register the window class. */

return RegisterClassEx(&wcx);

}

BOOL InitInstance(hinstance, nCmdShow)

HINSTANCE hinstance;

int nCmdShow;

{

HWND hwnd;

/* Save the application-instance handle. */

hinst = hinstance;

/* Create the main window. */

hwnd = CreateWindow(

"MainWClass", /* name of window class */

"Sample", /* title-bar string */

WS_OVERLAPPEDWINDOW, /* top-level window */

CW_USEDEFAULT, /* default horizontal position */

CW_USEDEFAULT, /* default vertical position */

CW_USEDEFAULT, /* default width */ D CW_USEDEFAULT, /* default height */

(HWND) NULL, /* no owner window */

(HMENU) NULL, /* use class menu */

hinstance, /* handle of app. instance */

(LPVOID) NULL); /* no window-creation data */

if (!hwnd)

return FALSE;

/*

* Show the window and send a WM_PAINT message to the window

* procedure.

*/

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

return TRUE;

}

Registering an application global class is similar to registering an application local class, with the following exceptions:

• The style parameter of the WNDCLASSEX structure must specify the CS_GLOBALCLASS style.

• The class can be registered by an application or a DLL. If registered by an application, the class is global only within the application. If the class is registered by a DLL and listed in the registry, the system loads the DLL for every application.

• The application or DLL need not check for a previous instance of the application or DLL before registering the class.

+$#A Window Class Functions

Following are the functions used with window classes.

GetClassInfo
GetClassInfoEx
GetClassLong
GetClassName
GetClassWord
GetWindowLong
GetWindowWord
RegisterClass
RegisterClassEx
SetClassLong
SetClassWord
SetWindowLong
SetWindowWord
UnregisterClass

Créé avec HelpNDoc Personal Edition: Outils facile d'utilisation pour créer des aides HTML et des sites web


Window Size and Position.rtf

+$#AK Window Size and Position

A window's size and position are expressed as a bounding rectangle, given in coordinates relative to the screen or the parent window. The coordinates of a top-level window are relative to the upper left corner of the screen; the coordinates of a child window are relative to the upper left corner of the parent window. An application specifies a window's initial size and position when it creates the window, but it can change the window's size and position at any time. For more information about bounding rectangles, see Filled Shapes.

+$#A Size

A window's size (width and height) is given in pixels. A window can have zero width or height. If an application sets a window's width and height to zero, Windows sets the size to the default minimum window size. To discover the default minimum window size, an application uses the GetSystemMetrics function with the SM_CXMIN and SM_CYMIN flags.

An application may need to create a window with a client area of a particular size. The AdjustWindowRect and AdjustWindowRectEx functions calculate the required size of a window based on the desired size of the client area. The application can pass the resulting size values to the CreateWindowEx function.

An application can size a window so that it is extremely large; however, it should not size a window so that it is larger than the screen. Before setting a window's size, the application should check the width and height of the screen by using GetSystemMetrics with the SM_CXSCREEN and SM_CYSCREEN flags.

+$#A Position

A window's position is defined as the coordinates of its upper left corner. These coordinates, sometimes called window coordinates, are always relative to the upper left corner of the screen or, for a child window, the upper left corner of the parent window's client area. For example, a top-level window having the coordinates (10,10) is placed 10 pixels to the right of the upper left corner of the screen and 10 pixels down from it. A child window having the coordinates (10,10) is placed 10 pixels to the right of the upper left corner of its parent window's client area and 10 pixels down from it.

The WindowFromPoint function retrieves the handle of the window occupying a particular point on the screen. Similarly, the ChildWindowFromPoint and ChildWindowFromPointEx functions retrieve the handle of the child window occupying a particular point in the parent window's client area. Although ChildWindowFromPointEx can ignore invisible, disabled, and transparent child windows, ChildWindowFromPoint cannot.

+$#A Default Size and Position

An application can allow Windows to calculate the initial size or position of a top-level window by specifying CW_USEDEFAULT in CreateWindowEx. If the application sets the window's coordinates to CW_USEDEFAULT and has created no other top-level windows, Windows sets the new window's position relative to the upper left corner of the screen; otherwise, it sets the position relative to the position of the top-level window that the application created most recently. If the width and height parameters are set to CW_USEDEFAULT, Windows calculates the size of the new window. If the application has created other top-level windows, Windows bases the size of the new window on the size of the application's most recently created top-level window. Specifying CW_USEDEFAULT when creating a child or pop-up window causes Windows to set the window's size to the default minimum window size.

+$#A Tracking Size

Windows maintains a minimum and maximum tracking size for a window of the WS_THICKFRAME style; a window with this style has a sizing border. The minimum tracking size is the smallest window size the user can produce by dragging the window's sizing border. Similarly, the maximum tracking size is the largest window size the user can produce by dragging the sizing border.

A window's minimum and maximum tracking sizes are set to system-defined default values when Windows creates the window. An application can discover the defaults and override them by processing the WM_GETMINMAXINFO message. For more information about this message, see Size and Position Messages.

+$#A System Commands

An application that has a window with a System menu can change the size and position of that window by sending system commands. System commands are generated when the user chooses commands from the System menu. An application can emulate the user action by sending a WM_SYSCOMMAND message to the window. The following system commands affect the size and position of a window.

Command | Description
SC_CLOSE | Closes the window. This command sends a WM_CLOSE message to the window. The window carries out any steps needed to clean up and destroy itself.
SC_MAXIMIZE | Maximizes the window.
SC_MINIMIZE | Minimizes the window.
SC_RESTORE | Restores a minimized or maximized window to its previous size and position.
SC_SIZE | Starts a Size command. The user can change the size of the window by using the mouse or keyboard.

+$#A Z Order

The Z order of a window indicates the window's position in a stack of overlapping windows. This window stack is oriented along an imaginary axis, the z-axis, extending outward from the screen. The window at the top of the Z order overlaps all other windows. The window at the bottom of the Z order is overlapped by all other windows. An application sets a window's position in the Z order by placing it behind a given window, or at the top or bottom of the stack.

Windows maintains three separate Z orders ⎯ one for top-level windows, one for sibling windows, and one for topmost windows. A topmost window overlaps all other non-topmost windows, regardless of whether it is the active or foreground window. A topmost window has the WS_EX_TOPMOST style.

By default, Windows puts a window at the top of the Z order when creating the window. The user changes the Z order by activating a different window; Windows always positions the active window at the top of the Z order. An application uses the BringWindowToTop function to bring a window to the top of the Z order. It can rearrange the Z order by using SetWindowPos or DeferWindowPos.

+$#A Size and Position Functions

After creating a window, an application can set the window's size or position by calling one of several different functions, including SetWindowPlacement, MoveWindow, SetWindowPos, and DeferWindowPos. SetWindowPlacement sets a window's minimized position, maximized position, restored size and position, and show state. The MoveWindow and SetWindowPos functions are similar; both set the size or position of a single application window. The SetWindowPos function includes a set of flags that affect the window's show state; MoveWindow does not include these flags. Use the BeginDeferWindowPos, DeferWindowPos, and EndDeferWindowPos functions to simultaneously set the position of a number of windows, including the size, position, position in the Z order, and show state.

An application can retrieve the coordinates of a window's bounding rectangle by using the GetWindowRect function. GetWindowRect fills a RECT structure with the coordinates of the window's upper left and lower right corners. The coordinates are relative to the upper left corner of the screen, even for a child window. The ScreenToClient or MapWindowPoints function maps the screen coordinates of a child window's bounding rectangle to coordinates relative to the parent window's client area.

The GetClientRect function retrieves the coordinates of a window's client area. GetClientRect fills a RECT structure with the coordinates of the upper left and lower right corners of the client area, but the coordinates are relative to the client area itself. This means the coordinates of a client area's upper left corner are always (0,0), and the coordinates of the lower right corner are the width and height of the client area.

+$#A Size and Position Messages

Windows sends the WM_GETMINMAXINFO message to a window whose size or position is about to change. For example, the message is sent when the user chooses the Move or Size command from the System menu or clicks the sizing border or title bar; the message is also sent when an application calls SetWindowPos to move or size the window. WM_GETMINMAXINFO includes a pointer to a MINMAXINFO structure containing the default maximized size and position for the window, as well as the default minimum and maximum tracking sizes. An application can override the defaults by processing WM_GETMINMAXINFO and setting the appropriate members of MINMAXINFO. A window must have the WS_THICKFRAME or WS_CAPTION style to receive WM_GETMINMAXINFO. A window with the WS_THICKFRAME style receives this message during the window-creation process, as well as when it is being moved or sized.

Windows sends the WM_WINDOWPOSCHANGING message to a window whose size, position, position in the Z order, or show state is about to change. This message includes a pointer to a WINDOWPOS structure that specifies the window's new size, position, position in the Z order, and show state. By setting the members of WINDOWPOS, an application can affect the window's new size, position, and appearance.

After changing a window's size, position, position in the Z order, or show state, Windows sends the WM_WINDOWPOSCHANGED message to the window. This message includes a pointer to WINDOWPOS that informs the window of its new size, position, position in the Z order, and show state. Setting the members of the WINDOWPOS structure that is passed with WM_WINDOWPOSCHANGED has no effect on the window. A window that must process WM_SIZE and WM_MOVE messages must pass WM_WINDOWPOSCHANGED to the DefWindowProc function; otherwise, Windows does not send WM_SIZE and WM_MOVE messages to the window.

Windows sends the WM_NCCALCSIZE message to a window when the window is created or sized. Windows uses the message to calculate the size of a window's client area and the position of the client area relative to the upper left corner of the window. A window typically passes this message to the default window procedure; however, this message can be useful in applications that customize a window's nonclient area or preserve portions of the client area when the window is sized. For more information about window size, see Painting and Drawing.

+$#A Window Destruction

In general, an application must destroy all the windows it creates. It does this by using the DestroyWindow function. When a window is destroyed, the system hides the window, if it is visible, and then removes any internal data associated with the window. This invalidates the window handle, which can no longer be used by the application.

An application destroys many of the windows it creates soon after creating them. For example, an application usually destroys a dialog box window as soon as the application has sufficient input from the user to continue its task. An application eventually destroys the main window of the application (before terminating).

Before destroying a window, an application should save or remove any data associated with the window, and it should release any system resources allocated for the window. If the application does not release the resources, Windows will free any resources not freed by the application.

Destroying a window does not affect the window class from which the window is created. New windows can still be created using that class, and any existing windows of that class continue to operate. Destroying a window also destroys the window's descendant windows. The DestroyWindow function sends a WM_DESTROY message first to the window, then to its child windows and descendant window. In this way, all descendant windows of the window being destroyed are also destroyed.

A window with a System menu receives a WM_CLOSE message when the user chooses the Close command from the System menu. By processing this message, an application can prompt the user for confirmation before destroying the window. If the user confirms that the window should be destroyed, the application can call the DestroyWindow function to destroy the window.

If the window being destroyed is the active window, both the active and focus states are transferred to another window. The window that becomes the active window is the next window, as determined by the ALT+ESC key combination. The new active window then determines which window receives the keyboard focus.

+$#A Using Windows

• Creating a main window.

• Creating, enumerating, and sizing child windows.

• Destroying a window.

+$#A Creating a Main Window

The first window an application creates is typically the main window. You create the main window by using the CreateWindowEx function, specifying the window class, window name, window styles, size, position, menu handle, instance handle, and creation data. A main window belongs to an application-defined window class, so you must register the window class and provide a window procedure for the class before creating the main window.

Most applications typically use the WS_OVERLAPPEDWINDOW style to create the main window. This style gives the window a title bar, a System menu, a sizing border, and minimize and maximize boxes. The CreateWindowEx function returns a handle that uniquely identifies the window.

The following example creates a main window belonging to an application-defined window class. The window name, "Main Window", will appear in the window's title bar. By combining the WS_VSCROLL and WS_HSCROLL styles with the WS_OVERLAPPEDWINDOW style, the application creates a main window with horizontal and vertical scroll bars in addition to the components provided by the WS_OVERLAPPEDWINDOW style. The four occurrences of the CW_USEDEFAULT constant set the initial size and position of the window to the system-defined default values. By specifying NULL instead of a menu handle, the window will have the menu defined for the window class.

HINSTANCE hinst;

HWND hwndMain;

.

. /* Register the window class for the main window. */

.

/* Create the main window. */

hwndMain = CreateWindowEx(

0, /* no extended styles */

"MainWClass", /* class name */

"Main Window", /* window name */

WS_OVERLAPPEDWINDOW | /* overlapped window */

WS_HSCROLL | /* horizontal scroll bar */

WS_VSCROLL, /* vertical scroll bar */

CW_USEDEFAULT, /* default horizontal position */

CW_USEDEFAULT, /* default vertical position */

CW_USEDEFAULT, /* default width */

CW_USEDEFAULT, /* default height */

(HWND) NULL, /* no parent or owner window */

(HMENU) NULL, /* class menu used */

hinstance, /* instance handle */

NULL); /* no window creation data */

if (!hwndMain)

return FALSE;

/*

* Show the window using the flag specified by the program

* that launched the application, and send the application

* a WM_PAINT message.

*/

ShowWindow(hwndMain, SW_SHOWDEFAULT);

UpdateWindow(hwndMain);

.

. /* Start the message loop for the application. */

.

Notice that the preceding example calls the ShowWindow function after creating the main window. This is done because Windows does not automatically display the main window after creating it. By passing the SW_SHOWDEFAULT flag to ShowWindow, the application allows the program that launched the application to set the initial show state of the main window. The UpdateWindow function sends the window its first WM_PAINT message.

+$#A Creating, Enumerating, and Sizing Child Windows

You can divide a window's client area into different functional areas by using child windows. Creating a child window is like creating a main window ⎯ you use the CreateWindowEx function. To create a window of an application-defined window class, you must register the window class and provide a window procedure before creating the child window. You must give the child window the WS_CHILD style and specify a parent window for the child window when you create it.

The following example divides the client area of an application's main window into three functional areas by creating three child windows of equal size. Each child window is the same height as the main window's client area, but each is one-third its width. The main window creates the child windows in response to the WM_CREATE message, which the main window receives during its own window-creation process. Because each child window has the WS_BORDER style, each has a thin line border. Also, because the WS_VISIBLE style is not specified, each child window is initially hidden. Notice also that each child window is assigned a child-window identifier.

The main window sizes and positions the child windows in response to the WM_SIZE message, which the main window receives when its size changes. In response to WM_SIZE, the main window retrieves the dimensions of its client area by using the GetWindowRect function and then passes the dimensions to the EnumChildWindows function. EnumChildWindows passes the handle of each child window, in turn, to the application-defined EnumChildProc callback function. This function sizes and positions each child window by calling the MoveWindow function; the size and position are based on the dimensions of the main window's client area and the identifier of the child window. Afterward, EnumChildProc calls the ShowWindow function to make the window visible.

#define ID_FIRSTCHILD 100

#define ID_SECONDCHILD 101

#define ID_THIRDCHILD 102

LONG APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

UINT wParam;

LONG lParam;

{

RECT rcClient;

int i;

switch(uMsg) {

case WM_CREATE: /* creating main window */

/* Create three invisible child windows. */

for (i = 0; i < 3; i++)

CreateWindowEx(

0,

"ChildWClass",

(LPCTSTR) NULL,

WS_CHILD | WS_BORDER,

0,0,0,0,

hwnd,

(HMENU) (int) (ID_FIRSTCHILD + i),

hinst,

NULL);

return 0;

case WM_SIZE: /* main window changed size */

/*

* Get the dimensions of the main window's client

* area, and enumerate the child windows. Pass the

* dimensions to the child windows during enumeration.

*/

GetClientRect(hwnd, &rcClient);

EnumChildWindows(hwnd, EnumChildProc,

(LPARAM) &rcClient);

return 0;

.

. /* Process other messages. */

.

}

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

BOOL CALLBACK EnumChildProc(hwndChild, lParam)

HWND hwndChild;

LPARAM lParam;

{

LPRECT rcParent;

int i, idChild;

/*

* Retrieve the child-window identifier. Use it to set the

* position of the child window.

*/

idChild = GetWindowLong(hwndChild, GWL_ID);

if (idChild == ID_FIRSTCHILD)

i = 0;

else if (idChild == ID_SECONDCHILD)

i = 1;

else

i = 2;

/* Size and position the child window. */

rcParent = (LPRECT) lParam;

MoveWindow(hwndChild,

(rcParent->right / 3) * i,

0,

rcParent->right / 3,

rcParent->bottom,

TRUE);

/* Make sure the child window is visible. */

ShowWindow(hwndChild, SW_SHOW);

return TRUE;

}

+$#A Destroying a Window

You can use the DestroyWindow function to destroy a window. Typically, an application sends the WM_CLOSE message before destroying a window, giving the window the opportunity to prompt the user for confirmation before the window is destroyed. A window that includes a System menu automatically receives the WM_CLOSE message when the user chooses the Close command from the menu. If the user confirms that the window should be destroyed, the application calls DestroyWindow. Windows sends the WM_DESTROY message to the window after removing it from the screen. In response to WM_DESTROY, the window saves its data and frees any resources it allocated. A main window concludes its processing of WM_DESTROY by calling the PostQuitMessage function to quit the application.

The following example shows how to prompt for user confirmation before destroying a window. In response to WM_CLOSE, the example displays a dialog box that contains Yes, OK, and Cancel buttons. If the user clicks the Yes button, DestroyWindow is called; otherwise, the window is not destroyed. Because the window being destroyed is a main window, the example calls PostQuitMessage in response to WM_DESTROY.

case WM_CLOSE:

/*

* Create the message box. If the user clicks

* the Yes button, destroy the main window.

*/

if (MessageBox(hwnd, szConfirm, szAppName, B_YESNOCANCEL) == IDYES)

DestroyWindow(hwndMain);

else

return 0;

case WM_DESTROY:

/*

* Post the WM_QUIT message to

* quit the application terminate.

*/

PostQuitMessage(0);

return 0;

+$#AK Window Reference

The following functions and messages are associated with windows.

+$#A Window Functions

Following are the functions to create and manage windows.

AdjustWindowRect
AdjustWindowRectEx
AnyPopup
BeginDeferWindowPos
BringWindowToTop
ChildWindowFromPoint
CloseWindow
CreateWindow
CreateWindowEx
DeferWindowPos
DestroyWindow
EnableWindow
EndDeferWindowPos
EnumChildProc
EnumChildWindows
EnumThreadWindows
EnumThreadWndProc
EnumWindows
EnumWindowsProc
FindWindow
GetClientRect
GetDesktopWindow
GetForegroundWindow
GetLastActivePopup
GetNextWindow
GetParent
GetTopWindow
GetWindow
GetWindowPlacement
GetWindowRect
GetWindowText
GetWindowTextLength
GetWindowThreadProcessId
IsChild
IsWindow
IsWindowUnicode
IsWindowVisible
IsZoomed
MoveWindow
SetForegroundWindow
SetParent
SetWindowLong
SetWindowPlacement
SetWindowPos
SetWindowText
ShowOwnedPopups
ShowWindow
WindowFromPoint
WinMain

Obsolete or Deleted Functions

EnumTaskWindows
GetSysModalWindow
GetWindowTask
SetSysModalWindow

+$#A Window Messages

Following are the messages to create and manage windows.

WM_ACTIVATE
WM_ACTIVATEAPP
WM_CANCELMODE
WM_CHILDACTIVATE
WM_CLOSE
WM_COPYDATA
WM_CREATE
WM_DESTROY
WM_ENABLE
WM_ENDSESSION
WM_GETMINMAXINFO
WM_GETTEXT
WM_MOVE
WM_NCACTIVATE
WM_NCCALCSIZE
WM_NCCREATE
WM_NCDESTROY
WM_PARENTNOTIFY
WM_POWER
WM_QUERYENDSESSION
WM_QUERYOPEN
WM_QUIT
WM_SETTEXT
WM_SHOWWINDOW
WM_SIZE
WM_WINDOWPOSCHANGED
WM_WINDOWPOSCHANGING

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide CHM facilement


Window Procedures.rtf

+$#AK Window Procedures

In Microsoft® Windows®, every window has an associated window procedure ⎯ a function that processes all messages sent or posted to all windows of the class. All aspects of a window's appearance and behavior depend on the window procedure's response to these messages.

+$#A About Window Procedures

Each window is a member of a particular window class. The window class determines the default window procedure that an individual window uses to process its messages. All windows belonging to the same class use the same default window procedure. For example, the system defines a window procedure for the combo box class (COMBOBOX); all combo boxes then use that window procedure.

An application typically registers at least one new window class and an associated window procedure. After registering a class, the application can create many windows of that class, all of which use the same default window procedure. Because this means several sources could simultaneously call the same piece of code, the developer must be careful when modifying shared resources from a window procedure. For more information about window classes, see Window Classes.

Window procedures for dialog boxes (called dialog box procedures) have a similar structure and function as regular window procedures. All points referring to window procedures in this section also apply to dialog box procedures. For more information about dialog boxes, see Dialog Boxes.

+$#A Structure of a Window Procedure

A window procedure is a function that has four parameters and returns a 32-bit signed value. The parameters consist of a window handle, a UINT message identifier, and two message parameters declared with the WPARAM and LPARAM data types. The operating system defines a WPARAM value as a 32-bit unsigned integer and an LPARAM value as a 32-bit signed integer.

The window procedure parameters are described in the following table.

Parameter | Description
hwnd | Handle of the window receiving the message.
uMsg | Message identifier. The identifier corresponds to one of the predefined constants (for example, WM_CREATE) defined in the system header files or to an application-defined message identifier.
wParam, lParam | Message parameters. Their interpretation depends on the value of the uMsg parameter.

Message parameters often contain information in both their low-order and high-order words. The Microsoft® Win32® application programming interface (API) includes several macros an application can use to extract information from the message parameters. The LOWORD macro, for example, extracts the low-order word (bits 0 through 15) from a message parameter. Other macros include HIWORD, LOBYTE, and HIBYTE.

The return value of a window procedure is declared as an LRESULT data type, which is defined as a 32-bit signed value. The interpretation of the return value depends on the particular message. Consult the description of each message to determine the appropriate return value.

Because it is possible to call a window procedure recursively, it is important to minimize the number of local variables that it uses. When processing individual messages, an application should call functions outside the window procedure to avoid excessive use of local variables, possibly causing the stack to overflow during deep recursion.

+$#A Default Window Procedure

The default window procedure function DefWindowProc defines certain fundamental behavior shared by all windows. The default window procedure provides the minimal functionality for a window. An application-defined window procedure should pass any messages that it does not process to the DefWindowProc function for default processing.

+$#A Window Procedure Subclassing

When an application creates a window, the operating system allocates a block of memory for storing information specific to the window, including the address of the window procedure that processes messages for the window. When Windows needs to pass a message to the window, it searches the window-specific information for the address of the window procedure and passes the message to that procedure.

Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. By subclassing a window, an application can augment, modify, or monitor the behavior of the window. Although it is not recommended, an application can subclass any window, including those belonging to a system global class, such as an edit control or a list box. For example, an application could subclass an edit control to prevent the control from accepting certain characters. For an explanation of the risks involved, see the following section.

An application subclasses a window by replacing the address of the window's original window procedure with the address of a new window procedure, called the subclass procedure. Thereafter, the subclass procedure receives any messages sent or posted to the window.

The subclass procedure can take three actions upon receiving a message: it can pass the message to the original window procedure, modify the message and pass it to the original window procedure, or process the message and not pass it to the original window procedure. If the subclass procedure processes a message, it can do so before, after, or both before and after it passes the message to the original window procedure.

Windows provides two types of subclassing: instance and global. In instance subclassing, an application replaces the window procedure address of a single instance of a window. An application must use instance subclassing to subclass an existing window. In global subclassing, an application replaces the address of the window procedure in the WNDCLASS structure of a window class. All subsequent windows created with the class have the address of the subclass procedure, but existing windows of the class are not affected.

+$#A Instance Subclassing

An application subclasses an instance of a window by using the SetWindowLong function. The application passes the GWL_WNDPROC flag, the handle of the window to subclass, and the address of the subclass procedure to SetWindowLong. The subclass procedure can reside in either the application's module or a dynamic-link library (DLL). An application must list the name of the subclass procedure in the EXPORTS statement of the application's or DLL's module-definition (.DEF) file.

SetWindowLong returns the address of the window's original window procedure. The application must save the address, using it in subsequent calls to the CallWindowProc function, to pass intercepted messages to the original window procedure. The application must also have the original window procedure address to remove the subclass from the window. To remove the subclass, the application calls SetWindowLong again, passing the address of the original window procedure with the GWL_WNDPROC flag and the handle of the window.

An application can subclass any window in the system; however, when subclassing a window it does not own, the application must ensure that the subclass procedure does not destroy the original behavior of the window. Because the application does not control the window, it must not depend on information about the window that the owner might change in the future.

An application should not use the extra window bytes or the class bytes for a window without knowing exactly what the bytes mean and how the original window procedure uses them. Even so, the application should not use them unless it owns the window. If the application uses the extra window bytes of a window that another application owns and the owner changes some aspect of the extra bytes, the subclass procedure may fail. For this reason, an application should not subclass a window that belongs to a system global control class. Windows owns the system global classes, and aspects of the controls might change from one version of Windows to the next. If the application must subclass a window that belongs to a system global class, the developer may need to update the application when a new version of Windows is released.

Because instance subclassing occurs after a window is created, the application subclassing the window cannot add any extra bytes to the window. Applications that subclass a window should use the window's property list to store any data needed for an instance of the subclassed window. For more information about window properties, see Window Properties.

When an application subclasses a subclassed window, it must remove the subclasses in the reverse order they were performed. If the removal order is not reversed, an unrecoverable system error may occur.

+$#A Global Subclassing

To globally subclass a window class, the application must have a handle to a window of the class. The application also needs the handle to remove the subclass. To get the handle, an application typically creates a hidden window of the class to be subclassed. After obtaining the handle, the application calls the SetClassLong function, specifying the handle, the GCL_WNDPROC flag, and the address of the subclass procedure. SetClassLong returns the address of the original window procedure for the class.

The original window procedure address is used in global subclassing in the same way it is used in instance subclassing. The subclass procedure passes messages to the original window procedure by calling CallWindowProc. The application removes the subclass from the window class by calling SetClassLong again, specifying the address of the original window procedure, the GCL_WNDPROC flag, and the handle to a window of the class being subclassed. An application that globally subclasses a control class must remove the subclass when the application terminates; otherwise, an unrecoverable system error may occur.

Global subclassing has the same limitations as instance subclassing, plus some additional restrictions. An application should not use the extra bytes for either the class or the window instance without knowing exactly how the original window procedure uses them. If the application must associate data with a window, it should use window properties.

An application must not globally subclass a system global class. An unrecoverable system error may occur if more than one application globally subclasses a control class. If the application could benefit from globally subclassing a control class, use the technique called superclassing.

+$#A Window Procedure Superclassing

Superclassing is a technique that allows an application to create a new window class with the basic functionality of the existing class, plus enhancements provided by the application. A superclass is based on an existing window class called the base class. Frequently, the base class is a system global window class such as an edit control, but it can be any window class.

A superclass has its own window procedure, called the superclass procedure. The superclass procedure can take three actions upon receiving a message: It can pass the message to the original window procedure, modify the message and pass it to the original window procedure, or process the message and not pass it to the original window procedure. If the superclass procedure processes a message, it can do so before, after, or both before and after it passes the message to the original window procedure.

Unlike a subclass procedure, a superclass procedure can process window creation messages (WM_NCCREATE, WM_CREATE, and so on), but it must also pass them to the original base-class window procedure so that the base-class window procedure can perform its initialization procedure.

To superclass a window class, an application first calls the GetClassInfo function to retrieve information about the base class. GetClassInfo fills a WNDCLASS structure with the values from the WNDCLASS structure of the base class. Next, the application copies its own instance handle into the hInstance member of the WNDCLASS structure and copies the name of the superclass into the lpszClassName member. If the base class has a menu, the application must provide a new menu with the same menu identifiers and copy the menu name into the lpszMenuName member. If the superclass procedure processes the WM_COMMAND message and does not pass it to the window procedure of the base class, the menu need not have corresponding identifiers. GetClassInfo does not return the lpszMenuName, lpszClassName, or hInstance member of the WNDCLASS structure.

An application must also set the lpfnWndProc member of the WNDCLASS structure. The GetClassInfo function fills this member with the address of the original window procedure for the class. The application must save this address, to pass messages to the original window procedure, and then copy the address of the superclass procedure into the lpfnWndProc member. The application can, if necessary, modify any other members of the WNDCLASS structure. After it fills the WNDCLASS structure, the application registers the superclass by passing the address of the structure to the RegisterClass function. The superclass can then be used to create windows.

Because superclassing registers a new window class, an application can add to both the extra class bytes and the extra window bytes. The superclass must not use the original extra bytes for the base class or the window for the same reasons that an instance subclass or a global subclass should not use them. Also, if the application adds extra bytes for its use to either the class or the window instance, it must reference the extra bytes relative to the number of extra bytes used by the original base class. Because the number of bytes used by the base class may vary from one version of the base class to the next, the starting offset for the superclass's own extra bytes may also vary from one version of the base class to the next.

+$#A Using Window Procedures

• Designing a window procedure

• Associating a window procedure with a window class

• Subclassing a window

+$#A Designing a Window Procedure

The following example shows the structure of a typical window procedure. The window procedure uses the message argument in a switch statement with individual messages handled by separate case statements. Notice that each case returns a specific value for each message. For messages that it does not process, the window procedure calls the DefWindowProc function.

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd; // handle of window

UINT uMsg; // message identifier

WPARAM wParam; // first message parameter

LPARAM lParam; // second message parameter

{

switch (uMsg) {

case WM_CREATE:

// Initialize the window.

return 0;

case WM_PAINT:

// Paint the window's client area.

return 0;

case WM_SIZE:

// Set the size and position of the window.

return 0;

case WM_DESTROY:

// Clean up window-specific data objects.

return 0;

.

. // Process other messages.

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

The WM_NCCREATE messageis sent just after your window is created, but if an application responds to this message by returning FALSE, CreateWindowEx function fails. The WM_CREATE message is sent after your window is already created.

The WM_DESTROY message is sent when your window is about to be destroyed. The DestroyWindow function takes care of destroying any child windows of the window being destroyed. The WM_NCDESTROY message is sent just before a window is destroyed.

At the very least, a window procedure should process the WM_PAINT message to draw itself. Typically, it should handle mouse and keyboard messages as well. Consult the descriptions of individual messages to determine whether your window procedure should handle them.

Your application can call the DefWindowProc function as part of the processing of a message. In such a case, the application can modify the message parameters before passing the message to DefWindowProc, or it can continue with the default processing after performing its own operations.

A dialog box procedure receives a WM_INITDIALOG message instead of a WM_CREATE message and does not pass unprocessed messages to the DefDlgProc function. Otherwise, a dialog box procedure is exactly the same as a window procedure. For more information about dialog box procedures, see Dialog Boxes.

+$#A Associating a Window Procedure with a Window Class

You associate a window procedure with a window class when registering the class. You must fill a WNDCLASS structure with information about the class, and the lpfnWndProc member must specify the address of the window procedure. To register the class, pass the address of WNDCLASS structure to the RegisterClass function. Once the window class is registered, the window procedure is automatically associated with each new window created with that class.

The following example shows how to associate the window procedure in the previous example with a window class.

int APIENTRY WinMain(hinstance, hinstPrev, lpCmdLine, nCmdShow)

HINSTANCE hinstance; // handle of current instance

HINSTANCE hinstPrev; // handle of previous instance

LPSTR lpCmdLine; // address of command-line string

int nCmdShow; // show-window type

{

WNDCLASS wc;

// Register the main window class.

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hinstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MainMenu";

wc.lpszClassName = "MainWindowClass";

if (!RegisterClass(&wc))

return FALSE;

.

. // Process other messages.

.

}

+$#A Subclassing a Window

To subclass an instance of a window, call the SetWindowLong function and specify the handle of the window to subclass the GWL_WNDPROC flag and a pointer to the subclass procedure. SetWindowLong returns a pointer to the original window procedure; use this pointer to pass messages to the original procedure. The subclass window procedure must use the CallWindowProc function to call the original window procedure.

The following example shows how to subclass an instance of an edit control in a dialog box. The subclass window procedure enables the edit control to receive all keyboard input, including the ENTER and TAB keys, whenever the control has the input focus.

WNDPROC wpOrigEditProc;

LRESULT APIENTRY EditBoxProc(hwndDlg, uMsg, wParam, lParam)

HWND hwndDlg;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

HWND hwndEdit;

switch(uMsg) {

case WM_INITDIALOG:

// Retrieve the handle of the edit control.

hwndEdit = GetDlgItem(hwndDlg, ID_EDIT);

// Subclass the edit control.

wpOrigEditProc = (WNDPROC) SetWindowLong(hwndEdit,

GWL_WNDPROC, (LONG) EditSubclassProc);

.

. // Continue the initialization procedure.

.

return TRUE;

case WM_DESTROY:

// Remove the subclass from the edit control.

SetWindowLong(hwndEdit, GWL_WNDPROC,

(LONG) wpOrigEditProc);

.

. // Continue the cleanup procedure.

.

break;

}

return FALSE;

UNREFERENCED_PARAMETER(lParam);

}

// Subclass procedure

LRESULT APIENTRY EditSubclassProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

if (uMsg == WM_GETDLGCODE)

return DLGC_WANTALLKEYS;

return CallWindowProc(wpOrigEditProc, hwnd, uMsg,

wParam, lParam);

}

+$#A Window Procedure Functions

Following are the functions used with window procedures.

CallWindowProc
DefWindowProc
WindowProc

D

Créé avec HelpNDoc Personal Edition: Produire des livres Kindle gratuitement


Window Properties.rtf

+$#AK Window Properties

A window property is any data assigned to a window by the SetProp function. A window property is usually a handle of the window-specific data, but it may be any 32-bit value. Each window property is identified by a string name.

+$#A About Window Properties

Window properties are typically used to associate data with a subclassed window or a window in a multiple document interface (MDI) application. In either case, it is not convenient to use the extra bytes specified in the CreateWindow function or class structure for the following two reasons:

• An application might not know how many extra bytes are available or how the space is being used. By using window properties, the application can associate data with a window without accessing the extra bytes.

• An application must access the extra bytes by using offsets. Window properties, however, are accessed by their string identifiers, not by offsets.

For more information about subclassing, see Window Procedures. For more information about MDI windows, see Multiple Document Interface.

The Microsoft® Win32® application programming interface (API) provides several functions that enable applications to use window properties. The SetProp function assigns a window property and its string identifier to a window. The GetProp function retrieves the window property identified by a given string. The RemoveProp function destroys the association between a window and a window property but does not destroy the data itself. The EnumProps and EnumPropsEx functions enumerate all of a window's properties by using an application-defined callback function. EnumPropsEx includes an extra parameter for application-defined data used by the callback function.

+$#A Using Window Properties

• Adding a window property

• Retrieving a window property

• Listing window properties for a given window

• Deleting a window property

+$#A Adding a Window Property

The following example loads an icon and then a cursor and allocates memory for a buffer. The example then uses the SetProp function to assign the resulting icon, cursor, and memory handles as window properties for the window identified by the application-defined hwndSubclass variable. The properties are identified by the strings PROP_ICON, PROP_CURSOR, and PROP_BUFFER.

#define BUFFER 4096

HINSTANCE hinst; /* handle of current instance */

HWND hwndSubclass; /* handle of a subclassed window */

HANDLE hIcon, hCursor;

HGLOBAL hMem;

char *lpMem;

TCHAR tchPath[] = "c:\\winnt\\samples\\winprop.c";

/* Load resources. */

hIcon = LoadIcon(hinst, MAKEINTRESOURCE(400));

hCursor = LoadCursor(hinst, MAKEINTRESOURCE(220));

/* Allocate and fill a memory buffer. */

hMem = GlobalAlloc(GPTR, BUFFER);

lpMem = GlobalLock(hMem);

lstrcpy(lpMem, tchPath);

GlobalUnlock(hMem);

/* Set the window properties for hwndSubclass. */

SetProp(hwndSubclass, "PROP_ICON", hIcon);

SetProp(hwndSubclass, "PROP_CURSOR", hCursor);

SetProp(hwndSubclass, "PROP_BUFFER", hMem);

+$#A Retrieving a Window Property

A window can create handles to its window property data and use the data for any purpose. The following example uses GetProp to obtain handles to the window properties identified by PROP_ICON, PROP_CURSOR, and PROP_BUFFER. The example then displays the contents of the newly obtained memory buffer, cursor, and icon in the window's client area.

#define PATHLENGTH 256

HWND hwndSubclass; /* handle of a subclassed window */

HANDLE hIconProp, hCursProp;

HGLOBAL hMemProp;

char *lpFilename;

TCHAR tchBuffer[PATHLENGTH];

int nSize;

HDC hdc;

/* Get the window properties, then use the data. */

hIconProp = (HICON) GetProp(hwndSubclass, "PROP_ICON");

TextOut(hdc, 10, 40, "PROP_ICON", 9);

DrawIcon(hdc, 90, 40, hIconProp);

hCursProp = (HCURSOR) GetProp(hwndSubclass, "PROP_CURSOR");

TextOut(hdc, 10, 85, "PROP_CURSOR", 9);

DrawIcon(hdc, 110, 85, hCursProp);

hMemProp = (HGLOBAL) GetProp(hwndSubclass, "PROP_BUFFER");

lpFilename = GlobalLock(hMemProp);

nSize = sprintf(tchBuffer,

"Path to file: %s", lpFilename);

TextOut(hdc, 10, 10, tchBuffer, nSize);

+$#A Listing Window Properties for a Given Window

In the following example, the EnumPropsEx function lists the string identifiers of the window properties for the window identified by the application-defined hwndSubclass variable. This function relies on the application-defined callback function WinPropProc to display the strings in the window's client area.

EnumPropsEx(hwndSubclass, WinPropProc, NULL);

/*

* WinPropProc is an application-defined callback function

* that lists a window property.

*/

BOOL CALLBACK WinPropProc(

HWND hwndSubclass, /* handle of window with property */

LPCSTR lpszString, /* property string or atom */

HANDLE hData) /* data handle */

{

static int nProp = 1; /* property counter */

TCHAR tchBuffer[BUFFER]; /* expanded-string buffer */

int nSize; /* size of string in buffer */

HDC hdc; /* device-context handle */

hdc = GetDC(hwndSubclass);

/* Display window property string in client area. */

nSize = sprintf(tchBuffer, "WinProp %d: %s", nProp++,

lpszString);

TextOut(hdc, 10, nProp * 20, tchBuffer, nSize);

ReleaseDC(hwndSubclass, hdc);

return TRUE;

}

+$#A Deleting a Window Property

When a window is destroyed, it must destroy any window properties it set. The following example uses the EnumPropsEx function and the application-defined callback function DelPropProc to destroy the properties associated with the window identified by the application-defined hwndSubclass variable. The callback function, which uses the RemoveProp function, is also shown.

case WM_DESTROY:

EnumPropsEx(hwndSubclass, DelPropProc, NULL);

PostQuitMessage(0);

break;

.

.

.

/*

* DelPropProc is an application-defined callback function

* that deletes a window property.

*/

BOOL CALLBACK DelPropProc(

HWND hwndSubclass, /* handle of window with property */

LPCSTR lpszString, /* property string or atom */

HANDLE hData) /* data handle */

{

RemoveProp(hwndSubclass, lpszString);

return TRUE;

}

+$#A Window Property Functions

Following are the functions used with window properties.

EnumProps
EnumPropsEx
GetProp
PropEnumProc
PropEnumProcEx
RemoveProp
SetProp

Créé avec HelpNDoc Personal Edition: Générer des livres électroniques EPub facilement


Window Stations and Desktops.rtf

+$#AK Window Stations and Desktops

This overview describes the new window station and desktop functionality, and is primarily intended for developers of Win32 services. Developers of typical applications that will be run by the logged-on user do not need to be concerned about window stations and desktops.

Beginning with Microsoft® Windows NT™ version 3.5, the Win32® API provides new window station and desktop functionality that enables Win32 services to call USER32 and GDI32 functions regardless of the logon account in which the service is running. This new functionality enables Win32 services to make these calls while maintaining the security of applications running in the account of the logged-on interactive user.

For more information on how Win32 services use the window station and desktop support, see Interactive Services.

+$#A Window Stations

A window station is a secure object that contains a clipboard, a set of global atoms and a group of desktop objects. The interactive window station assigned to the logon session of the interactive user also contains the keyboard, mouse, and display device. The interactive window station is visible to the user and can receive input from the user. All other window stations are noninteractive, which means that they cannot be made visible to the user, and cannot receive user input.

Applications can use the following functions for manipulating window station objects:

Function | Description
CloseWindowStation | Closes a specified window station.
CreateWindowStation | Creates a new window station.
EnumWindowStations | Enumerates the window stations in the system by repeatedly calling an application-defined EnumWindowStationProc callback function.
GetProcessWindowStation | Returns a handle of the window station assigned to the calling process.
GetUserObjectInformation | Gets information about a window station or desktop object.
GetUserObjectSecurity | Gets security information for a window station or desktop object.
OpenWindowStation | Opens a handle of an existing window station.
SetProcessWindowStation | Assigns a specified window station to the calling process.
SetUserObjectInformation | Sets information about a window station or desktop object.
SetUserObjectSecurity | Sets security information for a window station or desktop object.

+$#A Desktops

A desktop is a secure object contained within a window station. A desktop has a logical display surface and contains windows, menus, and hooks. A window station can have multiple desktops. Only the desktops of the interactive window station can be visible and receive user input. On the interactive window station, only one desktop at a time is active. This active desktop, also known as the input desktop, is the one that is currently visible to the user and that receives user input. Applications can use the OpenInputDesktop function to get a handle of the input desktop. Applications that have the necessary access can use the SwitchDesktop function to specify a different input desktop.

Invisible, noninteractive window stations enable Win32 services that do not have access to the interactive window station to start GUI applications. Applications running in a noninteractive window station cannot receive input or be visible. For example, the schedule service could use this support to start a GUI backup application on a remote computer on which the interactive window station was not accessible. For more information, see Interactive Services.

Applications can use the following functions for manipulating desktop objects:

Function | Description
CloseDesktop | Closes a specified desktop.
CreateDesktop | Creates a new desktop on a specified window station.
EnumDesktops | Enumerates the desktops on a specified window station by repeatedly calling an application-defined EnumDesktopProc callback function.
EnumDesktopWindows | Enumerates the windows on a specified desktop by repeatedly calling an application-defined EnumWindowsProc callback function.
GetThreadDesktop | Returns a handle of the desktop assigned to the calling thread.
GetUserObjectInformation | Gets information about a window station or desktop object.
GetUserObjectSecurity | Gets security information for a window station or desktop object.
OpenDesktop | Opens a handle of an existing desktop.
SetThreadDesktop | Assigns a specified desktop to the calling thread.
SetUserObjectInformation | Sets information about a window station or desktop object.
SetUserObjectSecurity | Sets security information for a window station or desktop object.

+$#A Window Station and Desktop Creation

The system automatically creates the interactive window station. When an interactive user logs on, the system associates the interactive window station with the user's logon session. The system also creates the default input desktop for the interactive window station.

When a noninteractive process such as a Win32 service attempts to connect to Win32 and no window station exists for the process' logon session, Win32 attempts to create a window station and desktop for the session. The name of the created window station is based on the logon session identifier, and the desktop is named "Default." The discretionary access control lists (DACLs) for the window station and desktop grant the following accesses to the service's user account:

Window Station: WINSTA_READATTRIBUTES | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | STANDARD_RIGHTS_REQUIRED

Desktop: DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_ENUMERATE | STANDARD_RIGHTS_REQUIRED

A process can use the CreateWindowStation function to create a new window station, and the CreateDesktop function to create a new desktop.

+$#A Process Connection to a Window Station

A process automatically establishes a connection to a window station and desktop when it first calls a USER32 or GDI32 function (other than the window station or desktop functions) that must go to the Win32 server. The Win32 server determines the window station to which a process connects according to the following rules:

1. If the process has called the SetProcessWindowStation function, it connects to the window station specified in that call.

2. If the process did not call SetProcessWindowStation, it connects to the window station inherited from the parent process.

3. If the process did not call SetProcessWindowStation and did not inherit a window station, the Win32 server attempts to open for MAXIMUM_ACCESS and connect to a window station as follows:

a. If a window station name was specified in the lpDesktop member of the STARTUPINFO structure that was passed to the CreateProcess function when the process was created, the process connects to the specified window station.

b. Otherwise, if the process is running in the logon session of the interactive user, the process connects to the interactive window station.

c. If the process is running in a noninteractive logon session, the window station name is formed based on the logon session identifier and an attempt is made to open that window station. If the open operation fails because this window station does not exist, Win32 tries to create the window station and a default desktop.

+$#A Thread Connection to a Desktop

After a process connects to a window station, the Win32 server assigns a desktop to the thread making the connection to Win32. The desktop assigned during this connection process cannot be closed by calling the CloseDesktop function. The Win32 server determines the desktop to assign to the thread according to the following rules:

1. If the thread has called the SetThreadDesktop function, it connects to the specified desktop.

2. If the thread did not call SetThreadDesktop, it connects to the desktop inherited from the parent process.

3. If the thread did not call SetThreadDesktop and did not inherit a desktop, the Win32 server attempts to open for MAXIMUM_ACCESS and connect to a desktop as follows:

a. If a desktop name was specified in the lpDesktop member of the STARTUPINFO structure that was passed to the CreateProcess function when the process was created, the thread connects to the specified desktop.

b. Otherwise, the thread connects to the default desktop of the window station to which the process connected.

The desktop assigned during this connection process cannot be closed by calling the CloseDesktop function.

+$#A Inherited Handles

When a process is connecting to a window station or desktop, the Win32 server searches the process's handle table for inherited handles. The Win32 server uses the first window station and desktop handles that it finds. If you want a child process to connect to a particular inherited window station or desktop, you must ensure that the only the desired handles are marked inheritable. If a child process inherits multiple window station or multiple desktop handles, the results of the window station and desktop connection are undefined.

Handles to a window station and desktop that the Win32 server opens while connecting a process to a window station or desktop are not inheritable.

+$#A Logoff Events

A logoff event occurs when a process calls the ExitWindows or ExitWindowsEx function. The logoff event is identified by the window station of the process calling ExitWindowsEx.

When a logoff event occurs, the system notifies all processes in the system as follows:

• All windows of processes in the logon session being logged off receive the WM_QUERYENDSESSION and WM_ENDSESSION messages. All windows on the desktops of the window station being logged off receive these messages, even if the windows belong to processes running in another logon session.

• Every process in the system receives the CTRL_LOGOFF_EVENT control signal. This signal does not indicate which session is being logged off. The system sends this signal after sending the window messages. For more information on control signals, see the SetConsoleCtrlHandler function.

If the process that called ExitWindowsEx is running in the logon session of the interactive user, all processes in the logon session are terminated. If the process calling ExitWindowsEx is in some other logon session, only the notifications are made; no processes are terminated.

+$#A Using Window Stations and Desktops

• Creating an interactive process

• Interacting with the user by a win32 service

• Handling logoff events

+$#A Creating an Interactive Process

The following code fragment shows how a Win32 service process could use the STARTUPINFO structure in a CreateProcess call to create a process that has access to the user's interactive window station and default desktop. A noninteractive Win32 service could use this technique to interact with the logged on user. The new process could then use a named pipe or some other means of interprocess communication to communicate with the Win32 service.

To create an interactive process as shown in this example, a Win32 service must be logged in to the LocalSystem account.

#include <windows.h>

STARTUPINFO si;

PROCESS_INFORMATION ProcessInformation;

si.cb = sizeof(STARTUPINFO);

si.lpReserved = NULL;

si.lpTitle = NULL;

si.lpDesktop = "WinSta0\\Default";

si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;

si.dwFlags = 0;;

si.wShowWindow = SW_SHOW;

si.lpReserved2 = NULL;

si.cbReserved2 = 0;

if (CreateProcess(NULL, lpszCmdLine, NULL, NULL, FALSE,

0, NULL, NULL, &si, &ProcessInformation)) {

CloseHandle(ProcessInformation.hProcess);

CloseHandle(ProcessInformation.hThread);

}

+$#A Interacting with the User by a Win32 Service

For a noninteractive Win32 service to interact with the user, it must open the user's window station ("WinSta0") and desktop ("Default"). By default, only the logged-on user and Win 32 services running in the LocalSystem account are granted access to the user's window station and desktop. This means that services running in other accounts must either impersonate the user when opening the interactive window station and desktop, or have access granted to those accounts by the user.

For compatibility with future versions of Windows NT that may support multiple users and/or multiple desktops per user, the user's window station and desktop names should not be hard-coded. Client applications should pass the names of their window station and desktop to the service as part of the request to the service.

This code sample displays a message on the user's desktop in response to an RPC request from one of the user's applications. Note that a global variable dwGuiThreadId in this example is also used in the logoff handling example. If the thread that displays the message box terminates after it is done with the message box, it is not necessary to save and restore the thread's desktop. It is necessary to restore the process's window station.

DWORD dwGuiThreadId = 0;

int

UserMessageBox(

RPC_BINDING_HANDLE h,

LPSTR lpszWindowStation,

LPSTR lpszDesktop,

LPSTR lpszText,

LPSTR lpszTitle,

UINT fuStyle)

{

DWORD dwThreadId;

HWINSTA hwinstaSave;

HDESK hdeskSave;

HWINSTA hwinstaUser;

HDESK hdeskUser;

int result;

/*

* Ensure connection to service window station and desktop, and

* save their handles.

*/

GetDesktopWindow();

hwinstaSave = GetProcessWindowStation();

dwThreadId = GetCurrentThreadId();

hdeskSave = GetThreadDesktop(dwThreadId);

/*

* Impersonate the client and connect to the User's

* window station and desktop.

*/

RpcImpersonateClient(h);

hwinstaUser = OpenWindowStation(lpszWindowStation, FALSE, MAXIMUM_ALLOWED);

if (hwinstaUser == NULL) {

RpcRevertToSelf();

return 0;

}

SetProcessWindowStation(hwinstaUser);

hdeskUser = OpenDesktop(lpszDesktop, 0, FALSE, MAXIMUM_ALLOWED);

RpcRevertToSelf();

if (hdeskUser == NULL) {

SetProcessWindowStation(hwinstaSave);

CloseWindowStation(hwinstaUser);

return 0;

}

SetThreadDesktop(hdeskUser);

/*

* Display message box.

*/

dwGuiThreadId = dwThreadId;

result = MessageBox(NULL, lpszText, lpszTitle, fuStyle);

dwGuiThreadId = 0;

/*

* Restore window station and desktop.

*/

SetThreadDesktop(hdeskSave);

SetProcessWindowStation(hwinstaSave);

CloseDesktop(hdeskUser);

CloseWindowStation(hwinstaUser);

return result;

}

+$#A Handling Logoff Events

Win32 services that interact with the user should be prepared to handle logoff events. When a logoff event occurs, the Win32 service must close all handles to the user's window station and desktop.

This sample demonstrates how the message box in the interaction example code should be dismissed at logoff. The ConsoleCtrlHandler function in this example is a HandlerRoutine that was specified by a call to the SetConsoleCtrlHandler function.

BOOL CALLBACK EnumProc(

HWND hwnd,

LPARAM lParam)

{

/*

* Send a WM_CLOSE to destroy the window, because DestroyWindow does

* not work across threads.

*/

SendMessage(hwnd, WM_CLOSE, 0, 0);

return TRUE;

}

BOOL ConsoleCtrlHandler(

DWORD dwCtrlType)

{

if (dwCtrlType == CTRL_LOGOFF_EVENT && dwGuiThreadId != 0) {

SetThreadDesktop(GetThreadDesktop(dwGuiThreadId));

EnumThreadWindows(dwGuiThreadId, EnumProc, 0);

}

return FALSE;

}

+$#AK Window Station and Desktop Reference

The following functions and structures are associated with window station and desktop features.

+$#A Window Station and Desktop Functions

Use the following functions to implement window station and desktop features:

CloseDesktop
CloseWindowStation
CreateDesktop
CreateWindowStation
EnumDesktops
EnumDesktopWindows
EnumWindowStations
GetProcessWindowStation
GetThreadDesktop
GetUserObjectInformation
GetUserObjectSecurity
OpenDesktop
OpenInputDesktop
OpenWindowStation
SetProcessWindowStation
SetThreadDesktop
SetUserObjectInformation
SetUserObjectSecurity
SwitchDesktop

+$#A Window Station and Desktop Structures

Use the following structure to manipulate window station and desktop objects:

USEROBJECTFLAGS

Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle


Windows Sockets.rtf

+$#AK Windows Sockets

+$#A Introduction to Windows Sockets

+$#A What is Windows Sockets?

The Windows Sockets documentation defines a network programming interface for Microsoft® Windows® based on the "socket" paradigm popularized in the Berkeley Software Distribution (BSD) from the University of California at Berkeley. It encompasses both familiar Berkeley socket style routines and a set of Windows-specific extensions designed to allow the programmer to take advantage of the message-driven nature of Windows.

The Windows Sockets documentation is intended to provide a single application programming interface (API) to which application developers can program and multiple network software vendors can conform. Furthermore, in the context of a particular version of Microsoft Windows, it defines a binary interface (ABI) such that an application written using the Windows Sockets API can work with a conformant protocol implementation from any network software vendor. This specification thus defines the library calls and associated semantics to which an application developer can program and which a network software vendor can implement.

Network software that conforms to this Windows Sockets specification will be considered "Windows Sockets compliant." Suppliers of interfaces that are Windows Sockets compliant shall be referred to as "Windows Sockets Suppliers." To be Windows Sockets compliant, a vendor must implement 100% of this Windows Sockets specification.

Applications capable of operating with any Windows Sockets-compliant protocol implementation will be considered to have a "Windows Sockets Interface" and will be referred to as "Windows Sockets Applications."

This version of the Windows Sockets specification defines and documents the use of the API in conjunction with the Internet Protocol Suite (IPS, generally referred to as TCP/IP). Specifically, all Windows Sockets implementations support both stream (TCP) and datagram (UDP) sockets.

While the use of this API with alternative protocol stacks is not precluded (and is expected to be the subject of future revisions of the specification), such usage is beyond the scope of this version of the specification.

+$#A Berkeley Sockets

The Windows Sockets Specification has been built on the Berkeley Sockets programming model, which is the de facto standard for TCP/IP networking. It is intended to provide a high degree of familiarity for programmers who are used to programming with sockets in UNIX and other environments, and to simplify the task of porting existing sockets-based source code. The Windows Sockets API is consistent with release 4.3 of the Berkeley Software Distribution (4.3BSD).

Portions of the Windows Sockets specification are derived from material that is Copyright © 1982-1986 by the Regents of the University of California. All rights are reserved. The Berkeley Software License Agreement specifies the terms and conditions for redistribution.

+$#A Microsoft Windows and Windows-Specific Extensions

This API is intended to be usable within all implementations and versions of Microsoft Windows from Microsoft Windows version 3.0 onwards. It thus provides for Windows Sockets implementations and Windows Sockets applications in both 16- and 32-bit operating environments.

Windows Sockets makes provisions for multithreaded Windows processes. A process contains one or more threads of execution. In the Windows 3.1 non-multithreaded world, a task corresponds to a process with a single thread. All references to threads in this document refer to actual "threads" in multithreaded Windows environments. In non-multithreaded environments (such as Windows 3.0), use of the term "thread" refers to a Windows process.

The Microsoft Windows extensions included in Windows Sockets are provided to allow application developers to create software that conforms to the Windows programming model. It is expected that this will facilitate the creation of robust and high-performance applications, and will improve the cooperative multitasking of applications within nonpreemptive versions of Windows. With the exception of WSAStartup and WSACleanup, their use is not mandatory.

+$#A The Status of This Specification

Windows Sockets is an independent specification that was created for the benefit of application developers and network vendors and, indirectly, computer users. Discussion of this documentation and suggested improvements continue and are welcomed. Such discussion occurs mainly via the Internet news group alt.winsock.

+$#A Programming with Sockets

+$#A Windows Sockets Stack Installation Checking

To detect the presence of one (or many) Windows Sockets implementations on a system, an application that has been linked with the Windows Sockets Import Library may simply call the WSAStartup function. A more sophisticated application can examine the $PATH environment variable and search for instances of Windows Sockets implementations (WINSOCK.DLL). For each instance, it can issue a LoadLibrary call and use the WSAStartup function to discover implementation-specific data.

This version of the Windows Sockets specification does not attempt to address explicitly the issue of multiple concurrent Windows Sockets implementations. Nothing in the specification should be interpreted as restricting multiple Windows Sockets DLLs from being present and used concurrently by one or more Windows Sockets applications.

For further details of where to obtain Windows Sockets components, see Windows Sockets Components.

+$#A Sockets

The material in this guide is derived from the document "An Advanced 4.3BSD Interprocess Communication Tutorial," by Samuel J. Leffler, Robert S. Fabry, William N. Joy, Phil Lapsley, Steve Miller, and Chris Torek.

+$#A Socket Basic Concepts

The basic building block for communication is the socket. A socket is an endpoint of communication to which a name can be bound. Each socket in use has a type and an associated process. Sockets exist within communication domains. A communication domain is an abstraction introduced to bundle common properties of threads communicating through sockets. Sockets typically exchange data only with sockets in the same domain (it may be possible to cross domain boundaries, but only if some translation process is performed). The Windows Sockets facilities support a single communication domain: the Internet domain, which is used by processes that communicate using the Internet Protocol Suite. (Future versions of this specification may include additional domains.)

Sockets are typed according to the communication properties visible to a user. Applications are presumed to communicate only between sockets of the same type, although there is nothing that prevents communication between sockets of different types should the underlying communication protocols support this.

Two types of sockets are currently available to a user. A stream socket provides for the bidirectional, reliable, sequenced, and unduplicated flow of data without record boundaries.

A datagram socket supports bidirectional flow of data that is not promised to be sequenced, reliable, or unduplicated. That is, a process receiving messages on a datagram socket may find messages duplicated, and possibly, in an order different from the order in which it was sent. An important characteristic of a datagram socket is that record boundaries in data are preserved. Datagram sockets closely model the facilities found in many contemporary packet-switched networks such as Ethernet.

+$#A Client-Server Model

The paradigm most commonly used in constructing distributed applications is the client-server model. In this scheme, client applications request services from a server application. This implies an asymmetry in establishing communication between the client and server.

The client and server require a well-known set of conventions before service may be rendered (and accepted). These conventions constitute a protocol that must be implemented at both ends of a connection. Depending on the situation, the protocol may be symmetric or asymmetric. In a symmetric protocol, either side may play the master or slave roles. In an asymmetric protocol, one side is immutably recognized as the master, with the other as the slave. An example of a symmetric protocol is the TELNET protocol used in the Internet for remote terminal emulation. An example of an asymmetric protocol is the Internet file transfer protocol, FTP. No matter whether the specific protocol used in obtaining a service is symmetric or asymmetric, when accessing a service there is a "client process" and a "server process."

A server application typically listens at a well-known address for service requests. That is, the server process remains dormant until a connection is requested by a client's connection to the server's address. At such a time, the server process "wakes up" and services the client, performing whatever appropriate actions the client requests of it. While connection-based services are the norm, some services are based on the use of datagram sockets.

+$#A Out-of-Band Data

Note The following discussion of out-of-band data, also referred to as TCP Urgent data, follows the model used in the Berkeley software distribution. Users and implementors should be aware of the fact that there are at present two conflicting interpretations of RFC 793 (in which the concept is introduced), and that the implementation of out-of-band data in the Berkeley Software Distribution does not conform to the Host Requirements laid down in RFC 1122. To minimize interoperability problems, application developers are advised not to use out-of-band data unless this is required in order to interoperate with an existing service. Windows Sockets suppliers are urged to document the out-of-band semantics (BSD or RFC 1122) that their product implements. It is beyond the scope of this specification to mandate a particular set of semantics for out-of-band data handling.

The stream socket abstraction includes the notion of "out of band" data. Out-of-band data is a logically independent transmission channel associated with each pair of connected stream sockets. Out-of-band data is delivered to the user independently of normal data. The abstraction defines that the out-of-band data facilities must support the reliable delivery of at least one out-of-band message at a time. This message may contain at least one byte of data, and at least one message may be pending delivery to the user at any one time. For communications protocols that support only in-band signaling (that is, the urgent data is delivered in sequence with the normal data), the system typically extracts the data from the normal data stream and stores it separately. This allows users to choose between receiving the urgent data in order and receiving it out of sequence without having to buffer all the intervening data. It is possible to "peek" at out-of-band data.

An application may prefer to process out-of-band data "in-line," as part of the normal data stream. This is achieved by setting the socket option SO_OOBINLINE (see setsockopt). In this case, the application may wish to determine whether any of the unread data is "urgent" (the term usually applied to in-line out-of-band data). To facilitate this, the Windows Sockets implementation will maintain a logical "mark" in the data stream to indicate the point at which the out-of-band data was sent. An application can use the SIOCATMARK ioctlsocket command to determine whether there is any unread data preceding the mark. For example, the application might use this to resynchronize with its peer by ensuring that all data up to the mark in the data stream is discarded when appropriate.

The WSAAsyncSelect function is particularly well suited to handling notification of the presence of out-of-band data.

+$#A Broadcasting

By using a datagram socket, it is possible to send broadcast packets on many networks supported by the system. The network itself must support broadcast: the system provides no simulation of broadcast in software. Broadcast messages can place a high load on a network, since they force every host on the network to service them. Consequently, the ability to send broadcast packets has been limited to sockets that are explicitly marked as allowing broadcasting. Broadcast is typically used for one of two reasons: it is needed to find a resource on a local network without prior knowledge of its address, or important functions such as routing require that information be sent to all accessible neighbors.

The destination address of the message to be broadcast depends on the network(s) on which the message is to be broadcast. The Internet domain supports a shorthand notation for broadcast on the local network, the address INADDR_BROADCAST. Received broadcast messages contain the sender's address and port, because datagram sockets must be bound before use.

Some types of network support the notion of different types of broadcast. For example, the IEEE 802.5 token ring architecture supports the use of link-level broadcast indicators, which control whether broadcasts are forwarded by bridges. The Windows Sockets specification does not provide any mechanism whereby an application can determine the type of underlying network, nor any way to control the semantics of broadcasting.

+$#A Byte Ordering

The Intel byte ordering is like that of the DEC VAX™, and therefore differs from the Internet and 68000-type processor byte ordering. Thus, care must be taken to ensure correct orientation.

Any reference to IP addresses or port numbers passed to or from a Windows Sockets function must be in network order. This includes the IP address and port fields of a struct sockaddr_in (but not the sin_family field).

Consider an application that typically contacts a server on the TCP port corresponding to the "time" service, but provides a mechanism for the user to specify that an alternative port is to be used. The port number returned by getservbyname is already in network order, which is the format required when constructing an address, so no translation is required. However, if the user elects to use a different port, entered as an integer, the application must convert this from host order to network order (using the htons function) before using it to construct an address. Conversely, if the application is to display the number of the port within an address (returned using, for example, getpeername), the port number must be converted from network order to host order (using ntohs) before it can be displayed.

Since the Intel and Internet byte orders are different, the conversions described preceding are unavoidable. Application writers are cautioned that they should use the standard conversion functions provided as part of the Windows Sockets API rather than writing their own conversion code, since future implementations of Windows Sockets are likely to run on systems for which the host order is identical to the network byte order. Only applications that use the standard conversion functions are likely to be portable.

+$#A Socket Options

The socket options supported by Windows Sockets are listed in setsockopt and getsockopt. A Windows Sockets implementation must recognize all of these options, and (for getsockopt) return plausible values for each. The default value for each option is shown in the following table.

Value | Type | Meaning | Default | Note
SO_ACCEPTCONN | BOOL | Socket is in listen mode. | FALSE unless a listen has been performed |
SO_BROADCAST | BOOL | Socket is configured for the transmission of broadcast messages. | FALSE |
SO_DEBUG | BOOL | Debugging is enabled. | FALSE | (i)
SO_DONTLINGER | BOOL | If true, the SO_LINGER option is disabled. | TRUE |
SO_DONTROUTE | BOOL | Routing is disabled. | FALSE | (i)
SO_ERROR | int | Retrieve error status and clear. | 0 |
SO_KEEPALIVE | BOOL | Keepalive messages are being sent. | FALSE |
SO_LINGER | struct linger FAR * | Returns the current linger options. | l_onoff is 0 |
SO_OOBINLINE | BOOL | Out-of-band data is being received in the normal data stream. | FALSE |
SO_RCVBUF | int | Buffer size for recv operations. | Implementation dependent | (i)
SO_REUSEADDR | BOOL | The address to which this socket is bound can be used by others. | FALSE |
SO_SNDBUF | int | Buffer size for send operations. | Implementation dependent | (i)
SO_TYPE | int | The type of the socket (for example, SOCK_STREAM). | As created using socket |
TCP_NODELAY | BOOL | Disables the Nagle algorithm for send coalescing. | Implementation dependent |

Notes:

(i) An implementation may silently ignore this option on setsockopt and return a constant value for getsockopt, or it may accept a value for setsockopt and return the corresponding value in getsockopt without using the value in any way.

+$#A Database Files

The getXbyY and WSAAsyncGetXByY classes of routines are provided for retrieving network-specific information. (This specification uses the function name getXbyY to represent the set of routines gethostbyaddr, gethostbyname, and so on. Similarly WSAAsyncGetXByY represents WSAAsyncGetHostByAddr, and so on.) The getXbyY routines were originally designed (in the first Berkeley UNIX releases) as mechanisms for looking up information in text databases. Although the information may be retrieved by the Windows Sockets implementation in different ways, a Windows Sockets application requests such information in a consistent manner through either the getXbyY or the WSAAsyncGetXByY class of routines.

+$#A Deviation from Berkeley Sockets

There are a few limited instances in which the Windows Sockets API has had to divert from strict adherence to the Berkeley conventions, usually because of difficulties of implementation in a Windows environment.

+$#A Socket Data Type and Error Values

A new data type, SOCKET, has been defined. The definition of this type was necessary for future enhancements to the Windows Sockets specification, such as being able to use sockets as file handles in Windows NT. Definition of this type also facilitates porting applications to a Win32 environment, because the type will automatically be promoted from 16 to 32 bits.

In UNIX, all handles, including socket handles, are small, non-negative integers, and some applications make assumptions that this will be true. Windows Sockets handles have no restrictions, other than that the value INVALID_SOCKET is not a valid socket. Socket handles may take any value in the range 0 to INVALID_SOCKET − 1.

Because the SOCKET type is unsigned, compiling existing source code from, for example, a UNIX environment may lead to compiler warnings about signed/unsigned data type mismatches.

This means, for example, that checking for errors when the socket and accept routines return should not be done by comparing the return value with − 1, or seeing if the value is negative (both approaches are common and legal in BSD). Instead, an application should use the manifest constant INVALID_SOCKET as defined in WINSOCK.H. For example:

TYPICAL BSD STYLE:

s = socket(...);

if (s == -1) /* or s < 0 */

{...}

PREFERRED STYLE:

s = socket(...);

if (s == INVALID_SOCKET)

{...}

+$#A select and FD_XXX

Because a SOCKET is no longer represented by the UNIX-style "small non-negative integer," the implementation of the select function was changed in the Windows Sockets API. Each set of sockets is still represented by the fd_set type, but instead of being stored as a bitmask the set is implemented as an array of SOCKETs. To avoid potential problems, applications must adhere to the use of the FD_XXX macros to set, initialize, clear, and check the fd_set structures.

+$#A Error Codes - errno, h_errno & WSAGetLastError

Error codes set by the Windows Sockets implementation are not made available via the errno variable. Additionally, for the getXbyY class of functions, error codes are NOT made available via the h_errno variable. Instead, error codes are accessed by using the WSAGetLastError . This function is provided in Windows Sockets as a precursor (and eventually an alias) for the Win32 function GetLastError. This is intended to provide a reliable way for a thread in a multithreaded process to obtain per-thread error information.

For compatibility with BSD, an application may choose to include a line of the form:

#define errno WSAGetLastError

This will allow networking code that was written to use the global errno to work correctly in a single-threaded environment. There are, obviously, some drawbacks. If a source file includes code that inspects errno for both socket and nonsocket functions, this mechanism cannot be used. Furthermore, it is not possible for an application to assign a new value to errno. (In Windows Sockets the function WSASetLastError can be used for this purpose.)

TYPICAL BSD STYLE:

r = recv(...);

if (r == -1

&& errno == EWOULDBLOCK)

{...}

PREFERRED STYLE:

r = recv(...);

if (r == -1 /* (but see below) */

&& WSAGetLastError == EWOULDBLOCK)

{...}

Although error constants consistent with 4.3 Berkeley Sockets are provided for compatibility purposes, applications should, where possible, use the "WSA" error code definitions. For example, a more accurate version of the preceding source code fragment is:

r = recv(...);

if (r == -1 /* (but see below) */

&& WSAGetLastError == WSAEWOULDBLOCK)

{...}

+$#A Pointers

All pointers used by applications with Windows Sockets should be FAR. To facilitate this, data type definitions such as LPHOSTENT are provided.

+$#A Renamed Functions

In two cases it was necessary to rename functions that are used in Berkeley Sockets to avoid clashes with other functions.

#close & closesocket

In Berkeley Sockets, sockets are represented by standard file descriptors, and so the close function can be used to close sockets as well as regular files. While nothing in Windows Sockets prevents an implementation from using regular file handles to identify sockets, nothing requires it either. Socket descriptors are not presumed to correspond to regular file handles, and file operations such as read, write, and close cannot be assumed to work correctly when applied to sockets. Sockets must be closed by using the closesocket function. Using the close function to close a socket is incorrect and the effects of doing so are undefined by this specification.

#ioctl & ioctlsocket

Various C-language run-time systems use the ioctl function for purposes unrelated to Windows Sockets. For this reason we have defined the function ioctlsocket, which is used to handle socket functions that in the Berkeley Software Distribution are performed using ioctl and fcntl.

+$#A Blocking Routines and EINPROGRESS

Although blocking operations on sockets are supported under Windows Sockets, their use is strongly discouraged. Programmers who are constrained to use blocking mode ⎯ for example, as part of an existing application that is to be ported ⎯ should be aware of the semantics of blocking operations in Windows Sockets. For more details, see Blocking/Nonblocking and Data Volatility.

+$#A Maximum Number of Sockets Supported

The maximum number of sockets supported by a particular Windows Sockets supplier is implementation specific. An application should make no assumptions about the availability of a certain number of sockets. This topic is addressed further in WSAStartup. However, the number of sockets supported by a particular implementation is independent of the issue of the maximum number of sockets an application can actually make use of.

The maximum number of sockets a Windows Sockets application can make use of is determined at compile time by the manifest constant FD_SETSIZE. This value is used in constructing the fd_set structures used in select. The default value in WINSOCK.H is 64. If an application is designed to be capable of working with more than 64 sockets, the implementor should define the manifest FD_SETSIZE in every source file before including WINSOCK.H. One way of doing this is to include the definition within the compiler options in the makefile, for example adding -DFD_SETSIZE=128 as an option to the compiler command line for Microsoft C. It must be emphasized that defining FD_SETSIZE as a particular value has no effect on the actual number of sockets provided by a Windows Sockets implementation.

+$#A Include Files

For ease of portability of existing Berkeley sockets based source code, a number of standard Berkeley include files are supported. However, these Berkeley header files merely include the WINSOCK.H include file, and it is therefore sufficient (and recommended) that Windows Sockets application source files should simply include WINSOCK.H.

+$#A Return Values on Function Failure

The manifest constant SOCKET_ERROR is provided for checking function failure. Although use of this constant is not mandatory, it is recommended. The following example illustrates the use of the SOCKET_ERROR constant:

TYPICAL BSD STYLE:

r = recv(...);

if (r == -1 /* or r < 0 */

&& errno == EWOULDBLOCK)

{...}

PREFERRED STYLE:

r = recv(...);

if (r == SOCKET_ERROR && WSAGetLastError == WSAEWOULDBLOCK)

{...}

+$#A Raw Sockets

The Windows Sockets specification does not mandate that a Windows Sockets DLL support raw sockets, that is, sockets opened with SOCK_RAW. However, a Windows Sockets DLL is allowed and encouraged to supply raw socket support. A Windows Sockets-compliant application that needs to use raw sockets should attempt to open the socket with the socket call, and if it fails either attempt to use another socket type or indicate the failure to the user.

+$#A Windows Sockets in Multithreaded Versions of Windows

The Windows Sockets interface is designed to work for both single-threaded versions of Windows (such as Windows 3.1) and preemptive multithreaded versions of Windows (such as Windows NT). In a multithreaded environment the sockets interface is basically the same, but the author of a multithreaded application must be aware that it is the responsibility of the application, not the Windows Sockets implementation, to synchronize access to a socket between threads. This is the same rule that applies to other forms of I/O such as file I/O. Failure to synchronize calls on a socket leads to unpredictable results; for example if there are two simultaneous calls to send, there is no guarantee as to the order in which the data will be sent.

Closing a socket in one thread that has an outstanding blocking call on the same socket in another thread will cause the blocking call to fail with WSAEINTR, just as if the operation were canceled. This also applies if there is a select call outstanding and the application closes one of the sockets being selected.

There is no default blocking hook installed in preemptive multithreaded versions of Windows. This is because the computer will not be blocked if a single application is waiting for an operation to complete and hence not calling PeekMessage or GetMessage, which cause the application to yield in nonpreemptive Windows. However, for backwards compatibility, the WSASetBlockingHook call is implemented in multithreaded versions of Windows, and applications whose behavior depends on the default blocking hook can install their own blocking hook that duplicates the default hook's semantics.

Créé avec HelpNDoc Personal Edition: Produire des livres EPub gratuitement


Accessibility.rtf

+$#AK Accessibility

The Microsoft® Win32® API provides a set of accessibility features that make it easier for persons with disabilities to use computers. This overview describes the provided accessibility features and the Win32 functions and structures for controlling these features.

+$#A About Accessibility

There are two categories of accessibility features:

• Accessibility parameters. These are system parameters that can be set by the user. Applications can check the state of an accessibility parameter to determine whether the user wants special behavior that can be provided in an application-specific manner. For example, the ShowSounds parameter indicates that an application that typically uses sound to convey important information should also provide the information visually.

• Built-in accessibility features. These features are built into the system or provided as an extension to the system. They affect how the user provides keyboard and mouse input to the computer. When enabled, their functionality is available regardless of which applications are running. An example is a keyboard filter that makes it easier for users with movement impairments to type key combinations such as CTRL+ALT+DEL.

+$#A Accessibility Parameters

The system maintains a set of accessibility parameters that indicate whether the user has special needs or preferences. The user controls the state of these parameters, typically using a Control Panel application. Control Panel applications or other programs that allow the user to customize the Windows environment can use the SystemParametersInfo function to set the accessibility parameters.

Applications should use the GetSystemMetrics function to determine the state of the accessibility parameters. When an accessibility parameter is set to TRUE, the application should modify its user interface, if necessary, to accomodate the user's preferences.

Win32 supports the following accessibility parameter:

Parameter | Description
ShowSounds | Indicates that an application that typically uses sound to convey important information should provide the information visually.

+$#A Built-in Accessibility Features

The built-in accessibility features are extensions to the operating system. These features affect the behavior of the system regardless of which applications are running.

Win32 supports the following built-in accessibility features:

Feature | Description
AccessTimeout | Enables a user to specify a timeout interval after which system-wide accessibility features are automatically disabled. The AccessTimeout feature is intended for computers that are shared by several users with different preferences. Each individual can use hot keys or the Control Panel to enable preferred features. After a user leaves, the features he or she enabled will be automatically disabled by the timeout. The accessibility features affected by the timeout are FilterKeys, MouseKeys, StickyKeys, and ToggleKeys. The ACCESSTIMEOUT structure defines the parameters for this feature.
FilterKeys | Enables control of keyboard properties, such as the amount of time before a keystroke is accepted as input and the amount of time before a keystroke begins to repeat. The FilterKeys feature also provides sound feedback when a key is pressed and when it is accepted as input. The FILTERKEYS structure defines the parameters for this feature.
MouseKeys | Enables the user to control the mouse pointer using the numeric keypad. The MOUSEKEYS structure defines the parameters for this feature.
SoundSentry | Displays a visual signal when a sound is generated by a Windows-based application or an MS-DOS application running in a window. The SOUNDSENTRY structure defines the parameters for this feature.
StickyKeys | Enables the user to type key combinations, such as CTRL+ALT+DEL, in sequence rather than at the same time. The STICKYKEYS structure defines the parameters for this feature.
ToggleKeys | Provides sound feedback when the user turns on or off the CAPS LOCK, NUM LOCK, or SCROLL LOCK keys. The TOGGLEKEYS structure defines the parameters for this feature.

The SystemParametersInfo function provides the programming interface to the built-in accessibility features. This function is useful in Control Panel applications or other programs that allow the user to customize the Windows environment. The SystemParametersInfo function can perform the following tasks:

• Query the system to determine the availability and current state of a specified built-in accessibility feature

• Enable a feature and specify parameters that control its behavior

• Disable a feature

• Enable a hot key that allows the user to enable or disable a feature

For each of the built-in features, Win32 defines a structure that applications use with SystemParametersInfo to query or set the parameters of the feature.

An application can use SystemParametersInfo to enable or disable a feature directly. For some of the features, SystemParametersInfo can enable a hot key or key combination so the user can toggle the state of a feature. There are no hot keys for the SoundSentry feature. The following accessibility features have hot keys:

Feature | Description
FilterKeys | Hold down the LEFT SHIFT key for eight seconds to toggle the FilterKeys feature on or off. Holding down the LEFT SHIFT key for 12 seconds enables FilterKeys using the most conservative values for FILTERKEYS.iBounceMSec. Holding down the LEFT SHIFT key for 16 seconds enables FilterKeys using the most conservative values for FILTERKEYS.iWaitMSec.
MouseKeys | LEFT ALT+LEFT SHIFT+NUM LOCK
StickyKeys | Press the SHIFT key 5 times
ToggleKeys | Hold down the NUM LOCK key for 5 seconds

+$#A Using Accessibility

• Checking the state of an accessibility parameter.

• Enabling a built-in accessibility feature.

+$#A Checking the State of an Accessibility Parameter

The following code fragment uses the GetSystemMetrics function to check the ShowSounds parameter. If GetSystemMetrics returns TRUE, the application should present all important information in visual form.

BOOL fShowSounds;

fShowSounds = GetSystemMetrics(SM_SHOWSOUNDS);

+$#A Enabling a Built-in Accessibility Feature

The following code fragment uses the SystemParametersInfo function to enable the FilterKeys feature:

FILTERKEYS fk;

BOOL bSuccess;

// fill in members of the FILTERKEYS structure

fk.cbSize = sizeof(FILTERKEYS);

fk.dwFlags = (FKF_FILTERKEYSON | FKF_HOTKEYACTIVE |

FKF_AVAILABLE | FKF_HOTKEYSOUND | FKF_CLICKON);

fk.iWaitMSec = 1000;

fk.iDelayMSec = 1000;

fk.iRepeatMSec = 500;

fk.iBounceMSec = 0;

// call SystemParametersInfo with SPI_SETFILTERKEYS code

bSuccess = SystemParametersInfo(SPI_SETFILTERKEYS, 0, (LPVOID) &fk, 0);

+$#AK Accessibility Reference

The following structures and messages are associated with accessibility features.

+$#A Accessibility Functions

Use the following functions to implement accessibility features:

GetSystemMetrics
SystemParametersInfo

+$#A Accessibility Structures

Use the following structures to implement accessibility features:

ACCESSTIMEOUT
FILTERKEYS
HIGHCONTRAST
MOUSEKEYS
SERIALKEYS
SOUNDSENTRY
STICKYKEYS
TOGGLEKEYS


Atoms.rtf

+$#AK Atoms

In Microsoft® Windows®, an atom table is a system-defined table that stores strings and corresponding identifiers. An application places a string in an atom table and receives a 16-bit integer, called an atom, that can be used to access the string. A string that has been placed in an atom table is called an atom name.

+$#A About Atom Tables

Dynamic data exchange (DDE) applications use an atom table to share item-name and topic-name strings with other applications. Rather than passing actual strings, a DDE application passes atoms to its partner application. The partner uses the atoms to obtain the strings from the atom table.

+$#A Global Atom Table

The Windows global atom table, which can contain up to 37 entries, is available to all applications. When an application places a string in the global atom table, the system generates an atom that is unique throughout the system. Any application that has the atom can obtain the string it identifies by querying the global atom table.

An application that defines a private clipboard-data format or DDE-data format for sharing data with other applications should place the format name in the global atom table. This technique prevents conflicts with the names of formats defined by the system or by other applications, and makes the identifiers (atoms) for the messages or formats available to the other applications.

+$#A Local Atom Tables

An application can use a local atom table to efficiently manage a large number of strings used only within the application. These strings, and the associated atoms, are available only to the application that created the table.

An application requiring the same string in a number of structures can reduce memory usage by using a local atom table. Rather than copying the string into each structure, the application can place the string in the atom table and include the resulting atom in the structures. In this way, a string appears only once in memory but can be used many times in the application.

Applications can also use local atom tables to save time when searching for a particular string. To perform a search, an application need only place the search string in the atom table and compare the resulting atom with the atoms in the relevant structures. Comparing atoms is typically faster than comparing strings.

By default, a local atom table can contain up to 37 entries. However, the size of a local atom table, unlike the size of a global atom table, can be changed. An application uses the InitAtomTable function to accomplish this task. If the application calls InitAtomTable, however, it must do so before calling any other atom-management functions.

+$#A Atom Types

Applications can use two types of atoms: string and integer. The following sections describe these types.

+$#A String Atoms

Applications pass null-terminated strings to atom tables and receive string atoms (16-bit integers) in return. String atoms have the following properties:

• The values of string atoms are in the range 0xC000 through 0xFFFF.

• Case is not significant in searches for an atom name in an atom table. Also, the entire string must match in a search operation; no substring matching is performed.

• The string associated with a string atom can be no more than 255 bytes in size. This limitation applies to all atom functions.

• A reference count is associated with each atom name. The count is incremented each time the atom name is added to the table and decremented each time the atom name is deleted from it. This prevents different users of the same string atom from destroying each other's atom names. When the reference count for an atom name equals zero, the system removes the atom and the atom name from the table.

+$#A Integer Atoms

Integer atoms differ from string atoms in the following ways:

• The values of integer atoms are in the range 0x0001 through 0xBFFF. The values of integer atoms and string atoms do not overlap, so both types of atoms can be used in the same block of code.

• The string representation of an integer atom is #dddd, where the values represented by dddd are decimal digits. Leading zeros are ignored.

• There is no reference count or storage overhead associated with an integer atom.

Several Windows functions accept either string or integer atoms as parameters. When passing an integer atom to these functions, an application must use the MAKEINTATOM macro to create the atom.

+$#A Atom Creation and Usage Count

An application creates a local atom by calling the AddAtom function; it creates a global atom by calling the GlobalAddAtom function. Both functions require a pointer to a string. The system searches the appropriate atom table for the string and returns the corresponding atom to the application. In the case of a string atom, if the string already resides in the atom table, the system increments the reference count for the string during this process.

Repeated calls to add the same atom name return the same atom. If the atom name does not exist in the table when AddAtom is called, the atom name is added to the table and a new atom is returned. If it is a string atom, its reference count is also set to one.

An application should call the DeleteAtom function when it no longer needs to use a local atom; it should call the GlobalDeleteAtom function when it no longer needs a global atom. In the case of a string atom, either of these functions reduces the reference count of the corresponding atom by one. When the reference count reaches zero, the system deletes the atom name from the table.

The atom name of a string atom remains in the global atom table as long as its reference count is greater than zero, even after the application that placed it in the table terminates. A local atom table is destroyed when the associated application terminates, regardless of the reference counts of the atoms in the table.

+$#A Atom-Table Queries

An application can determine whether a particular string is already in an atom table by using the FindAtom or GlobalFindAtom function. These functions search an atom table for the specified string and, if the string is there, return the corresponding atom.

An application can use the GetAtomName or GlobalGetAtomName function to retrieve an atom-name string from an atom table, provided the application has the atom corresponding to the string sought. Both functions copy the atom-name string of the specified atom to a buffer and return the length of the string that was copied. GetAtomName retrieves an atom-name string from a local atom table, and GlobalGetAtomName retrieves an atom-name string from the global atom table.

+$#A Atom String Formats

The AddAtom, GlobalAddAtom, FindAtom, and GlobalFindAtom functions take a pointer to a null-terminated string. An application can specify this pointer in one of the following ways.

Format | Description
#dddd | An integer specified as a decimal string. Used to create or find an integer atom.
string atom name | A string atom name. Used to add a string atom name to an atom table and receive an atom in return.

+$#A Using Atoms

The most typical use for atoms is in DDE applications. In the DDE protocol, applications use global atoms to identify the applications exchanging data, the nature of the data being exchanged, and the actual data items being exchanged. For more information about DDE and for examples that show how to use atoms, see Dynamic Data Exchange.

+$#A Atom Functions

Following are the functions associated with atoms.

AddAtom
DeleteAtom
FindAtom
GetAtomName
GlobalAddAtom
GlobalDeleteAtom
GlobalFindAtom
GlobalGetAtomName
InitAtomTable

Obsolete or Deleted Functions

GetAtomHandle


Bitmaps.rtf

+$#AK Bitmaps

A bitmap is a powerful graphics object used to create, manipulate (scale, scroll, rotate, and paint), and store images as files on a disk.

+$#A About Bitmaps

A bitmap is one of seven objects that can be selected into a device context. The other six objects are the pen, brush, font, region, logical palette, and path. (For more information about device contexts and related objects, see Device Contexts.)

In the Microsoft® Win32® application programming interface (API), Control Panel applications are examples of applications that use bitmaps. When a user selects wallpaper for the desktop, the user actually selects a bitmap, which Windows uses to paint the desktop background.

Windows creates this pattern by repeatedly drawing a 32- by 32-pixel pattern on the desktop. This pattern is stored as a bitmap in the file REDBRICK.BMP.

From the user's point of view, a bitmap is a rectangle of pixels that form a visual image. However, from the developer's perspective, a bitmap is a collection of structures that specify or contain the following elements:

• A header that describes the resolution of the device on which the rectangle of pixels was created, the dimensions of the rectangle, the size of the array of bits, and so on.

• A logical palette.

• An array of bits that defines the relationship between pixels in the bitmapped image and entries in the logical palette.

The following illustration shows the developer's perspective of the bitmap found in the file REDBRICK.BMP. It shows a palette array, a 32- by 32-pixel rectangle, and the index array that maps colors from the palette to pixels in the rectangle.

{ewc msdncd, EWGraphic, group10651 0 /a "SDK_01.bmp"}

In the preceding example, the rectangle of pixels was created on a VGA display device using a palette of 16 colors. A 16-color palette requires 4-bit indices; therefore, the array that maps palette colors to pixel colors is composed of 4-bit indices as well. (For more information about logical color-palettes, see Colors.)

Note that in the above bitmap, Windows maps indices to pixels beginning with the bottom scan line of the rectangular region and ending with the top scan line. (A scan line is a single row of adjacent pixels on a video display.) For example, the first row of the array (row 0) corresponds to the bottom row of pixels, scan line 31. This is because the above bitmap is a "bottom-up" device-independent bitmap (DIB), a common type of bitmap. In "top-down" DIBs and in device-dependent bitmaps (DDBs), Windows maps indices to pixels beginning with the top scan line.

+$#A Bitmap Types

There are two types of bitmaps: device-dependent (DDBs) and device-independent (DIBs). DDBs were common in early (pre-3.0) versions of Windows. In fact, they were the only bitmaps available to developers. However, as display technology improved and as the variety of display devices increased among Windows users, certain inherent problems surfaced. For example, there was no method of storing (or retrieving) the resolution of the display type on which a bitmap was created, so a drawing application could not quickly determine whether a bitmap was suitable for the type of video display device on which the application was running. To solve this problem, Microsoft created DIBs.

+$#A Device-Independent Bitmaps

A DIB contains the following color and dimension information:

• The color format of the device on which the rectangular image was created.

• The resolution of the device on which the rectangular image was created.

• The palette for the device on which the image was created.

• An array of bits that maps red, green, blue (RGB) triplets to pixels in the rectangular image.

• A data-compression identifier that indicates the data compression scheme (if any) used to reduce the size of the array of bits.

This information is stored in a BITMAPINFO structure consisting of a BITMAPINFOHEADER structure followed by two or more RGBQUAD structures. The BITMAPINFOHEADER specifies the dimensions of the pixel rectangle, describes the device's color technology, and identifies the compression schemes used to reduce the bitmap's size. The RGBQUAD structures identify the colors that appear in the pixel rectangle.

There are two varieties of DIB: "bottom-up" DIBs, in which the origin lies at the lower left corner, and "top-down" DIBs, in which the origin lies at the upper left corner. If the height of a DIB, as indicated by the biHeight member of the BITMAPINFOHEADER structure, is a positive value, it is a bottom-up DIB; if the height is a negative value, it is a top-down DIB. Top-down DIBs cannot be compressed.

The color format is specified in terms of a count of color planes and color bits. The count of color planes is always 1; the count of color bits is 1 for monochrome bitmaps, 4 for VGA bitmaps, and 8, 16, 24, or 32 for bitmaps on other color devices. An application retrieves the number of color bits a particular display (or printer) uses by calling the GetDeviceCaps function, specifying BITSPIXEL as the second argument.

The resolution of a display device is specified in pixels per meter. An application can retrieve the horizontal resolution for a video display, or printer, by following a three-step process:

1. Call the GetDeviceCaps function, specifying HORZRES as the second argument.

2. Call GetDeviceCaps a second time, specifying HORZSIZE as the second argument.

3. Divide the first return value by the second return value.

The application can retrieve the vertical resolution by using the same three-step process with different parameters: VERTRES in place of HORZRES, and VERTSIZE in place of HORZSIZE.

The palette is represented by an array of RGBQUAD structures that specify the red, green, and blue intensity components for each color in a display device's color palette. Each color index in the palette array maps to a specific pixel in the rectangular region associated with the bitmap. The size of this array, in bits, is equivalent to the width of the rectangle, in pixels, multiplied by the height of the rectangle, in pixels, multiplied by the count of color bits for the device. An application can retrieve the size of the device's palette by calling the GetDeviceCaps function, specifying the NUMCOLORS constant as the second argument.

The Win32 API supports the compression of the palette array for 8-bit-per-pixel and 4-bit-per-pixel bottom-up DIBs. These arrays can be compressed by using the run-length encoding (RLE) scheme. The RLE scheme uses 2-byte values, the first byte specifying the number of consecutive pixels that use a color index and the second byte specifying the index. For more information about bitmap compression, see the description of the BITMAPINFOHEADER structure.

An application can create a DIB from a DDB by initializing the required structures and calling the GetDIBits function. To determine whether a device supports this function, call the GetDeviceCaps function, specifying RC_DI_BITMAP as the RASTERCAPS flag.

An application can use a DIB to set pixels on the display device by calling the SetDIBitsToDevice or the StretchDIBits function. To determine whether a device supports the SetDIBitsToDevice function, call the GetDeviceCaps function, specifying RC_DIBTODEV as the RASTERCAPS flag. Specify RC_STRETCHDIB as the RASTERCAPS flag to determine if the device supports StretchDIBits.

An application that simply needs to display a preexisting DIB can use the SetDIBitsToDevice function. For example, a spreadsheet application can open existing charts and display them in a window by using the SetDIBitsToDevice function. To repeatedly redraw a bitmap in a window, however, the application should use the BitBlt function. For example, a multimedia application that combines animated graphics with sound would benefit from calling the BitBlt function because it executes faster than SetDIBitsToDevice.

+$#A Device-Dependent Bitmaps

Device-dependent bitmaps are supported only for compatibility with applications written for Windows versions earlier than 3.0. A developer writing a new application, or porting an application written for a previous version of Windows to the Win32 platform, should use DIBs.

DDBs are described by using a single structure, the BITMAP structure. The members of this structure specify the width and height of a rectangular region, in pixels; the width of the array that maps entries from the device palette to pixels; and the device's color format, in terms of color planes and bits per pixel. An application can retrieve the color format of a device by calling the GetDeviceCaps function and specifying the appropriate constants.

There are two types of DDBs: discardable and nondiscardable. A discardable DDB is a bitmap that Windows discards if the bitmap is not selected into a DC and if system memory is low. The CreateDiscardableBitmap function creates discardable bitmaps. The CreateBitmap, CreateCompatibleBitmap, and CreateBitmapIndirect functions create nondiscardable bitmaps.

An application can create a DDB from a DIB by initializing the required structures and calling the CreateDIBitmap function. Specifying CBM_INIT in the call to CreateDIBitmap is equivalent to calling the CreateCompatibleBitmap function to create a DDB in the format of the device and then calling the SetDIBits function to translate the DIB bits to the DDB. To determine whether a device supports the SetDIBits function, call the GetDeviceCaps function, specifying RC_DI_BITMAP as the RASTERCAPS flag.

+$#A Bitmaps, Device Contexts, and Drawing Surfaces

A device context (DC) is a data structure defining the graphics objects, their associated attributes, and the graphics modes affecting output on a device. An application creates a DC by calling the CreateDC function; it retrieves a window manager device context by calling the GetDC function.

+$#A The Drawing Surface

Before returning a handle that identifies that DC, the window manager selects a drawing surface into the DC. If the application called the CreateDC function to create a device context for a VGA display, the dimensions of this drawing surface are 640 by 480 pixels. If the application called the GetDC function, the dimensions reflect the size of the client area.

When an application passes the handle returned by CreateDC or GetDC to one of the graphics device interface (GDI) drawing functions, the requested output appears on the drawing surface selected into the device context.

+$#A Compatible Device Contexts

To enable applications to place output in memory rather than sending it to an actual device, the Win32 API provides a special device context for bitmap operations called a compatible device context. A compatible DC enables Windows to treat a portion of memory as a virtual device. It is an array of bits in memory that an application can use temporarily to store the color data for bitmaps created on a normal drawing surface.

An application creates a compatible DC by calling the CreateCompatibleDC function and receives a handle that identifies a normal device context. Windows creates a temporary one-bit placeholder for the array. Before performing drawing operations using the compatible DC handle, the application must increase the size of this array. To do this, the application can call the CreateBitmap, CreateBitmapIndirect, or CreateCompatibleBitmap function to create a bitmap of the appropriate dimensions and then call the SelectObject function to select the bitmap into the DC. After the bitmap is selected into the compatible DC, Windows replaces the single-bit array with an array large enough to store color information for the specified rectangle of pixels.

When an application passes the handle returned by CreateCompatibleDC to one of the GDI drawing functions, the requested output does not appear on a device's drawing surface. Instead, Windows stores the color information for the resultant line, curve, text or region in the array of bits. The application can copy the image stored in memory back onto a drawing surface by calling the BitBlt function, identifying the compatible DC as the source device context and a window or screen DC as the target device context.

When displaying a DIB or a DDB created from a DIB on a palette device, you can improve the speed at which the image is drawn by arranging the logical palette to match the layout of the system palette. To do this, call GetDeviceCaps with the NUMRESERVED value to get the number of reserved colors in the system. Then call GetSystemPaletteEntries and fill in the first and last NUMRESERVED/2 entries of the logical palette with the corresponding system colors. For example, if NUMRESERVED is 20, you would fill in the first and last 10 entries of the logical palette with the system colors. Then fill in the remaining 256 − NUMRESERVED colors of the logical palette (in our example, the remaining 236 colors) with colors from the DIB and set the PC_NOCOLLAPSE flag on each of these colors. For more information about color and palettes, see Colors.

+$#A Bitmap Rotation

Windows provides a function to copy a bitmap into a parallelogram; this function, PlgBlt, performs a bit-block transfer from a rectangle in a source device context into a parallelogram in a destination device context. In order to rotate the bitmap, an application must provide the coordinates, in world units, to be used for the corners of the parallelogram. (For more information about rotation and world units, see Coordinate Spaces and Transformations.)

+$#A Bitmap Scaling

The Win32 API also provides a function to scale a bitmap; this function, StretchBlt, performs a bit-block transfer from a rectangle in a source device context into a rectangle in a destination device context. However, unlike the BitBlt function, which duplicates the source rectangle dimensions in the destination rectangle, StretchBlt allows an application to specify the dimensions of both the source and the destination rectangles. When the destination bitmap is smaller than the source bitmap, Windows combines rows or columns of color data (or both) in the bitmap before rendering the corresponding image on the display device. Windows combines the color data according to the specified stretch mode, which the application defines by calling the SetStretchBltMode function. When the destination bitmap is larger than the source bitmap, Windows scales or magnifies each pixel in the resultant image accordingly.

+$#A Bitmaps as Brushes

The Win32 API provides a number of functions that use the brush currently selected into a device context to perform bitmap operations. For example, the PatBlt function replicates the brush in a rectangular region within a window, and the FloodFill function replicates the brush inside an area in a window bounded by the specified color (unlike PatBlt, FloodFill does fill nonrectangular shapes).

The PatBlt function name (an abbreviation for pattern block transfer) implies that the function simply replicates the brush (or pattern) until it fills a specified rectangle. However, the function is actually much more powerful. Before replicating the brush, it combines the color data for the pattern with the color data for the existing pixels on the video display by using a raster operation (ROP). An ROP is a bitwise operation that is applied to the bits of color data for the replicated brush and the bits of color data for the target rectangle on the display device. There are 256 ROPs in the Win32 API; however, the PatBlt function recognizes only those that require a pattern and a destination (not those that require a source). The following table identifies the five most common ROPs.

ROP | Description
PATCOPY | Copies the pattern to the destination bitmap.
PATINVERT | Combines the destination bitmap with the pattern by using the Boolean OR operator.
DSTINVERT | Inverts the destination bitmap.
BLACKNESS | Turns all output to binary zeroes.
WHITENESS | Turns all output to binary ones.

The FloodFill function replicates the brush within a region bounded by a specified color. However, unlike the PatBlt function, FloodFill does not combine the color data for the brush with the color data for the pixels on the display; it simply sets the color of all pixels within the enclosed region on the display to the color of the brush that is currently selected into the device context.

+$#A Bitmap Storage

Bitmaps should be saved in a file that uses the established Windows format and assigned a name with the three-character .BMP extension. The established Windows format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER structure. An array of RGBQUAD structures (also called a color table) follows the BITMAPINFOHEADER structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).

The Windows format is shown in the following illustration.

{ewc msdncd, EWGraphic, group10651 1 /a "SDK_02.bmp"}

The members of the BITMAPFILEHEADER structure identify the file; specify the size of the file, in bytes; and specify the offset from the first byte in the header to the first byte of bitmap data. The members of the BITMAPINFOHEADER structure specify the bitmap's width and height, in pixels; the color format (count of color planes and color bits per pixel) of the display device on which the bitmap was created; whether the bitmap data was compressed before storage and the type of compression used; the number of bytes of bitmap data; the resolution of the display device on which the bitmap was created; and the number of colors represented in the data. The RGBQUAD structures specify the RGB intensity values for each of the colors in the device's palette. The color-index array maps indices values from the RGBQUAD array to pixels in a rectangular region on the display.

The following hexadecimal output shows the contents of the file REDBRICK.BMP.

0000 42 4d 76 02 00 00 00 00 00 00 76 00 00 00 28 00

0010 00 00 20 00 00 00 20 00 00 00 01 00 04 00 00 00

0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

0030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80

0040 00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80

0050 00 00 80 80 80 00 c0 c0 c0 00 00 00 ff 00 00 ff

0060 00 00 00 ff ff 00 ff 00 00 00 ff 00 ff 00 ff ff

0070 00 00 ff ff ff 00 00 00 00 00 00 00 00 00 00 00

0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00

0090 00 00 00 00 00 00 11 11 01 19 11 01 10 10 09 09

00a0 01 09 11 11 01 90 11 01 19 09 09 91 11 10 09 11

00b0 09 11 19 10 90 11 19 01 19 19 10 10 11 10 09 01

00c0 91 10 91 09 10 10 90 99 11 11 11 11 19 00 09 01

00d0 91 01 01 19 00 99 11 10 11 91 99 11 09 90 09 91

00e0 01 11 11 11 91 10 09 19 01 00 11 90 91 10 09 01

00f0 11 99 10 01 11 11 91 11 11 19 10 11 99 10 09 10

0100 01 11 11 11 19 10 11 09 09 10 19 10 10 10 09 01

0110 11 19 00 01 10 19 10 11 11 01 99 01 11 90 09 19

0120 11 91 11 91 01 11 19 10 99 00 01 19 09 10 09 19

0130 10 91 11 01 11 11 91 01 91 19 11 00 99 90 09 01

0140 01 99 19 01 91 10 19 91 91 09 11 99 11 10 09 91

0150 11 10 11 91 99 10 90 11 01 11 11 19 11 90 09 11

0160 00 19 10 11 01 11 99 99 99 99 99 99 99 99 09 99

0170 99 99 99 99 99 99 00 00 00 00 00 00 00 00 00 00

0180 00 00 00 00 00 00 90 00 00 00 00 00 00 00 00 00

0190 00 00 00 00 00 00 99 11 11 11 19 10 19 19 11 09

01a0 10 90 91 90 91 00 91 19 19 09 01 10 09 01 11 11

01b0 91 11 11 11 10 00 91 11 01 19 10 11 10 01 01 11

01c0 90 11 11 11 91 00 99 09 19 10 11 90 09 90 91 01

01d0 19 09 91 11 01 00 90 10 19 11 00 11 11 00 10 11

01e0 01 10 11 19 11 00 90 19 10 91 01 90 19 99 00 11

01f0 91 01 11 01 91 00 99 09 09 01 10 11 91 01 10 91

0200 99 11 10 90 91 00 91 11 00 10 11 01 10 19 19 09

0210 10 00 99 01 01 00 91 01 19 91 19 91 11 09 10 11

0220 00 91 00 10 90 00 99 01 11 10 09 10 10 19 09 01

0230 91 90 11 09 11 00 90 99 11 11 11 90 19 01 19 01

0240 91 01 01 19 09 00 91 10 11 91 99 09 09 90 11 91

0250 01 19 11 11 91 00 91 19 01 00 11 00 91 10 11 01

0260 11 11 10 01 11 00 99 99 99 99 99 99 99 99 99 99

0270 99 99 99 99 99 90

The following table shows the data bytes associated with the structures in a bitmap file.

Structure | Corresponding bytes
BITMAPFILEHEADER | 0x00 − 0x0D
BITMAPINFOHEADER | 0x0E − 0x31
RGBQUAD array | 0x32 − 0x75
Color-index array | 0x76 − 0x275

+$#A Using Bitmaps

• Capturing an image

• Scaling an image

• Storing an image

+$#A Capturing an Image

You can use a bitmap to capture an image, and you can store the captured image in memory, display it at a different location in your application's window, or display it in another window.

In some cases, you may want your application to capture images and store them only temporarily. For example, when you scale or "zoom" a picture created in a drawing application, the application must temporarily save the normal view of the image and display the zoomed view. Later, when the user selects the normal view, the application must replace the zoomed image with a copy of the normal view that it temporarily saved.

To store an image temporarily, your application must call CreateCompatibleDC to create a DC that is compatible with the current window DC. After you create a compatible DC, you create a bitmap with the appropriate dimensions by calling the CreateCompatibleBitmap function and then select it into this device context by calling the SelectObject function.

After the compatible device context is created and the appropriate bitmap has been selected into it, you can capture the image. The Win32 API provides the BitBlt function to capture images. This function performs a bit block transfer ⎯ that is, it copies data from a source bitmap into a destination bitmap. Because it copies data from bitmaps, you'd expect that two arguments to this function would be bitmap handles; however, this is not the case. Instead, BitBlt receives handles that identify two device contexts and copies the bitmap data from a bitmap selected into the source DC into a bitmap selected into the target DC. In this case, the target DC is the compatible DC, so when BitBlt completes the transfer, the image has been stored in memory. To redisplay the image, call BitBlt a second time, specifying the compatible DC as the source DC and a window (or printer) DC as the target DC.

The following example code, from an application that captures an image of the entire desktop, creates a compatible device context and a bitmap with the appropriate dimensions, selects the bitmap into the compatible DC, and then copies the image using the BitBlt function.

/*

* Create a normal DC and a memory DC for the entire screen. The

* normal DC provides a "snapshot" of the screen contents. The

* memory DC keeps a copy of this "snapshot" in the associated

* bitmap.

*/

hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL);

hdcCompatible = CreateCompatibleDC(hdcScreen);

/* Create a compatible bitmap for hdcScreen. */

hbmScreen = CreateCompatibleBitmap(hdcScreen,

GetDeviceCaps(hdcScreen, HORZRES),

GetDeviceCaps(hdcScreen, VERTRES));

if (hbmScreen == 0)

errhandler("hbmScreen", hwnd);

/* Select the bitmaps into the compatible DC. */

if (!SelectObject(hdcCompatible, hbmScreen))

errhandler("Compatible Bitmap Selection", hwnd);

/* Hide the application window. */

ShowWindow(hwnd, SW_HIDE);

/*

* Copy color data for the entire display into a

* bitmap that is selected into a compatible DC.

*/

if (!BitBlt(hdcCompatible,

0,0,

bmp.bmWidth, bmp.bmHeight,

hdcScreen,

0,0,

SRCCOPY))

errhandler("Screen to Compat Blt Failed", hwnd);

/* Redraw the application window. */

ShowWindow(hwnd, SW_SHOW);

+$#A Scaling an Image

Some applications scale images ⎯ that is, they display zoomed or reduced views of an image. For example, a drawing application may provide a zoom feature that enables the user to view and edit a drawing on a pixel-by-pixel basis.

Applications scale images by calling the StretchBlt function. Like the BitBlt function, StretchBlt copies bitmap data from a bitmap in a source DC into a bitmap in a target DC. However, unlike the BitBlt function, StretchBlt scales the image based on the specified dimensions of the source and target rectangles. If the source rectangle is larger than the target rectangle, the resultant image will appear to have shrunk; if the source rectangle is smaller than the target rectangle, the resultant image will appear to have expanded.

If the target rectangle is smaller than the source rectangle, StretchBlt removes color data from the image according to a specified stretch mode as shown in the following table.

Stretch Mode | Method
BLACKONWHITE | Performs a logical AND operation on the color data for the eliminated pixels and the color data for the remaining pixels.
WHITEONBLACK | Performs a logical OR operation on the color data for the eliminated pixels and the color data for the remaining pixels.
COLORONCOLOR | Eliminates the color data of the deleted pixels completely.
HALFTONE | Approximates the original (source) color data in the destination.

You set the stretch mode by calling the SetStretchBltMode function.

The following example code is taken from an application that displays an image either at its original size or a twice the original size. (This application uses the default stretch mode.)

hdcScaled = CreateCompatibleDC(hdcScreen);

hbmScaled = CreateCompatibleBitmap(hdcScreen,

GetDeviceCaps(hdcScreen, HORZRES) * 2,

GetDeviceCaps(hdcScreen, VERTRES) * 2);

if (hbmScaled == 0)

errhandler("hbmScaled", hwnd);

/* Select the bitmaps into the compatible DC. */

if (!SelectObject(hdcScaled, hbmScaled))

errhandler("Scaled Bitmap Selection", hwnd);

case WM_COMMAND: /* message: command from application menu */

switch(wParam) {

case IDM_SCALEX1:

if (fBlt){

fScaled = FALSE;

hdcWin = GetDC(hwnd);

BitBlt(hdcWin,

0,0,

bmp.bmWidth, bmp.bmHeight,

hdcCompatible,

0,0,

SRCCOPY);

ReleaseDC(hwnd, hdcWin);

}

break;

case IDM_SCALEX2:

if (fBlt){

fScaled = TRUE;

StretchBlt(hdcScaled,

0, 0,

bmp.bmWidth * 2, bmp.bmHeight * 2,

hdcCompatible,

0, 0,

bmp.bmWidth, bmp.bmHeight,

SRCCOPY);

hdcWin = GetDC(hwnd);

BitBlt(hdcWin,

0,0,

bmp.bmWidth, bmp.bmHeight,

hdcScaled,

0,0,

SRCCOPY);

ReleaseDC(hwnd, hdcWin);

}

break;

+$#A Storing an Image

Many applications store images permanently as files. For example, drawing applications store pictures, spreadsheet applications store charts, CAD applications store drawings, and so on.

If you are writing an application that will store a bitmapped image in a file, you should use the Windows file format described in Bitmap Storage. In order to store a bitmap in this format, you must initialize a BITMAPINFO structure (consisting of a BITMAPFILEHEADER structure and an array of RGBQUAD structures), as well as an array of palette indices.

The following example code defines a function that allocates memory for and initializes members within a BITMAPINFOHEADER structure.

PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp) {

BITMAP bmp;

PBITMAPINFO pbmi;

WORD cClrBits;

/* Retrieve the bitmap's color format, width, and height. */

if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))

errhandler("GetObject", hwnd);

/* Convert the color format to a count of bits. */

cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);

if (cClrBits == 1)

cClrBits = 1;

else if (cClrBits <= 4)

cClrBits = 4;

else if (cClrBits <= 8)

cClrBits = 8;

else if (cClrBits <= 16)

cClrBits = 16;

else if (cClrBits <= 24)

cClrBits = 24;

else

cClrBits = 32;

/*

* Allocate memory for the BITMAPINFO structure. (This structure

* contains a BITMAPINFOHEADER structure and an array of RGBQUAD data

* structures.)

*/

if (cClrBits != 24)

pbmi = (PBITMAPINFO) LocalAlloc(LPTR,

sizeof(BITMAPINFOHEADER) +

sizeof(RGBQUAD) * (2^cClrBits));

/*

* There is no RGBQUAD array for the 24-bit-per-pixel format.

*/

else

pbmi = (PBITMAPINFO) LocalAlloc(LPTR,

sizeof(BITMAPINFOHEADER));

/* Initialize the fields in the BITMAPINFO structure. */

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

pbmi->bmiHeader.biWidth = bmp.bmWidth;

pbmi->bmiHeader.biHeight = bmp.bmHeight;

pbmi->bmiHeader.biPlanes = bmp.bmPlanes;

pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;

if (cClrBits < 24)

pbmi->bmiHeader.biClrUsed = 2^cClrBits;

/* If the bitmap is not compressed, set the BI_RGB flag. */

pbmi->bmiHeader.biCompression = BI_RGB;

/*

* Compute the number of bytes in the array of color

* indices and store the result in biSizeImage.

*/

pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8

* pbmi->bmiHeader.biHeight

* cClrBits;

/*

* Set biClrImportant to 0, indicating that all of the

* device colors are important.

*/

pbmi->bmiHeader.biClrImportant = 0;

return pbmi;

}

The following example code defines a function that initializes the remaining structures, retrieves the array of palette indices, opens the file, copies the data, and closes the file.

void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi,

HBITMAP hBMP, HDC hDC)

{

HANDLE hf; /* file handle */

BITMAPFILEHEADER hdr; /* bitmap file-header */

PBITMAPINFOHEADER pbih; /* bitmap info-header */

LPBYTE lpBits; /* memory pointer */

DWORD dwTotal; /* total count of bytes */

DWORD cb; /* incremental count of bytes */

BYTE *hp; /* byte pointer */

DWORD dwTmp;

pbih = (PBITMAPINFOHEADER) pbi;

lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)

errhandler("GlobalAlloc", hwnd);

/*

* Retrieve the color table (RGBQUAD array) and the bits

* (array of palette indices) from the DIB.

*/

if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight,

lpBits, pbi, DIB_RGB_COLORS))

errhandler("GetDIBits", hwnd);

/* Create the .BMP file. */

hf = CreateFile(pszFile,

GENERIC_READ | GENERIC_WRITE,

(DWORD) 0,

(LPSECURITY_ATTRIBUTES) NULL,

CREATE_ALWAYS,

FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

if (hf == INVALID_HANDLE_VALUE)

errhandler("CreateFile", hwnd);

hdr.bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */

/* Compute the size of the entire file. */

hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +

pbih->biSize + pbih->biClrUsed

* sizeof(RGBQUAD) + pbih->biSizeImage);

hdr.bfReserved1 = 0;

hdr.bfReserved2 = 0;

/* Compute the offset to the array of color indices. */

hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +

pbih->biSize + pbih->biClrUsed

* sizeof (RGBQUAD);

/* Copy the BITMAPFILEHEADER into the .BMP file. */

if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),

(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))

errhandler("WriteFile", hwnd);

/* Copy the BITMAPINFOHEADER and RGBQUAD array into the file. */

if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)

+ pbih->biClrUsed * sizeof (RGBQUAD),

(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))

errhandler("WriteFile", hwnd);

/* Copy the array of color indices into the .BMP file. */

dwTotal = cb = pbih->biSizeImage;

hp = lpBits;

while (cb > MAXWRITE) {

if (!WriteFile(hf, (LPSTR) hp, (int) MAXWRITE,

(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))

errhandler("WriteFile", hwnd);

cb-= MAXWRITE;

hp += MAXWRITE;

}

if (!WriteFile(hf, (LPSTR) hp, (int) cb,

(LPDWORD) &dwTmp, (LPOVERLAPPED) NULL))

errhandler("WriteFile", hwnd);

/* Close the .BMP file. */

if (!CloseHandle(hf))

errhandler("CloseHandle", hwnd);

/* Free memory. */

GlobalFree((HGLOBAL)lpBits);
}

+$#A Bitmap Functions

Following are the functions used with bitmaps.

BitBlt
CreateBitmap
CreateBitmapIndirect
CreateCompatibleBitmap
CreateDIBitmap
CreateDIBSection
CreateDiscardableBitmap
ExtFloodFill
FloodFill
GetBitmapBits
GetBitmapDimensionEx
GetDIBColorTable
GetDIBits
GetPixel
GetStretchBltMode
LoadBitmap
MaskBlt
PatBlt
PlgBlt
SetBitmapBits
SetBitmapDimensionEx
SetDIBColorTable
SetDIBits
SetDIBitsToDevice
SetPixel
SetPixelV
SetStretchBltMode
StretchBlt
StretchDIBits

Obsolete or Deleted Functions

GetBitmapDimension
SetBitmapDimension


Brushes.rtf

+$#AK Brushes

A brush is a graphics tool that a Microsoft® Win32®-based application uses to paint the interior of polygons, ellipses, and paths. Drawing applications use brushes to paint shapes; word processing applications use brushes to paint rules; computer-aided design (CAD) applications use brushes to paint the interiors of cross-section views; and spreadsheet applications use brushes to paint the sections of pie charts and the bars in bar graphs.

+$#A About Brushes

There are two types of brushes: logical and physical. A logical brush is a description of the ideal bitmap that an application would use to paint shapes. A physical brush is the actual bitmap that a device driver creates based on an application's logical-brush definition. For more information about bitmaps, see Bitmaps.

When an application calls one of the functions that create a brush, it retrieves a handle that identifies a logical brush. When the application passes this handle to the SelectObject function, the device driver for the corresponding display or printer creates the physical brush.

+$#A Brush Origin

When an application calls a drawing function to paint a shape, Windows positions a brush at the start of the paint operation and maps a pixel in the brush bitmap to the window origin of the client area. (The window origin is the upper-left corner of the window's client area.) The coordinates of the pixel that Windows maps are called the brush origin. The default brush origin is located in the upper-left corner of the brush bitmap, at the coordinates (0,0). Windows then copies the brush across the client area, forming a pattern that is as tall as the bitmap. The copy operation continues, row by row, until the entire client area is filled. However, the brush pattern is visible only within the boundaries of the specified shape.

There are instances when the default brush origin should not be used. For example, it may be necessary for an application to use the same brush to paint the backgrounds of its parent and child windows and blend a child window's background with that of the parent window. To do this, the application should reset the brush origin by calling the SetBrushOrgEx function and shifting the origin the required number of pixels. (An application can retrieve the current brush origin by calling the GetBrushOrgEx function.)

The following illustration shows a five-pointed star filled by using an application-defined brush. The illustration shows a zoomed image of the brush, as well as the location to which it was mapped at the beginning of the paint operation.

{ewc msdncd, EWGraphic, group10652 0 /a "SDK_01.bmp"}

+$#A Logical Brush Types

There are four types of logical brushes (solid, stock, hatch, and pattern) as shown in the following illustration.

{ewc msdncd, EWGraphic, group10652 1 /a "SDK_02.bmp"}

The stock and hatch types each have several predefined brushes, as described in Hatch Brush.

+$#A Solid Brush

A solid brush is a logical brush that contains 64 pixels of the same color. An application can create a solid logical brush by calling the CreateSolidBrush function, specifying the color of the brush required. After creating the solid brush, the application can select it into its device context and use it to paint filled shapes.

+$#A Stock Brush

There are seven predefined logical stock brushes maintained by the graphics device interface (GDI). There are also 21 predefined logical stock brushes maintained by the window management interface (USER).

The following rectangles were painted by using the seven predefined stock brushes.

{ewc msdncd, EWGraphic, group10652 2 /a "SDK_03.bmp"}

An application can retrieve a handle identifying one of the seven stock brushes by calling the GetStockObject function, specifying the brush type.

The 21 stock brushes maintained by the window management interface correspond to the colors of window elements such as menus, scroll bars, and buttons. An application can obtain a handle identifying one of these brushes by calling the GetSysColorBrush function and specifying a system-color value. An application can retrieve the color corresponding to a particular window element by calling the GetSysColor function. An application can set the color corresponding to a window element by calling the SetSysColors function.

+$#A Hatch Brush

There are six predefined logical hatch brushes maintained by GDI. The following rectangles were painted by using the six predefined hatch brushes.

{ewc msdncd, EWGraphic, group10652 3 /a "SDK_04.bmp"}

An application can create a hatch brush by calling the CreateHatchBrush function, specifying one of the six hatch styles.

+$#A Pattern Brush

A pattern (or custom) brush is created from an application-defined bitmap or device-independent bitmap (DIB). The following rectangles were painted by using different pattern brushes.

{ewc msdncd, EWGraphic, group10652 4 /a "SDK_05.bmp"}

To create a logical pattern brush, an application must first create a bitmap. After creating the bitmap, the application can create the logical pattern brush by calling the CreatePatternBrush or CreateDIBPatternBrushPt function, supplying a handle that identifies the bitmap (or DIB). The brushes that appear in the preceding illustration were created from monochrome bitmaps. For a description of bitmaps, DIBs, and the functions that create them, see Bitmaps.

+$#A Using Brushes

You can use a brush to paint the interior of virtually any shape by using a GDI function. This includes the interiors of rectangles, ellipses, polygons, and paths. Depending on the requirements of your application, you can use a solid brush of a specified color, a stock brush, a hatch brush, or a pattern brush.

This section contains code samples that demonstrate the creation of a custom brush dialog box. The dialog box contains a grid that represents the bitmap Windows uses as a brush. A user can use this grid to create a pattern-brush bitmap and then view the custom pattern by clicking the Test Pattern button.

The following illustration shows a pattern created by using the Custom Brush dialog box.

{ewc msdncd, EWGraphic, group10652 5 /a "SDK_06.bmp"}

To display a dialog box, you must first create a dialog box template. The following dialog box template defines the Custom Brush dialog box.

CustBrush DIALOG 6, 18, 160, 118

STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION

CAPTION "Custom Brush"

FONT 8, "MS Sans Serif"

BEGIN

CONTROL "", IDD_GRID, "Static", SS_BLACKFRAME |

WS_CHILD, 3, 2, 83, 79

CONTROL "", IDD_RECT, "Static", SS_BLACKFRAME |

WS_CHILD, 96, 11, 57, 28

PUSHBUTTON "Test Pattern", IDD_PAINTRECT, 96, 47, 57, 14

PUSHBUTTON "OK", IDD_OK, 29, 98, 40, 14

PUSHBUTTON "Cancel", IDD_CANCEL, 92, 98, 40, 14

END

The Custom Brush dialog box contains five controls: a bitmap-grid window, a pattern-viewing window, and three push buttons, labeled Test Pattern, OK and Cancel. The Test Pattern push button enables the user to view the pattern. The dialog box template specifies the overall dimensions of the dialog box window, assigns a value to each control, specifies the location of each control, and so forth. (For more information about creating dialog box templates, see Dialog Boxes.)

The control values in the dialog box template are constants that have been defined as follows in the application's header file.

#define IDD_GRID 120

#define IDD_RECT 121

#define IDD_PAINTRECT 122

#define IDD_OK 123

#define IDD_CANCEL 124

After you create a dialog box template and include it in the application's resource-definition file, you must write a dialog procedure. This procedure processes messages that Windows sends to the dialog box. The following excerpt from an application's source code shows the dialog box procedure for the Custom Brush dialog box and the two application-defined functions it calls.

int APIENTRY BrushDlgProc(HWND hdlg, WORD message, LONG wParam,

LONG lParam)

{

static HWND hwndGrid; /* grid-window control */

static HWND hwndBrush; /* pattern-brush control */

static RECT rctGrid; /* grid-window rectangle */

static RECT rctBrush; /* pattern-brush rectangle */

static UINT bBrushBits[8]; /* bitmap bits */

static RECT rect[64]; /* grid-cell array */

static HBITMAP hbm; /* bitmap handle */

HBRUSH hbrush; /* current brush */

HBRUSH hbrushOld; /* default brush */

HRGN hrgnCell; /* test-region handle */

HDC hdc; /* DC handle */

int x, y, deltaX, deltaY; /* drawing coordinates */

POINTS ptlHit; /* mouse coordinates */

int i; /* count variable */

switch (message)

{

case WM_INITDIALOG:

/*

* Retrieve a window handle for the grid-window and

* pattern-brush controls

*/

hwndGrid = GetDlgItem(hdlg, IDD_GRID);

hwndBrush = GetDlgItem(hdlg, IDD_RECT);

/* Initialize the array of bits that defines the

* custom brush pattern with the value 1 to produce a

* solid white brush).

*/

for (i=0; i<8; i++)

bBrushBits[i] = 0xFF;

/*

* Retrieve dimensions for the grid-window and pattern-brush

* controls.

*/

GetClientRect(hwndGrid, &rctGrid);

GetClientRect(hwndBrush, &rctBrush);

/* Determine the width and height of a single cell. */

deltaX = (rctGrid.right - rctGrid.left)/8;

deltaY = (rctGrid.bottom - rctGrid.top)/8;

/* Initialize the array of cell rectangles. */

for (y=rctGrid.top, i=0; y < rctGrid.bottom; y += deltaY){

for (x=rctGrid.left; x < (rctGrid.right - 8) && i < 64;

x += deltaX, i++) {

rect[i].left = x; rect[i].top = y;

rect[i].right = x + deltaX;

rect[i].bottom = y + deltaY;

}

}

return FALSE;

case WM_PAINT:

/* Draw the grid. */

hdc = GetDC(hwndGrid);

for (i=rctGrid.left; i<rctGrid.right;

i+=(rctGrid.right - rctGrid.left)/8){

MoveToEx(hdc, i, rctGrid.top, NULL);

LineTo(hdc, i, rctGrid.bottom);

}

for (i=rctGrid.top; i<rctGrid.bottom;

i+=(rctGrid.bottom - rctGrid.top)/8){

MoveToEx(hdc, rctGrid.left, i, NULL);

LineTo(hdc, rctGrid.right, i);

}

ReleaseDC(hwndGrid, hdc);

return FALSE;

case WM_LBUTTONDOWN:

/* Store the mouse coordinates in a POINT structure. */

ptlHit = MAKEPOINTS((POINTS FAR *)lParam);

/*

* Create a rectangular region with dimensions and

* coordinates that correspond to those of the grid

* window.

*/

hrgnCell = CreateRectRgn(rctGrid.left, rctGrid.top,

rctGrid.right, rctGrid.bottom);

/* Retrieve a window DC for the grid window. */

hdc = GetDC(hwndGrid);

/* Select the region into the DC. */

SelectObject(hdc, hrgnCell);

/* Test for a button click in the grid-window rectangle. */

if (PtInRegion(hrgnCell, ptlHit.x, ptlHit.y)){

/*

* A button click occurred in the grid-window rectangle;

* isolate the cell in which it occurred.

*/

for(i=0; i<64; i++){

DeleteObject(hrgnCell);

hrgnCell = CreateRectRgn(rect[i].left, rect[i].top,

rect[i].right, rect[i].bottom);

if (PtInRegion(hrgnCell, ptlHit.x, ptlHit.y)){

InvertRgn(hdc, hrgnCell);

/* Set the appropriate brush bits. */

if (i % 8 == 0)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x80;

else if (i % 8 == 1)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x40;

else if (i % 8 == 2)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x20;

else if (i % 8 == 3)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x10;

else if (i % 8 == 4)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x08;

else if (i % 8 == 5)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x04;

else if (i % 8 == 6)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x02;

else if (i % 8 == 7)

bBrushBits[i/8] = bBrushBits[i/8] ^ 0x01;

/* Exit the "for" loop after the bit is set. */

break;

} /* end if */

} /* end for */

} /* end if */

/* Release the DC for the control. */

ReleaseDC(hwndGrid, hdc);

return TRUE;

case WM_COMMAND:

switch (wParam){

case IDD_PAINTRECT:

hdc = GetDC(hwndBrush);

/* Create a monochrome bitmap. */

hbm = CreateBitmap(8, 8, 1, 1,

(LPBYTE)bBrushBits);

/* Select the custom brush into the DC. */

hbrush = CreatePatternBrush(hbm);

hbrushOld = SelectObject(hdc, hbrush);

/* Use the custom brush to fill the rectangle. */

Rectangle(hdc, rctBrush.left, rctBrush.top,

rctBrush.right, rctBrush.bottom);

/* Clean up memory. */

SelectObject(hdc, hbrushOld);

DeleteObject(hbrush);

DeleteObject(hbm);

ReleaseDC(hwndBrush, hdc);

return TRUE;

case IDD_OK:

case IDD_CANCEL:

EndDialog(hdlg, TRUE);

return TRUE;

} /* end switch */

break;

default:

return FALSE;

}

}

int GetStrLngth(LPTSTR cArray)

{

int i = 0;

while (cArray[i++] != 0);

return i-1;

}

DWORD RetrieveWidth(LPTSTR cArray, int iLength)

{

int i, iTmp;

double dVal, dCount;

dVal = 0.0;

dCount = (double)(iLength-1);

for (i=0; i<iLength; i++){

iTmp = cArray[i] - 0x30;

dVal = dVal + (((double)iTmp) * pow(10.0, dCount--));

}

return (DWORD)dVal;

}

The dialog box procedure for the Custom Brush dialog box processes four messages, as described in the following table.

Message | Action
WM_INITDIALOG | Retrieves a window handle and dimensions for the grid-window and pattern-brush controls, computes the dimensions of a single cell in the grid-window control, and initializes an array of grid-cell coordinates.
WM_PAINT | Draws the grid pattern in the grid-window control.
WM_LBUTTONDOWN | Determines whether the cursor is within the grid-window control when the user presses the left mouse button. If so, the dialog box procedure inverts the appropriate grid cell and records the state of that cell in an array of bits that is used to create the bitmap for the custom brush.
WM_COMMAND | Processes input for the three push button controls. If the user presses the Test Pattern button, the dialog box procedure paints the Test Pattern control with the new custom brush pattern. If the user presses the OK or Cancel button, the dialog box procedure performs actions accordingly.

For more information about messages and message processing, see Messages and Message Queues.

After you write the dialog box procedure, export it in the module-definition file, include the function definition for the procedure in the application's header file, and then call the dialog box procedure at the appropriate point in the application.

The following excerpt from the module-definition file shows how the dialog box procedure for the Custom Brush is exported.

; Export all functions that are called by a Windows routine.

EXPORTS

MainWndProc @1 ; name of window processing function

BrushDlgProc @2 ; name of custom-brush processing function

The following excerpt from the application's header file shows the function definition for the dialog box procedure and the two functions it calls.

int APIENTRY BrushDlgProc(HWND hdlg, WORD message, LONG wParam,

LONG lParam);

int GetStrLngth(LPTSTR cArray);

DWORD RetrieveWidth(LPTSTR cArray, int iLength);

Finally, the following code shows how the dialog box procedure is called from the application's source-code file. In this example, the dialog box procedure is called when the user chooses an option from the application's menu.

switch (message) {

case WM_CREATE:

break;

case WM_COMMAND: /* message: command from application menu */

switch(wParam) {

case IDM_CUSTOMBRUSH:

DialogBox((HANDLE)GetModuleHandle(NULL),

(LPTSTR)"CustBrush", hWnd,

(DLGPROC)BrushDlgProc);

break;

+$#A Brush Functions

Following are the functions that can be used with brushes.

CreateBrushIndirect
CreateDIBPatternBrush
CreateDIBPatternBrushPt
CreateHatchBrush
CreatePatternBrush
CreateSolidBrush
GetBrushOrgEx
GetSysColorBrush
SetBrushOrgEx

Obsolete or Deleted Functions

FixBrushOrgEx


Buttons.rtf

+$#AK Buttons

Microsoft® Windows® provides dialog boxes and controls to support communication between an application and the user. A button is a control the user can turn on or off to provide input to an application.

+$#A About Buttons

There are several types of buttons and, within each type, one or more styles to distinguish among buttons of the same type. The user turns a button on or off by selecting it using the mouse or keyboard. Selecting a button typically changes its visual appearance and state (from checked to unchecked, for example). Windows, the button, and the application cooperate in changing the button's appearance and state. A button can send messages to its parent window, and a parent window can send messages to a button. Some buttons are painted by Windows, some by the application. Buttons can be used alone or in groups and can appear with or without application-defined text (a label). They belong to the BUTTON window class.

Although an application can use buttons in overlapped, pop-up, and child windows, they are designed for use in dialog boxes, where Windows standardizes their behavior. If an application uses buttons outside dialog boxes, it increases the risk that the application may behave in a nonstandard fashion. Applications typically either use buttons in dialog boxes or use window subclassing to create customized buttons.

For general information about controls, see Controls. For more information about dialog boxes, see Dialog Boxes. For more information about window subclassing, see Window Procedures.

+$#A Button Types and Styles

Windows provides five kinds of buttons: push buttons, check boxes, radio buttons, group boxes, and owner-drawn buttons. Each type has one or more styles.

+$#A Push Buttons

A push button is a rectangle containing application-defined text (label), an icon, or a bitmap that indicates what the button does when the user selects it. A push button can be one of two styles: standard or default, as defined by the constants BS_PUSHBUTTON and BS_DEFPUSHBUTTON. A standard push button is typically used to start an operation. It receives the keyboard focus when the user selects it. A default push button, on the other hand, is typically used to indicate the most common or default choice. It is a button that the user can select by simply pressing ENTER when a dialog box has the input focus.

When the user selects a push button (of either style), it receives the keyboard focus from Windows, which sends the button's parent window a WM_COMMAND message containing the BN_CLICKED notification code. In response, the dialog box typically closes and carries out the operation indicated by the button.

+$#A Check Boxes

A check box consists of a square box and application-defined text (label), an icon, or a bitmap that indicates a choice the user can make by selecting the button. Applications typically display check boxes in a group box to permit the user to choose from a set of related, but independent options. For example, an application might present a group of check boxes from which the user can select error conditions that produce warning beeps.

A check box can be one of four styles: standard, automatic, three-state, and automatic three-state, as defined by the constants BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, and BS_AUTO3STATE, respectively. Each style can assume two check states: checked (an check mark inside the box) or unchecked (no check mark). In addition, a three-state check box can assume an indeterminate state (a grayed box inside the check box). Repeatedly selecting a standard or automatic check box toggles it from checked to unchecked and back again. Repeatedly selecting a three-state check box toggles it from checked to unchecked to indeterminate and back again.

When the user selects a check box (of any style), the check box receives the keyboard focus from Windows, which sends the check box's parent window a WM_COMMAND message containing the BN_CLICKED notification code. The parent window doesn't acknowledge this message if it comes from an automatic check box or automatic three-state check box, because Windows automatically sets the check state for those styles. But the parent window must acknowledge the message if it comes from a check box or three-state check box because the parent window, not Windows, is responsible for setting the check state for those styles. Regardless of the check box style, Windows automatically repaints the check box once its state is changed.

+$#A Radio Buttons

A radio button consists of a round button and application-defined text (a label), an icon, or a bitmap that indicates a choice the user can make by selecting the button. An application typically uses radio buttons in a group box to permit the user to choose from a set of related, but mutually exclusive options. For example, the application might present a group of radio buttons from which the user can select a format preference for text selected in the client area. The user could select a left-aligned, right-aligned, or centered format by selecting the corresponding radio button. Typically, the user can select only one option at a time from a set of radio buttons.

A radio button can be one of two styles: standard or automatic, as defined by the constants BS_RADIOBUTTON and BS_AUTORADIOBUTTON. Each style can assume two check states: checked (a dot in the button) or unchecked (no dot in the button). Repeatedly selecting a radio button (standard or automatic) toggles it from checked to unchecked and back again.

When the user selects either state, the radio button receives the keyboard focus from Windows, which sends the button's parent window a WM_COMMAND message containing the BN_CLICKED notification code. The parent window doesn't acknowledge this message if it comes from an automatic radio button because Windows automatically sets the check state for that style. But the parent window should acknowledge the message if it comes from a radio button because the parent window, not Windows, is responsible for setting the check state 1for that style. Regardless of the radio button style, Windows automatically repaints the button as its state changes.

When the user selects an automatic radio button, Windows automatically sets the check state of all other radio buttons within the same group to unchecked. The same behavior is available for standard radio buttons by using the WS_GROUP style, as discussed in Dialog Boxes.

+$#A Owner-Drawn Buttons

Unlike radio buttons, an owner-drawn button is painted by the application, not by Windows, and has no predefined appearance or usage. Its purpose is to provide a button whose appearance and behavior are defined by the application alone. There is only one owner-drawn button style: BS_OWNERDRAW.

When the user selects an owner-drawn button, Windows sends the button's parent window a WM_COMMAND message containing the BN_CLICKED notification code, just as it does for a button that is not owner-drawn. The application must respond appropriately.

+$#A Group Boxes

A group box is a rectangle that surrounds a set of controls, such as check boxes or radio buttons, with application-defined text (label) in its upper left corner. The sole purpose of a group box is to organize controls related by a common purpose (usually indicated by the label). The group box has only one style, defined by the constant BS_GROUPBOX. Because a group box cannot be selected, it has no check state, focus state, or push state. An application cannot send messages to a group box.

+$#A Button Styles

Many developers create dialog boxes by using standalone tools, not requiring them to specify button styles. However, if an application creates a button by using the CreateWindow or CreateWindowEx function, the following table of constants can be used to define the button style.

Style | Meaning
BS_3STATE | Creates a button that is the same as a check box, except that the box can be grayed as well as checked or unchecked. Use the grayed state to show that the state of the check box is not determined.
BS_AUTO3STATE | Creates a button that is the same as a three-state check box, except that the box changes its state when the user selects it. The state cycles through checked, grayed, and unchecked.
BS_AUTOCHECKBOX | Creates a button that is the same as a check box, except that the check state automatically toggles between checked and unchecked each time the user selects the check box.
BS_AUTORADIOBUTTON | Creates a button that is the same as a radio button, except that when the user selects it, Windows automatically sets the button's check state to checked and automatically sets the check state for all other buttons in the same group to unchecked.
BS_CHECKBOX | Creates a small, empty check box with text. By default, the text is displayed to the right of the check box. To display the text to the left of the check box, combine this flag with the BS_LEFTTEXT style (or on Windows 95 only, with the equivalent BS_RIGHTBUTTON style).
BS_DEFPUSHBUTTON | Creates a push button that behaves like a BS_PUSHBUTTON style button, but also has a heavy black border. If the button is in a dialog box, the user can select the button by pressing the ENTER key, even when the button does not have the input focus. This style is useful for enabling the user to quickly select the most likely (default) option.
BS_GROUPBOX | Creates a rectangle in which other controls can be grouped. Any text associated with this style is displayed in the rectangle's upper left corner.
BS_LEFTTEXT | Places text on the left side of the radio button or check box when combined with a radio button or check box style. Same as the Windows 95 BS_RIGHTBUTTON style.
BS_OWNERDRAW | Creates an owner-drawn button. The owner window receives a WM_MEASUREITEM message when the button is created and a WM_DRAWITEM message when a visual aspect of the button has changed. Do not combine the BS_OWNERDRAW style with any other button styles.
BS_PUSHBUTTON | Creates a push button that posts a WM_COMMAND message to the owner window when the user selects the button.
BS_RADIOBUTTON | Creates a small circle with text. By default, the text is displayed to the right of the circle. To display the text to the left of the circle, combine this flag with the BS_LEFTTEXT style (or on Windows 95 only, with the equivalent BS_RIGHTBUTTON style). Use radio buttons for groups of related, but mutually exclusive choices.
BS_USERBUTTON | Obsolete, but provided for compatibility with 16-bit versions of Windows. Win32-based applications should use BS_OWNERDRAW instead.
BS_BITMAP | Windows 95 only: Specifies that the button displays a bitmap.
BS_BOTTOM | Windows 95 only: Places text at the bottom of the button rectangle.
BS_CENTER | Windows 95 only: Centers text horizontally in the button rectangle.
BS_ICON | Windows 95 only: Specifies that the button displays an icon.
BS_LEFT | Windows 95 only: Left-justifies the text in the button rectangle. However, if the button is a check box or radio button that does not have the BS_RIGHTBUTTON style, the text is left justified on the right side of the check box or radio button.
BS_MULTILINE | Windows 95 only: Wraps the button text to multiple lines if the text string is too long to fit on a single line in the button rectangle.
BS_NOTIFY | Windows 95 only: Enables a button to send BN_DBLCLK, BN_KILLFOCUS, and BN_SETFOCUS notification messages to its parent window. Note that buttons send the BN_CLICKED notification message regardless of whether it has this style.
BS_PUSHLIKE | Windows 95 only: Makes a button (such as a check box, three-state check box, or radio button) look and act like a push button. The button looks raised when it isn't pushed or checked, and sunken when it is pushed or checked.
BS_RIGHT | Windows 95 only: Right-justifies text in the button rectangle. However, if the button is a check box or radio button that does not have the BS_RIGHTBUTTON style, the text is right justified on the right side of the check box or radio button.
BS_RIGHTBUTTON | Windows 95 only: Positions a radio button's circle or a check box's square on the right side of the button rectangle. Same as the BS_LEFTTEXT style.
BS_TEXT | Windows 95 only: Specifies that the button displays text.
BS_TOP | Windows 95 only: Places text at the top of the button rectangle.
BS_VCENTER | Windows 95 only: Places text in the middle (vertically) of the button rectangle.

+$#A Button States

This section discusses how selecting a button changes its state and how the application should respond.

+$#A Button Selection

The user can select a button in three ways: by clicking it with the mouse, by tabbing to it and then pressing the ENTER key, or (if the button is part of a group defined by the WS_GROUP style) by tabbing to the selected button in the group and using the arrow keys to move within that group. The two tabbing methods are part of the predefined keyboard interface provided by Windows. For a complete description of this interface, see Dialog Boxes.

Selecting a button typically causes the following events:

• Windows gives the button the keyboard focus.

• The button sends its parent window a message to notify it of the selection.

• The parent window (or Windows) sends the button a message to change its state.

• The parent window (or Windows) repaints the button to reflect its new state.

The following topics discuss these events and button states in greater detail.

+$#A Elements of a Button State

A button's state can be characterized by its focus state, push state, and check state. The focus state applies to a check box, radio button, push button, or owner-drawn button. A button receives the keyboard focus when the user selects it and loses the focus when the user selects another control. Only one control can have the keyboard focus at a time.

When a button has the keyboard focus, the system typically highlights the text, icon, or bitmap of a button by surrounding it with a dotted line. In addition, a push button has a heavy dark border when it has the focus. Windows automatically changes the highlight for an automatic button, but the application must change the highlight for a nonautomatic button by sending messages.

The push state applies to a push button, check box, radio button, or three-state check box, but does not apply to other buttons. The push state of a button can be either pushed or not pushed. When a push button (or any button with the BS_PUSHLIKE style) is pushed, the button is drawn as a sunken button. When it is not pushed, it is drawn as a raised button. When a check box, radio button, or three-state check box is pushed, the background of the button is grayed. When it is not pushed, the background of the button is not grayed.

The check state applies to a check box, radio button, or three-state check box, but does not apply to other buttons. The state can be checked, unchecked, or (for three-state check boxes) indeterminate. A check box is checked when it contains a check mark, and is unchecked when it does not. A radio button is checked when it contains a black dot; and is unchecked when it does not. A three-state check box is checked when it contains a check mark, is unchecked when it does not, and is indeterminate when it contains a grayed box. Windows automatically changes the check state of an automatic button, but the application must change the check state of a nonautomatic button.

+$#A Changes to a Button State

When the user selects a button, it is generally necessary to change one or more of the button's state elements. Windows automatically changes the focus state for all button types, the push state for push buttons or buttons with the BS_PUSHLIKE style, and the check state for all automatic buttons. The application must make all other state changes, taking into account the button's type, style, and current state. The following list shows the state elements that must be changed for each button type:

• A check box must change the check state.

• A radio button must change the check state. It may also be necessary to change the check state of other radio buttons in the same group to ensure the mutually exclusive nature of radio buttons.

• Because the state of an owner-drawn button is application dependent, what the application must change in the button can vary. No elements of a group box must be changed, because users cannot select group boxes.

An application can determine a button's state by sending it a BM_GETCHECK or BM_GETSTATE message; the application can set a button's state by sending it a BM_SETCHECK or BM_SETSTATE message.

+$#A Notification Messages from Buttons

When the user selects a button, its state changes, and the button sends notification messages to its parent window. For example, a push button control sends the BN_CLICKED notification message whenever the user chooses the button. In all cases, the low-order word of the wParam parameter contains the control identifier, the high-order word of wParam contains the notification code, and the lParam parameter contains the control window handle.

Portability Issue The placement of notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x − based applications that process notification messages must be modified in this respect when they are ported to Win32-based applications.

Both the message and the parent window's response depend on the type, style, and current state of the button. Following are the button notification messages an application should monitor and process.

Message | Description
BN_CLICKED | The user clicked a button.
BN_DBLCLK | The user double-clicked a button.
BN_DISABLE | A button is disabled.
BN_PUSHED | The user pushed a button.
BN_KILLFOCUS | The button lost the keyboard focus.
BN_PAINT | The button should be painted.
BN_SETFOCUS | The button gained the keyboard focus.
BN_UNPUSHED | The button is no longer pushed.

A button sends the BN_DISABLE, BN_PUSHED, BN_KILLFOCUS, BN_PAINT, BN_SETFOCUS, and BN_UNPUSHED notification messages only if it has the BS_NOTIFY style. It sends the BN_CLICKED and BN_DBLCLK notification messages regardless of the BS_NOTIFY style.

For automatic buttons, the operating system performs pushing, unpushing, and painting. In this case, the application typically processes only the BN_CLICKED and BN_DBLCLK notification messages. For buttons that are not automatic, the application typically responds to the notification message by sending a message to change the state of the button. For information about sending messages to buttons, Messages to Buttons.

When the user selects an owner-drawn button, the button sends its parent window a WM_DRAWITEM message containing the identifier of the control to be drawn and information about its dimensions and state. For more information about this message, see Using Owner-Drawn Buttons.

+$#A Messages to Buttons

A parent window can send messages to a button in an overlapped or child window by using the SendMessage function, or it can send messages to a button in a dialog box by using the SendDlgItemMessage, CheckDlgButton, CheckRadioButton, and IsDlgButtonChecked functions.

An application can use the BM_GETCHECK message to retrieve the check state of check box or radio button. An application can also use the BM_GETSTATE message to retrieve the button's current states (the check state, push state, and focus state). To get information about a specific state, use a bitmask on the returned state value.

The BM_SETCHECK message sets the check state of a check box or radio button; the message returns zero. The BM_SETSTATE message sets the push state of a button; this message also returns zero. The BM_SETSTYLE message changes the style of a button. It is designed for changing button styles within a type (for example, changing a check box to an automatic check box). It is not designed for changing between types (for example, changing a check box to a radio button). An application should not change a button from one type to another.

A button of the BS_BITMAP or BS_ICON style displays a bitmap or icon instead of text. The BM_SETIMAGE message associates the handle of a bitmap or icon with a button. The BM_GETIMAGE message retrieves the handle of the bitmap or icon associated with a button.

An application can also use the DM_GETDEFID message to retrieve the identifier of the default push button control in a dialog box. An application can use the DM_SETDEFID message to set the default push button for a dialog box.

Calling the CheckDlgButton or CheckRadioButton function is equivalent to sending a BM_SETCHECK message. Calling the IsDlgButtonChecked function is equivalent to sending a BM_GETCHECK message.

+$#A Button Color Messages

Windows provides default color values for buttons. The system sends a WM_CTLCOLORBTN message to a button's parent window before the button is drawn. This message contains a handle of the button's device context and a handle of the child window. The parent window can use these handles to change the button's text and background colors. The following table shows the default button-color values.

Value | Element colored
COLOR_BTNFACE | Button faces.
COLOR_BTNHIGHLIGHT | Highlight area (the top and left edges) of a button.
COLOR_BTNSHADOW | Shadow area (the bottom and right edges) of a button.
COLOR_BTNTEXT | Regular (nongrayed) text in buttons.
COLOR_GRAYTEXT | Disabled (gray) text in buttons. This color is set to 0 if the current display driver does not support a solid gray color.
COLOR_WINDOW | Window backgrounds.
COLOR_WINDOWFRAME | Window frames.
COLOR_WINDOWTEXT | Text in windows.

An application can retrieve the default values for these colors by calling the GetSysColor function, or set the values by calling the SetSysColors function. For more information about system colors, see System Information. For more information about how colors are used with controls, see Controls.

Portability Issue The WM_CTLCOLOR message has been replaced by the set of control-color messages. When porting your Windows 3.x−based application to the Win32 API, you must modify any code that processes the WM_CTLCOLOR message.

+$#A Button Default Message Processing

The window procedure for the predefined button control window class carries out default processing for all messages that the button control procedure does not process. When the button control procedure returns FALSE for any message, the predefined window procedure checks the messages and performs the default actions listed in the following table.

Message | Default action
BM_CLICK | Sends the button a WM_LBUTTONDOWN and a WM_LBUTTONUP message, and sends the parent window a BN_CLICKED notification message.
BM_GETCHECK | Returns the check state of the button.
BM_GETIMAGE | Returns the handle of the bitmap or icon associated with the button or NULL if the button has no bitmap or icon.
BM_GETSTATE | Returns the current check state, push state, and focus state of the button.
BM_SETCHECK | Sets the check state for all styles of radio buttons and check boxes. If the wParam parameter is greater than zero for radio buttons, the button is given the WS_TABSTOP style.
BM_SETIMAGE | Associates the specified bitmap or icon handle with the button and returns the handle of the previous bitmap or icon.
BM_SETSTATE | Sets the push state of the button. For owner-drawn buttons, a WM_DRAWITEM message is sent to the parent window if the state of the button has changed.
BM_SETSTYLE | Sets the button style. If the low-order word of the lParam parameter is TRUE, the button is redrawn.
WM_CHAR | Checks a check box or automatic check box when the user presses the plus (+) or equal (=) keys. Unchecks a check box or automatic check box when the user presses the minus (−) key.
WM_ENABLE | Paints the button.
WM_ERASEBKGND | Erases the background for owner-drawn buttons. The backgrounds of other buttons are erased as part of the WM_PAINT and WM_ENABLE processing.
WM_GETDLGCODE | Returns values indicating the type of input processed by the default button procedure, as shown in the following table.

Button style |  | Returns
BS_AUTOCHECKBOX |
 | DLGC_WANTCHARS | DLGC_BUTTON
BS_AUTORADIOBUTTON |
 | DLGC_RADIOBUTTON
BS_CHECKBOX |
 | DLGC_WANTCHARS | DLGC_BUTTON
BS_DEFPUSHBUTTON |
 | DLGC_DEFPUSHBUTTON
BS_GROUPBOX |
 | DLGC_STATIC
BS_PUSHBUTTON |
 | DLGC_UNDEFPUSHBUTTON
BS_RADIOBUTTON |
 | DLGC_RADIOBUTTON

WM_GETFONT | Returns a handle of the current font.
WM_KEYDOWN | Pushes the button if the user presses the SPACEBAR.
WM_KEYUP | Releases the mouse capture for all cases except the TAB key.
WM_KILLFOCUS | Removes the focus rectangle from a button. For push buttons and default push buttons, the focus rectangle is invalidated. If the button has the mouse capture, the capture is released, the button is not clicked, and any push state is removed.
WM_LBUTTONDBLCLK | Sends a BN_DBLCLK notification message to the parent window for radio buttons and owner-drawn buttons. For other buttons, a double-click is processed as a WM_LBUTTONDOWN message.
WM_LBUTTONDOWN | Highlights the button if the position of the mouse cursor is within the button's client rectangle.
WM_LBUTTONUP | Releases the mouse capture if the button had the mouse capture.
WM_MOUSEMOVE | Performs the same action as WM_LBUTTONDOWN, if the button has the mouse capture. Otherwise, no action is performed.
WM_NCCREATE | Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button.
WM_NCHITTEST | Returns HTTRANSPARENT, if the button control is a group box.
WM_PAINT | Draws the button according to its style and current state.
WM_SETFOCUS | Draws a focus rectangle on the button getting the focus. For radio buttons and automatic radio buttons, the parent window is sent a BN_CLICKED notification message.
WM_SETFONT | Sets a new font and optionally updates the window.
WM_SETTEXT | Sets the text of the button. In the case of a group box, the message paints over the preexisting text before repainting the group box with the new text.
WM_SYSKEYUP | Releases the mouse capture for all cases except the TAB key.

The predefined window procedure passes all other messages to the DefWindowProc function for default processing.

+$#A Using Buttons

• Creating a button outside a dialog box

• Using buttons that are not owner-drawn

• Using owner-drawn buttons

+$#A Creating a Button Outside a Dialog Box

The following example shows how to use the CreateWindow function to create a default push button.

hwndButton = CreateWindow(

"BUTTON", // predefined class

"OK", // button text

WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // styles

// Size and position values are given explicitly, because

// the CW_USEDEFAULT constant gives zero values for buttons.

10, // starting x position

10, // starting y position

100, // button width

100, // button height

hwnd, // parent window

NULL, // No menu

(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),

NULL); // pointer not needed

+$#A Using Buttons that Are Not Owner-Drawn

The example in this section is the window procedure for a dialog box, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10611 0 /a "SDK_04.bmp"}

The check boxes and radio buttons in the Buttons dialog box are automatic. The check boxes are three-state. The Clear colors push button is a default push button. The check boxes, radio buttons, and push buttons are defined in the header file of the application, as follows.

#define IDB_BOX1 101 // first check box

#define IDB_BOX2 102 // second check box

#define IDB_BOX3 103 // third check box

#define IDB_REDBACK 104 // top radio button

#define IDB_BLUEBACK 105 // bottom radio button

#define IDB_CLEARBOXES 107 // top push button

#define IDB_CLEARBACK 108 // bottom push button

HBRUSH hbrRed, hbrBlue, hbrWhite;

BOOL fRedBack, fBlueBack, fClearColor; // background-state flags

Note that it is not necessary to define IDOK, the identifier for the OK push button.

In the following window procedure, the WM_CTLCOLORDLG message notifies the application that the dialog box is about to be drawn. If the user presses the Clear colors button (signified by the fClearColor flag), the procedure uses the SendDlgItemMessage function to uncheck the check boxes and radio buttons. The BN_CLICKED notification message contains the identifiers of the buttons.

LRESULT APIENTRY ButtonProc(hDlg, message, wParam, lParam)

HWND hDlg; // window handle of dialog box

UINT message; // type of message

UINT wParam; // message-specific information

LONG lParam;

{

LRESULT lState;

switch (message) {

case WM_INITDIALOG:

hbrRed = CreateSolidBrush(RGB(255, 0, 0));

hbrBlue = CreateSolidBrush(RGB(0, 0, 255));

hbrWhite = GetStockObject(WHITE_BRUSH);

return TRUE;

case WM_CTLCOLORDLG:

if (fRedBack) {

fRedBack = FALSE;

return (LRESULT) hbrRed;

}

else if (fBlueBack) {

fBlueBack = FALSE;

return (LRESULT) hbrBlue;

}

else if (fClearColor) {

fClearColor = FALSE;

// Uncheck all check boxes and radio buttons.

SendDlgItemMessage(hDlg, // window handle

IDB_BOX1, // button identifier

BM_SETCHECK, // message

0, // check state unchecked)

0); // must be zero

SendDlgItemMessage(hDlg, IDB_BOX2,

BM_SETCHECK, 0, 0);

SendDlgItemMessage(hDlg, IDB_BOX3,

BM_SETCHECK, 0, 0);

SendDlgItemMessage(hDlg, IDB_REDBACK,

BM_SETCHECK, 0, 0);

SendDlgItemMessage(hDlg, IDB_BLUEBACK,

BM_SETCHECK, 0, 0);

}

return (LRESULT) hbrWhite;

case WM_COMMAND:

if (wParam == IDOK) {

EndDialog(hDlg, TRUE);

return TRUE;

}

if (HIWORD(wParam) == BN_CLICKED) {

switch (LOWORD(wParam)) {

case IDB_BOX1:

// Retrieve the state of the

// check box.

lState = SendDlgItemMessage(

hDlg, IDB_BOX1, BM_GETSTATE,

0, 0);

// The box-painting function is

// application defined.

BoxPainter(

hDlg, // window handle

1, // box to paint

lState); // state of box

break;

case IDB_BOX2:

lState = SendDlgItemMessage(

hDlg, IDB_BOX2, BM_GETSTATE,

0, 0);

BoxPainter(hDlg, 2, lState);

break;

case IDB_BOX3:

lState = SendDlgItemMessage(

hDlg, IDB_BOX3, BM_GETSTATE,

0, 0);

BoxPainter(hDlg, 3, lState);

break;

case IDB_REDBACK:

fRedBack = TRUE;

InvalidateRect(hDlg, NULL,

TRUE);

break;

case IDB_BLUEBACK:

fBlueBack = TRUE;

InvalidateRect(hDlg, NULL,

TRUE);

break;

case IDB_CLEARBACK:

fClearColor = TRUE;

InvalidateRect(hDlg, NULL,

TRUE);

break;

case IDB_CLEARBOXES:

BoxPainter(hDlg, 4,

(LRESULT) 0);

break;

}

}

case WM_DESTROY:

DeleteObject(hbrRed);

DeleteObject(hbrBlue);

// Do not delete hbrWhite, because it is a

// stock object.

break;

}

return FALSE; // did not process a message

UNREFERENCED_PARAMETER(lParam);

}

+$#A Using Owner-Drawn Buttons

The parent window of an owner-drawn button typically responds to at least three messages for the button: WM_INITDIALOG, WM_COMMAND, and WM_DRAWITEM. It is not necessary to process the WM_MEASUREITEM message for owner-drawn buttons.

When you must paint an owner-drawn button, Windows sends the parent window a WM_DRAWITEM message whose lParam points to a DRAWITEMSTRUCT structure. Use this structure with all owner-drawn controls to provide the application with the information it requires to paint the control. The itemAction and itemState members of the DRAWITEMSTRUCT structure define how to paint an owner-drawn button.

The following example shows how to process WM_INITDIALOG, WM_DRAWITEM, and WM_COMMAND messages for owner-drawn buttons. This example demonstrates how to draw one of two bitmaps for a control, depending on whether the control is selected. You would typically use the wParam parameter of the WM_DRAWITEM message to identify the control; in this example, only one control is assumed.

LRESULT APIENTRY OwnDrawProc(hDlg, message, wParam, lParam)

HWND hDlg; // window handle of dialog box

UINT message; // type of message

UINT wParam; // message-specific information

LONG lParam;

{

HDC hdcMem;

LPDRAWITEMSTRUCT lpdis;

switch (message) {

case WM_INITDIALOG:

// hinst, hbm1 and hbm2 are defined globally.

hbm1 = LoadBitmap((HANDLE) hinst, "OwnBit1");

hbm2 = LoadBitmap((HANDLE) hinst, "OwnBit2");

return TRUE;

case WM_DRAWITEM:

lpdis = (LPDRAWITEMSTRUCT) lParam;

hdcMem = CreateCompatibleDC(lpdis->hDC);

if (lpdis->itemState & ODS_SELECTED) // if selected

SelectObject(hdcMem, hbm2);

else

SelectObject(hdcMem, hbm1);

// Destination

StretchBlt(

lpdis->hDC, // destination DC

lpdis->rcItem.left, // x upper left

lpdis->rcItem.top, // y upper left

// The next two lines specify the width and

// height.

lpdis->rcItem.right - lpdis->rcItem.left,

lpdis->rcItem.bottom - lpdis->rcItem.top,

hdcMem, // source device context

0, 0, // x and y upper left

32, // source bitmap width

32, // source bitmap height

SRCCOPY); // raster operation

DeleteDC(hdcMem);

return TRUE;

case WM_COMMAND:

if (wParam == IDOK

|| wParam == IDCANCEL) {

EndDialog(hDlg, TRUE);

return TRUE;

}

if (HIWORD(wParam) == BN_CLICKED) {

switch (LOWORD(wParam)) {

case IDB_OWNERDRAW:

.

. // application-defined processing

.

break;

}

}

break;

case WM_DESTROY:

DeleteObject(hbm1); // delete bitmaps

DeleteObject(hbm2);

break;

}

return FALSE;

UNREFERENCED_PARAMETER(lParam);

}

+$#AK Button Reference

The following functions and messages are associated with buttons.

+$#A Button Functions

Following are the functions used with buttons.

CheckDlgButton
CheckRadioButton
IsDlgButtonChecked

+$#A Button Messages

Following are the messages used with buttons.

BM_CLICK
BM_GETCHECK
BM_GETSTATE
BM_SETCHECK
BM_SETSTATE
BM_SETSTYLE
BN_CLICKED
BN_DISABLE
BN_HILITE
BN_PAINT
BN_UNHILITE
DM_GETDEFID
DM_SETDEFID
WM_CTLCOLORBTN
BN_DBLCLK
BN_DOUBLECLICKED
BN_KILLFOCUS
BN_SETFOCUS


Carets.rtf

+$#AK Carets

A caret is a flashing line, block, or bitmap in the client area of a window. The caret typically indicates the place at which text or graphics will be inserted. The following illustration shows some common variations in the appearance of the caret.

{ewc msdncd, EWGraphic, group10795 0 /a "SDK_01.bmp"}

Because only one window at a time can have the keyboard focus or be active, there is only one caret in the system. Generally, each window that accepts keyboard input must create the caret when it receives the keyboard focus and destroy the caret when it loses the keyboard focus. For more information on keyboard input, see Keyboard Input.

An application written for Microsoft® Windows® can create a caret, display or hide it, relocate the caret, and change its blink time.

+$#A About Carets

An application uses the CreateCaret function to specify the parameters for a caret. Windows forms a caret by inverting the pixel color within the rectangle specified by the caret's position, width, and height. The width and height are specified in logical units; therefore, the appearance of a caret is subject to the window's mapping mode.

After the caret is defined, an application uses the ShowCaret function to make the caret visible. When the caret appears, it automatically begins flashing. To display a solid caret, Windows inverts every pixel in the rectangle; to display a gray caret, Windows inverts every other pixel; to display a bitmap caret, Windows inverts only the white bits of the bitmap.

The elapsed time, in milliseconds, required to invert the caret is called the blink time. An application can determine the caret's blink time by using the GetCaretBlinkTime function. If it is necessary to define a specific blink time, use the SetCaretBlinkTime function to set the rate of the blink time to a specified number of milliseconds. The flash time is the elapsed time, in milliseconds, required to display, invert, and restore the caret's display. The flash time of a caret is twice as much as the blink time.

An application can determine the caret's position by using the GetCaretPos function. The position, in client coordinates, is copied to a POINT structure specified by a parameter in GetCaretPos. An application can move a caret in a window by using the SetCaretPos function. A window can move a caret only if it already owns the caret. SetCaretPos can move the caret whether it is visible or not.

+$#AK Removing a Caret

An application can use the HideCaret function to remove the caret from the screen. This is useful when your application must redraw the screen while processing a message, but must keep the caret out of the way. When the application finishes drawing, it can display the caret again by using the ShowCaret function. Hiding the caret does not destroy its shape or invalidate the insertion point. Hiding the caret is cumulative; that is, if the application calls HideCaret five times, it must also call ShowCaret five times before the caret will reappear.

An application can remove the caret from the screen and destroy its shape by using the DestroyCaret function. DestroyCaret destroys the caret only if the window involved in the current task owns the caret.

+$#A Using Carets

• Creating and displaying a caret

• Hiding a caret

• Destroying a caret

• Adjusting the blink time

• Processing keyboard input

+$#A Creating and Displaying a Caret

Upon receiving the keyboard focus, the window should create and display the caret. Use the CreateCaret function to create a caret in the given window. You can then call SetCaretPos to set the current position of the caret and ShowCaret to make the caret visible.

The system sends the WM_SETFOCUS message to the window receiving keyboard focus; therefore, an application should create and display the caret while processing this message.

HWND hwnd, /* window handle */

int x; /* horizontal coordinate of cursor */

int y; /* vertical coordinate of cursor */

int nWidth; /* width of cursor */

int nHeight; /* height of cursor */

char *lpszChar; /* pointer to character */

case WM_SETFOCUS:

/* Create a solid black caret. */

CreateCaret(hwnd, (HBITMAP) NULL, nWidth, nHeight);

/* Adjust the caret position, in client coordinates. */

SetCaretPos(x, y);

/* Display the caret. */

ShowCaret(hwnd);

break;

To create a caret based on a bitmap, you must specify a bitmap handle when using CreateCaret. You can use a graphics application to create the bitmap and a resource compiler to add the bitmap to your application's resources. Your application can then use the LoadBitmap function to load the bitmap handle. For example, you could replace the CreateCaret line in the preceding example with the following lines to create a bitmap caret.

/* Load the application-defined caret resource. */

hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));

/* Create a bitmap caret. */

CreateCaret(hwnd, hCaret, 0, 0);

Alternatively, you can use the CreateBitmap or CreateDIBitmap function to retrieve the handle of the caret bitmap. For more information about bitmaps, see Bitmaps.

If your application specifies a bitmap handle, CreateCaret ignores the width and height parameters. The bitmap defines the size of the caret.

+$#A Hiding a Caret

Whenever your application redraws a screen while processing a message other than WM_PAINT, it must make the caret invisible by using the HideCaret function. When your application is finished drawing, redisplay the caret by using the ShowCaret function. If your application processes the WM_PAINT message, it is not necessary to hide and redisplay the caret, because this function does this automatically.

The following code sample shows how to have your application hide the caret while drawing a character on the screen and while processing the WM_CHAR message.

HWND hwnd, /* window handle */

HDC hdc; /* device context */

case WM_CHAR:

switch (wParam) {

case 0x08:

.

. /* Process a backspace. */

.

break;

case 0x09:

.

. /* Process a tab. */

.

break;

case 0x0D:

.

. /* Process a carriage return. */

.

break;

case 0x1B:

.

. /* Process an escape. */

.

break;

case 0x0A:

.

. /* Process a linefeed. */

.

break;

default:

/* Process all other characters. */

/* Hide the caret. */

HideCaret(hwnd);

/* Draw the character on the screen. */

hdc = GetDC(hwnd);

SelectObject(hdc,

GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, x, y, lpszChar, 1);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

}

If your application calls the HideCaret function several times without calling ShowCaret, the caret will not be displayed until the application also calls ShowCaret the same number of times.

+$#A Destroying a Caret

When a window loses the keyboard focus, the system sends the WM_KILLFOCUS message to the window. Your application should destroy the caret while processing this message by using the DestroyCaret function. The following code shows how to destroy a caret in a window that no longer has the keyboard focus.

case WM_KILLFOCUS:

/*

* The window is losing the keyboard focus,

* so destroy the caret.

*/

DestroyCaret();

break;

+$#A Adjusting the Blink Time

Because there is only one caret in the system, use caution when your application must adjust the rate of the blink time, so as not to disrupt subsequent windows that may receive the keyboard focus. If your application changes the blink time, it must restore the blink time to its original value upon losing the keyboard focus. The GetCaretBlinkTime function returns the current blink time, in milliseconds, and the SetCaretBlinkTime function sets the blink time to the specified number of milliseconds.

If you anticipate that your application will adjust the blink time, add the following line to the WM_SETFOCUS message processing.

uOldBlink = GetCaretBlinkTime();

Then, you can restore the blink time to its original rate by including the following line in the WM_KILLFOCUS message processing.

SetCaretBlinkTime(uOldBlink);

+$#A Processing Keyboard Input

The following example demonstrates how to use a caret in a simple text editor. The example updates the caret position as the user types printable characters and uses various keys to move through the client area.

#define TEXTMATRIX(x, y) \

*(pTextMatrix + (y * nWindowCharsX) + x)

HINSTANCE hinst; /* current instance */

HBITMAP hCaret; /* caret bitmap */

HDC hdc; /* device context */

PAINTSTRUCT ps; /* client area paint info */

static char *pTextMatrix = NULL; /* points to text matrix */

static int nCharX, /* width of char. in logical units */

nCharY, /* height of char. in logical units */

nWindowX, /* width of client area */

nWindowY, /* height of client area */

nWindowCharsX, /* width of client area */

nWindowCharsY, /* height of client area */

nCaretPosX, /* x-position of caret */

nCaretPosY; /* y-position of caret */

static UINT uOldBlink; /* previous blink rate */

int x, y; /* x and y coordinates to use in text matrix */

TEXTMETRIC tm; /* font information */

LONG APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

switch (message) {

case WM_CREATE:

/*

* Select a fixed-width system font,

* and get the text metrics of the font.

*/

hdc = GetDC(hwnd);

SelectObject(hdc,

GetStockObject(SYSTEM_FIXED_FONT));

GetTextMetrics(hdc, &tm);

ReleaseDC(hwnd, hdc);

/* Save the avg. width and height of characters. */

nCharX = tm.tmAveCharWidth;

nCharY = tm.tmHeight;

return 0;

case WM_SIZE:

/*

* Determine the width of the client area, in pixels

* and in number of characters.

*/

nWindowX = LOWORD(lParam);

nWindowCharsX = max(1, nWindowX/nCharX);

/*

* Determine the height of the client area, in

* pixels and in number of characters.

*/

nWindowY = HIWORD(lParam);

nWindowCharsY = max(1, nWindowY/nCharY);

/* Clear the buffer that holds the text input. */

if (pTextMatrix != NULL) {

free(pTextMatrix);

}

/*

* If there is enough memory, allocate space for the

* text input buffer.

*/

pTextMatrix = malloc(nWindowCharsX * nWindowCharsY);

if (pTextMatrix == NULL) {

ErrorHandler("Not enough memory.");

}

else {

for (y = 0; y < nWindowCharsY; y++)

for (x = 0; x < nWindowCharsX; x++)

TEXTMATRIX(x, y) = ' ';

}

/* Move the caret to the origin. */

SetCaretPos(0, 0);

return 0;

case WM_KEYDOWN:

switch (wParam) {

case VK_HOME: /* Home */

nCaretPosX = 0;

break;

case VK_END: /* End */

nCaretPosX = nWindowCharsX - 1;

break;

case VK_PRIOR: /* Page Up */

nCaretPosY = 0;

break;

case VK_NEXT: /* Page Down */

nCaretPosY = nWindowCharsY -1;

break;

case VK_LEFT: /* Left arrow */

nCaretPosX = max(nCaretPosX - 1, 0);

break;

case VK_RIGHT: /* Right arrow */

nCaretPosX = min(nCaretPosX + 1,

nWindowCharsX - 1);

break;

case VK_UP: /* Up arrow */

nCaretPosY = max(nCaretPosY - 1, 0);

break;

case VK_DOWN: /* Down arrow */

nCaretPosY = min(nCaretPosY + 1,

nWindowCharsY - 1);

break;

case VK_DELETE: /* Delete */

/*

* Move all the characters that followed the

* deleted character (on the same line) one

* space back (to the left) in the matrix.

*/

for (x = nCaretPosX; x < nWindowCharsX; x++)

TEXTMATRIX(x, nCaretPosY) =

TEXTMATRIX(x + 1, nCaretPosY);

/*

* Replace the last character on the

* line with a space.

*/

TEXTMATRIX(nWindowCharsX - 1,

nCaretPosY) = ' ';

/*

* The application will draw outside the

* WM_PAINT message processing, so hide the

* caret.

*/

HideCaret(hwnd);

/*

* Redraw the line, adjusted for the

* deleted character.

*/

hdc = GetDC(hwnd);

SelectObject(hdc,

GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, nCaretPosX * nCharX,

nCaretPosY * nCharY,

&TEXTMATRIX(nCaretPosX, nCaretPosY),

nWindowCharsX - nCaretPosX);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

break;

}

/*

* Adjust the caret position based on the

* virtual-key processing.

*/

SetCaretPos(nCaretPosX * nCharX,

nCaretPosY * nCharY);

return 0;

case WM_CHAR:

switch (wParam) {

case 0x08: /* Backspace */

/*

* Move the caret back one space, and then

* process this like the DEL key.

*/

if (nCaretPosX > 0) {

nCaretPosX--;

SendMessage(hwnd, WM_KEYDOWN,

VK_DELETE, 1L);

}

break;

case 0x09: /* Tab */

/*

* Tab stops exist every four spaces, so add

* spaces until the user hits the next tab.

*/

do {

SendMessage(hwnd, WM_CHAR, ' ', 1L);

} while (nCaretPosX % 4 != 0);

break;

case 0x0D: /* Carriage return */

/*

* Go to the beginning of the next line.

* The bottom line wraps around to the top.

*/

nCaretPosX = 0;

if (++nCaretPosY == nWindowCharsY) {

nCaretPosY = 0;

}

break;

case 0x1B: /* Escape */

case 0x0A: /* Linefeed */

/* No processing. */

MessageBeep((UINT) -1);

break;

default: /* all other characters */

/* Add the character to the text buffer. */

TEXTMATRIX(nCaretPosX, nCaretPosY) =

(char) wParam;

/*

* The application will draw outside the

* WM_PAINT message processing, so hide the

* caret.

*/

HideCaret(hwnd);

/* Draw the character on the screen. */

hdc = GetDC(hwnd);

SelectObject(hdc,

GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc, nCaretPosX * nCharX,

nCaretPosY * nCharY,

&TEXTMATRIX(nCaretPosX, nCaretPosY), 1);

ReleaseDC(hwnd, hdc);

/* Display the caret. */

ShowCaret(hwnd);

/*

* Prepare to wrap around if you reached the

* end of the line.

*/

if (++nCaretPosX == nWindowCharsX) {

nCaretPosX = 0;

if (++nCaretPosY == nWindowCharsY) {

nCaretPosY = 0;

}

}

break;

}

/*

* Adjust the caret position based on the

* character processing.

*/

SetCaretPos(nCaretPosX * nCharX,

nCaretPosY * nCharY);

return 0;

case WM_PAINT:

/*

* Draw all the characters in the buffer, line by

* line.

*/

hdc = BeginPaint(hwnd, &ps);

SelectObject(hdc,

GetStockObject(SYSTEM_FIXED_FONT));

for (y = 0; y < nWindowCharsY; y++)

TextOut(hdc, 0, y * nCharY, &TEXTMATRIX(0, y),

nWindowCharsX);

EndPaint(hwnd, &ps);

case WM_SETFOCUS:

/*

* The window has the input focus. Load the

* application-defined caret resource.

*/

hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));

/* Create the caret. */

CreateCaret(hwnd, hCaret, 0, 0);

/* Adjust the caret position. */

SetCaretPos(nCaretPosX * nCharX,

nCaretPosY * nCharY);

/* Display the caret. */

ShowCaret(hwnd);

break;

case WM_KILLFOCUS:

/*

* The window is losing the input focus,

* so destroy the caret.

*/

DestroyCaret();

break;

default: /* passes it on if unprocessed */

return DefWindowProc(hwnd, message, wParam, lParam);

} /* ends switch (message) */

return NULL;

} /* ends main window procedure */

+$#A Caret Functions

The following functions are used with carets.

CreateCaret
DestroyCaret
GetCaretBlinkTime
GetCaretPos
HideCaret
SetCaretBlinkTime
SetCaretPos
ShowCaret


Clipboard.rtf

+$#AK Clipboard

The clipboard is a set of functions and messages that enable applications designed for the Microsoft® Win32® application programming interface (API) to transfer data. Because all applications have access to the clipboard, data can be easily transferred between applications or within an application.

This overview does not describe how to copy and paste linked or embedded objects. For information on these subjects, see the OLE Programmer's Reference in the OLE volume.

+$#A About the Clipboard

A memory object on the clipboard can be in any data format, called a clipboard format. Each format is identified by an unsigned integer value. For standard (predefined) clipboard formats, this value is a constant defined by the Win32 API; for registered clipboard formats, it is the return value of the RegisterClipboardFormat function.

Except for registering clipboard formats, individual windows perform most clipboard operations. Typically, a window procedure transfers information to or from the clipboard in response to the WM_COMMAND message.

The clipboard is user-driven. A window should transfer data to or from the clipboard only in response to a command from the user. A window must not use the clipboard to transfer data without the user's knowledge.

+$#A Clipboard Formats

A window can place more than one object on the clipboard, each representing the same information in a different clipboard format. Users need not be aware of the clipboard formats used for an object on the clipboard.

+$#A Registered Clipboard Formats

Many applications work with data that cannot be translated into a standard clipboard format without loss of information. Such applications can register new clipboard formats by using the RegisterClipboardFormat function. A clipboard format that is used by an application, as opposed to a standard clipboard format, is called a registered clipboard format. For example, if a word-processing application copied formatted text to the clipboard using a standard text format, the formatting information would be lost. The solution would be to register a new clipboard format, such as rich-text format (RTF).

If more than one application registers a clipboard format with exactly the same name, the clipboard format is registered only once. Both calls to the RegisterClipboardFormat function return the same value. In this way, two different applications can share data by using a registered clipboard format.

For a list of standard clipboard formats, see the SetClipboardData function.

+$#A Private Clipboard Formats

An application can identify a private clipboard format by defining a value in the range CF_PRIVATEFIRST through CF_PRIVATELAST. An application can use a private clipboard format for an application-defined data format that does not need to be registered with the system.

Data handles associated with private clipboard formats are automatically freed by the system. Windows that use private clipboard formats can use the WM_DESTROYCLIPBOARD message to free any related resources that are no longer needed.

For more information about the WM_DESTROYCLIPBOARD message, see Clipboard Ownership.

An application can place data handles on the clipboard by defining a private format in the range CF_GDIOBJFIRST through CF_GDIOBJLAST. When using values in this range, the data handle is not a handle to a GDI object, but is a handle allocated by the GlobalAlloc function with the GMEM_DDESHARE and GMEM_MOVEABLE flags. When the clipboard is emptied the system automatically deletes the object using the GlobalFree function

+$#A Multiple Clipboard Formats

A window can place more than one clipboard object on the clipboard, each representing the same information in a different clipboard format. When placing information on the clipboard, the window should provide data in as many formats as possible.

Clipboard formats that contain the most information should be placed on the clipboard first, followed by less descriptive formats. A window pasting information from the clipboard typically retrieves a clipboard object in the first format it recognizes. Because clipboard formats are enumerated in the order they are placed on the clipboard, the first recognized format is also the most descriptive.

For example, suppose a user copies styled text from a word-processing document. The window containing the document might first place data on the clipboard in a registered format, such as RTF. Subsequently, the window would place data on the clipboard in a less descriptive format, such as text (CF_TEXT).

When the content of the clipboard is pasted into another window, the window retrieves data in the most descriptive format it recognizes. If the window recognizes RTF, the corresponding data is pasted into the document. Otherwise, the text data is pasted into the document and the formatting information is lost.

+$#A Synthesized Clipboard Formats

The system implicitly converts data between certain clipboard formats: if a window requests data in a format that is not on the clipboard, the system converts an available format to the requested format. The system can convert data between the text formats CF_UNICODETEXT, CF_TEXT, and CF_OEMTEXT; between the metafile formats CF_ENHMETAFILE and CF_METAFILEPICT; and between the bitmap formats CF_BITMAP and CF_DIB. The system can also convert CF_DIB to CF_PALETTE.

If the system provides a conversion between multiple formats, there is no advantage to placing multiple formats on the clipboard. When copying bitmaps, it is best to place only the CF_DIB format on the clipboard. This is because the colors in a device-dependent bitmap (CF_BITMAP) are relative to the system palette, which may change before the bitmap is pasted. If only the CF_DIB format is on the clipboard and a window requests the CF_BITMAP format, the system renders the device-dependent bitmap (DIB) using the current palette at that time.

If you place the CF_BITMAP format on the clipboard (and not CF_DIB), the system renders the CF_DIB clipboard format as soon as the clipboard is closed. This ensures that the correct palette is used to generate the DIB. Conversions between other clipboard formats occur upon demand.

+$#A Clipboard Operations

A window should use the clipboard when cutting, copying, or pasting data. A window places data on the clipboard for cut and copy operations and retrieves data from the clipboard for paste operations. The following sections describe these operations and related issues.

To place data on or retrieve data from the clipboard, a window must first open the clipboard by using the OpenClipboard function. Only one window can have the clipboard open at a time. When it has finished, the window must close the clipboard by calling the CloseClipboard function.

+$#A Cut and Copy Operations

To place information on the clipboard, a window first clears any previous clipboard content by using the EmptyClipboard function. This function sends the WM_DESTROYCLIPBOARD message to the previous clipboard owner, frees resources associated with data on the clipboard, and assigns clipboard ownership to the window that has the clipboard open.

After emptying the clipboard, a window places data on the clipboard in as many clipboard formats as possible. For each format, the window calls the SetClipboardData function, specifying the format identifier and a global memory handle. The memory handle can be NULL, indicating that the window renders the data on request. (For more information about the rendering of data, see Delayed Rendering.) A window should place data on the clipboard in order from the most descriptive clipboard format to the least descriptive.

+$#A Paste Operations

To retrieve paste information from the clipboard, a window first determines the clipboard format to retrieve. Typically, a window enumerates the available clipboard formats by using the EnumClipboardFormats function and uses the first format it recognizes. This method selects the best available format according to the priority set when the data was placed on the clipboard.

Alternatively, a window can use the GetPriorityClipboardFormat function. This function identifies the best available clipboard format according to a specified priority. A window that recognizes only one clipboard format can simply determine whether that format is available by using the IsClipboardFormatAvailable function.

After determining the clipboard format to use, a window calls the GetClipboardData function. This function returns the handle of a global memory object containing data in the specified format. A window can briefly lock the memory object in order to examine or copy the data. However, a window should not free the object or leave it locked for a long period of time.

+$#A Clipboard Ownership

The clipboard owner is the window associated with the information on the clipboard. A window becomes the clipboard owner when it places data on the clipboard ⎯ specifically, when it calls the EmptyClipboard function. The window remains the clipboard owner until it is closed or another window empties the clipboard.

When the clipboard is emptied, the clipboard owner receives a WM_DESTROYCLIPBOARD message. Following are some reasons why a window might process this message:

• The window delayed rendering of one or more clipboard formats. In response to the WM_DESTROYCLIPBOARD message, the window might free resources it had allocated in order to render data on request. For more information about the rendering of data, see Delayed Rendering.

• The window placed data on the clipboard in a private clipboard format. The data for private clipboard formats is not freed by the system when the clipboard is emptied. Therefore, the clipboard owner should free the data upon receiving the WM_DESTROYCLIPBOARD message. For more information about private clipboard formats, see Clipboard Formats.

• The window placed data on the clipboard using the CF_OWNERDISPLAY clipboard format. In response to the WM_DESTROYCLIPBOARD message, the window might free resources it had used to display information in the clipboard viewer window. For more information about this alternative format, see Owner-Display Format.

+$#A Delayed Rendering

When placing a clipboard format on the clipboard, a window can delay rendering the data in that format until the data is needed. To do so, an application can specify NULL for the hData parameter of the SetClipboardData function. This is useful if the application supports several clipboard formats, some or all of which are time-consuming to render. By passing a NULL handle, a window renders complex clipboard formats only when and if they are needed.

If a window delays rendering a clipboard format, it must be prepared to render the format upon request for as long as it is the clipboard owner. The system sends the clipboard owner a WM_RENDERFORMAT message when a request is received for a specific format that has not been rendered. Upon receiving this message, the window should call the SetClipboardData function to place a global memory handle on the clipboard in the requested format.

If the clipboard owner is destroyed and has delayed rendering some or all clipboard formats, it receives the WM_RENDERALLFORMATS message. Upon receiving this message, the window should place valid memory handles on the clipboard for all clipboard formats that it provides. This ensures that these formats remain available after the clipboard owner is destroyed.

An application should not open the clipboard before calling SetClipboardData in response to the WM_RENDERFORMAT or WM_RENDERALLFORMATS message.

Any clipboard formats that are not rendered in response to the WM_RENDERALLFORMATS message cease to be available to other applications and are no longer enumerated by the clipboard functions.

+$#A Memory and the Clipboard

A memory object that is to be placed on the clipboard should be allocated by using the GlobalAlloc function with the GMEM_DDESHARE and GMEM_MOVEABLE flags.

Once a memory object is placed on the clipboard, ownership of that memory handle is transferred to the system. When the clipboard is emptied and the memory object has one of the following clipboard formats, the system frees the memory object by calling the indicated Win32 function:

Function called to free object | Clipboard format
DeleteMetaFile | CF_DSPENHMETAFILE
 | CF_DSPMETAFILEPICT
 | CF_ENHMETAFILE
 | CF_METAFILEPICT
DeleteObject | CF_BITMAP
 | CF_DSPBITMAP
 | CF_PALETTE
GlobalFree | CF_DIB
 | CF_DSPTEXT
 | CF_OEMTEXT
 | CF_TEXT
 | CF_UNICODETEXT

When the clipboard is emptied of a memory object whose clipboard format is not shown in the preceding list, the application itself must free the memory object.

+$#A Clipboard Viewers

A clipboard viewer is a window that displays the current content of the clipboard. The clipboard viewer window is a convenience for the user and does not affect the data-transaction functions of the clipboard.

Typically, a clipboard viewer window can display at least the three most common formats: CF_TEXT, CF_BITMAP, and CF_METAFILEPICT. If a window does not make data available in any of these three formats, it should provide data in a display format or use the owner-display format.

A clipboard viewer chain is the linking together of two or more entities so that they are dependent upon one another for operation. This interdependency (chain) allows all running clipboard viewer applications to receive the messages sent to the current clipboard.

+$#A Clipboard Viewer Windows

A window adds itself to the clipboard viewer chain by calling the SetClipboardViewer function. The return value is the handle of the next window in the chain. Each clipboard viewer window must keep track of the next window in the clipboard viewer chain. When the content of the clipboard changes, the system sends a WM_DRAWCLIPBOARD message to the first window in the chain. After updating its display, each clipboard viewer window must pass this message on to the next window in the chain.

Before closing, a clipboard viewer window must remove itself from the clipboard viewer chain by calling the ChangeClipboardChain function. The system then sends a WM_CHANGECBCHAIN message to the first window in the chain.

For more information about processing the WM_DRAWCLIPBOARD and WM_CHANGECBCHAIN messages, see Creating a Clipboard Viewer Window.

+$#A Display Formats

A display format is a clipboard format used to display information in a clipboard viewer window. A clipboard owner that uses a private or registered clipboard format, and none of the most common standard formats, must provide data in a display format for viewing in a clipboard viewer window. The display formats are intended for viewing only and must not be pasted into a document.

The four display formats are: CF_DSPBITMAP, CF_DSPMETAFILEPICT, CF_DSPTEXT, and CF_DSPENHMETAFILE. These display formats are rendered in the same way as the standard formats, which are: CF_BITMAP, CF_TEXT, CF_METAFILEPICT, and CF_ENHMETAFILE.

+$#A Owner-Display Format

For a clipboard owner that does not use any of the common standard clipboard formats, an alternative to providing a display format is to use the owner-display (CF_OWNERDISPLAY) clipboard format.

By using the owner-display format, a clipboard owner can avoid the overhead of rendering data in an additional format by taking direct control over painting the clipboard viewer window. The clipboard viewer window sends messages to the clipboard owner whenever a portion of the window must be repainted or when the window is scrolled or resized.

+$#A Clipboard Commands

A user typically carries out clipboard operations by choosing commands from an application's Edit menu. Following is a brief description of the standard clipboard commands.

Command | Description
Cut | Places a copy of the current selection on the clipboard and deletes the selection from the document. The previous content of the clipboard is destroyed.
Copy | Places a copy of the current selection on the clipboard. The document remains unchanged. The previous content of the clipboard is destroyed.
Paste | Replaces the current selection with the content of the clipboard. The content of the clipboard is not changed.
Delete | Deletes the current selection from the document. The content of the clipboard is not changed. This command does not involve the clipboard, but it should appear with the clipboard commands on the Edit menu.

+$#A Using the Clipboard

• Implementing the Cut, Copy, and Paste commands

• Creating a clipboard viewer window

+$#A Implementing the Cut, Copy, and Paste Commands

This section describes how standard Cut, Copy, and Paste commands are implemented in an application. The example in this section uses these methods to place data on the clipboard using a registered clipboard format, the CF_OWNERDISPLAY format, and the CF_TEXT format. The registered format is used to represent rectangular or elliptical text windows, called labels.

+$#A Selecting Data

Before information can be copied to the clipboard, the user must select specific information to be copied or cut. An application should provide a means for the user to select information within a document and some kind of visual feedback to indicate selected data.

+$#A Creating an Edit Menu

An application should load an accelerator table containing the standard keyboard accelerators for the Edit menu commands. The TranslateAccelerator function must be added to the application's message loop for the accelerators to take effect. For more information about keyboard acceleratiors, see Keyboard Accelerators.

+$#A Processing the WM_INITMENUPOPUP Message

Not all clipboard commands are available to the user at any given time. An application should process the WM_INITMENUPOPUP message to enable the menu items for available commands and disable unavailable commands.

Following is the WM_INITMENUPOPUP case for an application named Label.

case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;

The InitMenu function is defined as follows.

void PASCAL InitMenu(HMENU hmenu)

{

int cMenuItems = GetMenuItemCount(hmenu);

int nPos;

UINT id;

UINT fuFlags;

PLABELBOX pbox = (hwndSelected == NULL) ? NULL :

(PLABELBOX) GetWindowLong(hwndSelected, 0);

for (nPos = 0; nPos < cMenuItems; nPos++) {

id = GetMenuItemID(hmenu, nPos);

switch (id) {

case IDM_CUT:

case IDM_COPY:

case IDM_DELETE:

if (pbox == NULL || !pbox->fSelected)

fuFlags = MF_BYCOMMAND | MF_GRAYED;

else if (pbox->fEdit)

fuFlags = (id != IDM_DELETE && pbox->ichSel

== pbox->ichCaret) ?

MF_BYCOMMAND | MF_GRAYED :

MF_BYCOMMAND | MF_ENABLED;

else

fuFlags = MF_BYCOMMAND | MF_ENABLED;

EnableMenuItem(hmenu, id, fuFlags);

break;

case IDM_PASTE:

if (pbox != NULL && pbox->fEdit)

EnableMenuItem(hmenu, id,

IsClipboardFormatAvailable(CF_TEXT) ?

MF_BYCOMMAND | MF_ENABLED :

MF_BYCOMMAND | MF_GRAYED

);

else

EnableMenuItem(hmenu, id,

IsClipboardFormatAvailable(

uLabelFormat) ?

MF_BYCOMMAND | MF_ENABLED :

MF_BYCOMMAND | MF_GRAYED

);

}

}

}

+$#A Processing the WM_COMMAND Message

To process menu commands, add the WM_COMMAND case to your application's main window procedure. Following is the WM_COMMAND case for the Label application's window procedure.

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDM_CUT:

if (EditCopy())

EditDelete();

break;

case IDM_COPY:

EditCopy();

break;

case IDM_PASTE:

EditPaste();

break;

case IDM_DELETE:

EditDelete();

break;

case IDM_EXIT:

DestroyWindow(hwnd);

}

break;

To carry out the Copy and Cut commands, the window procedure calls the application-defined EditCopy function. For more information about the EditCopy function, see Copying Information to the Clipboard. To carry out the Paste command, the window procedure calls the application-defined EditPaste function. For more information about the EditPaste function, see Pasting Information from the Clipboard.

+$#A Copying Information to the Clipboard

In the Label application, the application-defined EditCopy function copies the current selection to the clipboard. This function does the following:

1. Opens the clipboard by calling the OpenClipboard function.

2. Empties the clipboard by calling the EmptyClipboard function.

3. Calls the SetClipboardData function once for each clipboard format the application provides.

4. Closes the clipboard by calling the CloseClipboard function.

Depending on the current selection, the EditCopy function either copies a range of text or copies an application-defined structure representing an entire label. The structure, called LABELBOX, is defined as follows.

#define BOX_ELLIPSE 0

#define BOX_RECT 1

#define CCH_MAXLABEL 80

#define CX_MARGIN 12

typedef struct tagLABELBOX { /* box */

RECT rcText; /* coordinates of rectangle containing text */

BOOL fSelected; /* TRUE if the label is selected */

BOOL fEdit; /* TRUE if text is selected */

int nType; /* rectangular or elliptical */

int ichCaret; /* caret position */

int ichSel; /* with ichCaret, delimits selection */

int nXCaret; /* window position corresponding to ichCaret */

int nXSel; /* window position corresponding to ichSel */

int cchLabel; /* length of text in atchLabel */

TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

Following is the EditCopy function.

BOOL PASCAL EditCopy(VOID)

{

PLABELBOX pbox;

LPTSTR lptstrCopy;

HGLOBAL hglbCopy;

int ich1, ich2, cch;

if (hwndSelected == NULL)

return FALSE;

/* Open the clipboard, and empty it. */

if (!OpenClipboard(hwndMain))

return FALSE;

EmptyClipboard();

/* Get a pointer to the structure for the selected label. */

pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);

/* If text is selected, copy it using the CF_TEXT format. */

if (pbox->fEdit) {

if (pbox->ichSel == pbox->ichCaret) { /* zero-length */

CloseClipboard(); /* selection */

return FALSE;

}

if (pbox->ichSel < pbox->ichCaret) {

ich1 = pbox->ichSel;

ich2 = pbox->ichCaret;

}

else {

ich1 = pbox->ichCaret;

ich2 = pbox->ichSel;

}

cch = ich2 - ich1;

/* Allocate a global memory object for the text. */

hglbCopy = GlobalAlloc(GMEM_DDESHARE,

(cch + 1) * sizeof(TCHAR));

if (hglbCopy == NULL) {

CloseClipboard();

return FALSE;

}

/* Lock the handle and copy the text to the buffer. */

lptstrCopy = GlobalLock(hglbCopy);

memcpy(lptstrCopy, &pbox->atchLabel[ich1],

cch * sizeof(TCHAR));

lptstrCopy[cch] = (TCHAR) 0; /* null character */

GlobalUnlock(hglbCopy);

/* Place the handle on the clipboard. */

SetClipboardData(CF_TEXT, hglbCopy);

}

/* If no text is selected, the label as a whole is copied. */

else {

/*

* Save a copy of the selected label as a local memory

* object. This copy is used to render data on request.

* It is freed in response to the WM_DESTROYCLIPBOARD

* message.

*/

pboxLocalClip = (PLABELBOX) LocalAlloc(

LMEM_FIXED,

sizeof(LABELBOX)

);

if (pboxLocalClip == NULL) {

CloseClipboard();

return FALSE;

}

memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));

pboxLocalClip->fSelected = FALSE;

pboxLocalClip->fEdit = FALSE;

/*

* Place a registered clipboard format, the owner-display

* format, and the CF_TEXT format on the clipboard using

* delayed rendering.

*/

SetClipboardData(uLabelFormat, NULL);

SetClipboardData(CF_OWNERDISPLAY, NULL);

SetClipboardData(CF_TEXT, NULL);

}

/* Close the clipboard. */

CloseClipboard();

return TRUE;

}

+$#A Pasting Information from the Clipboard

In the Label application, the application-defined EditPaste function pastes the content of the clipboard. This function does the following:

1. Opens the clipboard by calling the OpenClipboard function.

2. Determines which of the available clipboard formats to retrieve.

3. Retrieves the handle of the data in the selected format by calling the GetClipboardData function.

4. Inserts a copy of the data into the document.

The handle returned by GetClipboardData is still owned by the clipboard, so an application must not free it or leave it locked.

5. Closes the clipboard by calling the CloseClipboard function.

If a label is selected and contains an insertion point, the EditPaste function inserts the text from the clipboard at the insertion point. If there is no selection or if a label is selected, the function creates a new label, using the application-defined LABELBOX structure on the clipboard. The LABELBOX structure is placed on the clipboard by using a registered clipboard format.

Following is the EditPaste function.

VOID PASCAL EditPaste(VOID)

{

PLABELBOX pbox;

HGLOBAL hglb;

LPTSTR lptstr;

PLABELBOX pboxCopy;

int cx, cy;

HWND hwnd;

pbox = hwndSelected == NULL ? NULL :

(PLABELBOX) GetWindowLong(hwndSelected, 0);

/*

* If the application is in edit mode,

* get the clipboard text.

*/

if (pbox != NULL && pbox->fEdit) {

if (!IsClipboardFormatAvailable(CF_TEXT))

return;

if (!OpenClipboard(hwndMain))

return;

hglb = GetClipboardData(CF_TEXT);

if (hglb != NULL) {

lptstr = GlobalLock(hglb);

if (lptstr != NULL) {

/*

* Call the application-defined ReplaceSelection

* function to insert the text and repaint the

* window.

*/

ReplaceSelection(hwndSelected, pbox, lptstr);

GlobalUnlock(hglb);

}

}

CloseClipboard();

return;

}

/*

* If the application is not in edit mode,

* create a label window.

*/

if (!IsClipboardFormatAvailable(uLabelFormat))

return;

if (!OpenClipboard(hwndMain))

return;

hglb = GetClipboardData(uLabelFormat);

if (hglb != NULL) {

pboxCopy = GlobalLock(hglb);

if (pboxCopy != NULL) {

cx = pboxCopy->rcText.right + CX_MARGIN;

cy = pboxCopy->rcText.top * 2 + cyText;

hwnd = CreateWindowEx(

WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT,

atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy,

hwndMain, NULL, hinst, NULL

);

if (hwnd != NULL) {

pbox = (PLABELBOX) GetWindowLong(hwnd, 0);

memcpy(pbox, pboxCopy, sizeof(LABELBOX));

ShowWindow(hwnd, SW_SHOWNORMAL);

SetFocus(hwnd);

}

GlobalUnlock(hglb);

}

}

CloseClipboard();

}

+$#A Registering a Clipboard Format

To register a clipboard format, add a call to the RegisterClipboardFormat function to your application's instance initialization function, as follows.

/* Register a clipboard format. */

LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,

sizeof(atchTemp));

uLabelFormat = RegisterClipboardFormat(atchTemp);

if (uLabelFormat == 0)

return FALSE;

+$# Processing the WM_RENDERFORMAT and WM_RENDERALLFORMATS Messages

If a window passes a NULL handle to the SetClipboardData function, it must process the WM_RENDERFORMAT and WM_RENDERALLFORMATS messages to render data upon request.

If the WM_RENDERFORMAT message delayed rendering a specific format and an application requested data in that format, the message is sent to the clipboard owner. If the WM_RENDERFORMAT message has delayed rendering one or more formats, the message is sent to the clipboard owner before it is destroyed.

To render a clipboard format, the window procedure must place a data handle on the clipboard by using the SetClipboardData function. It must not open the clipboard before calling SetClipboardData.

The Label application processes the WM_RENDERFORMAT and WM_RENDERALLFORMATS messages as follows.

case WM_RENDERFORMAT:

RenderFormat((UINT) wParam);

break;

case WM_RENDERALLFORMATS:

RenderFormat(uLabelFormat);

RenderFormat(CF_TEXT);

break;

In both cases, the window procedure calls the application-defined RenderFormat function, defined as follows.

void PASCAL RenderFormat(UINT uFormat)

{

HGLOBAL hglb;

PLABELBOX pbox;

LPTSTR lptstr;

int cch;

if (pboxLocalClip == NULL)

return;

if (uFormat == CF_TEXT) {

/* Allocate a buffer for the text. */

cch = pboxLocalClip->cchLabel;

hglb = GlobalAlloc(GMEM_DDESHARE,

(cch + 1) * sizeof(TCHAR));

if (hglb == NULL)

return;

/* Copy the text from pboxLocalClip. */

lptstr = GlobalLock(hglb);

memcpy(lptstr, pboxLocalClip->atchLabel,

cch * sizeof(TCHAR));

lptstr[cch] = (TCHAR) 0;

GlobalUnlock(hglb);

/* Place the handle on the clipboard. */

SetClipboardData(CF_TEXT, hglb);

}

else if (uFormat == uLabelFormat) {

hglb = GlobalAlloc(GMEM_DDESHARE, sizeof(LABELBOX));

if (hglb == NULL)

return;

pbox = GlobalLock(hglb);

memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));

GlobalUnlock(hglb);

SetClipboardData(uLabelFormat, hglb);

}

}

+$#A Processing the WM_DESTROYCLIPBOARD Message

A window can process the WM_DESTROYCLIPBOARD message in order to free any resources that it set aside to support delayed rendering. For example the Label application, when copying a label to the clipboard, allocates a local memory object. It then frees this object in response to the WM_DESTROYCLIPBOARD message, as follows.

case WM_DESTROYCLIPBOARD:

if (pboxLocalClip != NULL) {

LocalFree(pboxLocalClip);

pboxLocalClip = NULL;

}

break;

+$#A Using the Owner-Display Clipboard Format

If a window places information on the clipboard by using the CF_OWNERDISPLAY clipboard format, it must do the following:

• Process the WM_PAINTCLIPBOARD message. This message is sent to the clipboard owner when a portion of the clipboard viewer window must be repainted.

• Process the WM_SIZECLIPBOARD message. This message is sent to the clipboard owner when the clipboard viewer window has been resized or its content has changed.

Typically, a window responds to this message by setting the scroll positions and ranges for the clipboard viewer window. In response to this message, the Label application also updates a SIZE structure for the clipboard viewer window.

• Process the WM_HSCROLLCLIPBOARD and WM_VSCROLLCLIPBOARD messages. These messages are sent to the clipboard owner when a scroll bar event occurs in the clipboard viewer window.

• Process the WM_ASKCBFORMATNAME message. The clipboard viewer window sends this message to an application to retrieve the name of the owner-display format.

The window procedure for the Label application processes these messages, as follows.

LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)

HWND hwnd;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

static RECT rcViewer;

RECT rc;

LPRECT lprc;

LPPAINTSTRUCT lpps;

.

.

.

switch (msg) {

.

.

.

case WM_PAINTCLIPBOARD:

/* Determine the dimensions of the label. */

SetRect(&rc, 0, 0,

pboxLocalClip->rcText.right + CX_MARGIN,

pboxLocalClip->rcText.top * 2 + cyText

);

/* Center the image in the clipboard viewer window. */

if (rc.right < rcViewer.right) {

rc.left = (rcViewer.right - rc.right) / 2;

rc.right += rc.left;

}

if (rc.bottom < rcViewer.bottom) {

rc.top = (rcViewer.bottom - rc.bottom) / 2;

rc.bottom += rc.top;

}

/*

* Paint the image, using the specified PAINTSTRUCT

* structure, by calling the application-defined

* PaintLabel function.

*/

lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);

PaintLabel(lpps, pboxLocalClip, &rc);

GlobalUnlock((HGLOBAL) lParam);

break;

case WM_SIZECLIPBOARD:

/*

* Save the dimensions of the window in a static

* RECT structure.

*/

lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);

memcpy(&rcViewer, lprc, sizeof(RECT));

GlobalUnlock((HGLOBAL) lParam);

/*

* Set the scroll ranges to zero (thus eliminating

* the need to process the WM_HSCROLLCLIPBOARD and

* WM_VSCROLLCLIPBOARD messages).

*/

SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);

SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);

break;

case WM_ASKCBFORMATNAME:

LoadString(hinst, IDS_OWNERDISPLAY,

(LPSTR) lParam, wParam);

break;

.

.

.

default:

return DefWindowProc(hwnd, msg, wParam, lParam);

}

return 0;

}

+$#A Creating a Clipboard Viewer Window

A clipboard viewer window displays the current content of the clipboard, and receives messages when the clipboard content changes. To create a clipboard viewer window, your application must do the following:

• Add the window to the clipboard viewer chain.

• Process the WM_CHANGECBCHAIN message.

• Process the WM_DRAWCLIPBOARD message.

• Remove the window from the clipboard viewer chain before it is destroyed.

+$#A Adding a Window to the Clipboard Viewer Chain

A window adds itself to the clipboard viewer chain by calling the SetClipboardViewer function. The return value is the handle of the next window in the chain. A window must keep track of this value ⎯ for example, by saving it in a static variable named hwndNextViewer.

The following example adds a window to the clipboard viewer chain in response to the WM_CREATE message.

case WM_CREATE:

/* Add the window to the clipboard viewer chain. */

hwndNextViewer = SetClipboardViewer(hwnd);

break;

+$#A Processing the WM_CHANGECBCHAIN Message

A clipboard viewer window receives the WM_CHANGECBCHAIN message when another window is removing itself from the clipboard viewer chain. If the window being removed is the next window in the chain, the window receiving the message must unlink the next window from the chain. Otherwise, this message should be passed to the next window in the chain.

The following example shows the processing of the WM_CHANGECBCHAIN message.

case WM_CHANGECBCHAIN:

/* If the next window is closing, repair the chain. */

if ((HWND) wParam == hwndNextViewer)

hwndNextViewer = (HWND) lParam;

/* Otherwise, pass the message to the next link. */

else if (hwndNextViewer != NULL)

SendMessage(hwndNextViewer, uMsg, wParam, lParam);

break;

+$#A Removing a Window from the Clipboard Viewer Chain

To remove itself from the clipboard viewer chain, a window calls the ChangeClipboardChain function. The following example removes a window from the clipboard viewer chain in response to the WM_DESTROY message.

case WM_DESTROY:

ChangeClipboardChain(hwnd, hwndNextViewer);

PostQuitMessage(0);

break;

+$#A Processing the WM_DRAWCLIPBOARD Message

The WM_DRAWCLIPBOARD message notifies a clipboard viewer window that the content of the clipboard has changed. A window should do the following when processing the WM_DRAWCLIPBOARD message:

1. Determine which of the available clipboard formats to display.

2. Retrieve the clipboard data and display it in the window. Or if the clipboard format is CF_OWNERDISPLAY, send a WM_PAINTCLIPBOARD message to the clipboard owner.

3. Send the message to the next window in the clipboard viewer chain.

For an example of processing the WM_DRAWCLIPBOARD message, see the example listing in Example of a Clipboard Viewer.

+$#A Example of a Clipboard Viewer

The following example shows a simple clipboard viewer application.

HINSTANCE hinst;

UINT uFormat = (UINT)(-1);

BOOL fAuto = TRUE;

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

static HWND hwndNextViewer;

HDC hdc;

HDC hdcMem;

PAINTSTRUCT ps;

LPPAINTSTRUCT lpps;

RECT rc;

LPRECT lprc;

HGLOBAL hglb;

LPSTR lpstr;

HBITMAP hbm;

HENHMETAFILE hemf;

HWND hwndOwner;

switch (uMsg) {

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

/* Branch depending on the clipboard format. */

switch (uFormat) {

case CF_OWNERDISPLAY:

hwndOwner = GetClipboardOwner();

hglb = GlobalAlloc(GMEM_DDESHARE,

sizeof(PAINTSTRUCT));

lpps = GlobalLock(hglb);

memcpy(lpps, &ps, sizeof(PAINTSTRUCT));

GlobalUnlock(hglb);

SendMessage(hwndOwner, WM_PAINTCLIPBOARD,

(WPARAM) hwnd, (LPARAM) hglb);

GlobalFree(hglb);

break;

case CF_BITMAP:

hdcMem = CreateCompatibleDC(hdc);

if (hdcMem != NULL) {

if (OpenClipboard(hwnd)) {

hbm = (HBITMAP)

GetClipboardData(uFormat);

SelectObject(hdcMem, hbm);

GetClientRect(hwnd, &rc);

BitBlt(hdc, 0, 0, rc.right, rc.bottom,

hdcMem, 0, 0, SRCCOPY);

CloseClipboard();

}

DeleteDC(hdcMem);

}

break;

case CF_TEXT:

if (OpenClipboard(hwnd)) {

hglb = GetClipboardData(uFormat);

lpstr = GlobalLock(hglb);

GetClientRect(hwnd, &rc);

DrawText(hdc, lpstr, -1, &rc, DT_LEFT);

GlobalUnlock(hglb);

CloseClipboard();

}

break;

case CF_ENHMETAFILE:

if (OpenClipboard(hwnd)) {

hemf = GetClipboardData(uFormat);

GetClientRect(hwnd, &rc);

PlayEnhMetaFile(hdc, hemf, &rc);

CloseClipboard();

}

break;

case 0:

GetClientRect(hwnd, &rc);

DrawText(hdc, "The clipboard is empty.", -1,

&rc, DT_CENTER | DT_SINGLELINE |

DT_VCENTER);

break;

default:

GetClientRect(hwnd, &rc);

DrawText(hdc, "Unable to display format.", -1,

&rc, DT_CENTER | DT_SINGLELINE |

DT_VCENTER);

}

EndPaint(hwnd, &ps);

break;

case WM_SIZE:

if (uFormat == CF_OWNERDISPLAY) {

hwndOwner = GetClipboardOwner();

hglb = GlobalAlloc(GMEM_DDESHARE, sizeof(RECT));

lprc = GlobalLock(hglb);

GetClientRect(hwnd, lprc);

GlobalUnlock(hglb);

SendMessage(hwndOwner, WM_SIZECLIPBOARD,

(WPARAM) hwnd, (LPARAM) hglb);

GlobalFree(hglb);

}

break;

case WM_CREATE:

/* Add the window to the clipboard viewer chain. */

hwndNextViewer = SetClipboardViewer(hwnd);

break;

case WM_CHANGECBCHAIN:

/* If the next window is closing, repair the chain. */

if ((HWND) wParam == hwndNextViewer)

hwndNextViewer = (HWND) lParam;

/* Otherwise, pass the message to the next link. */

else if (hwndNextViewer != NULL)

SendMessage(hwndNextViewer, uMsg, wParam, lParam);

break;

case WM_DESTROY:

ChangeClipboardChain(hwnd, hwndNextViewer);

PostQuitMessage(0);

break;

case WM_DRAWCLIPBOARD: /* clipboard contents changed. */

/*

* Update the window by using Auto clipboard format.

*/

SetAutoView(hwnd);

/*

* Pass the message to the next window in clipboard

* viewer chain.

*/

SendMessage(hwndNextViewer, uMsg, wParam, lParam);

break;

case WM_INITMENUPOPUP:

if (!HIWORD(lParam))

InitMenu(hwnd, (HMENU) wParam);

break;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDM_EXIT:

DestroyWindow(hwnd);

break;

case IDM_AUTO:

SetAutoView(hwnd);

break;

default:

fAuto = FALSE;

uFormat = LOWORD(wParam);

InvalidateRect(hwnd, NULL, TRUE);

}

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return (LRESULT) NULL;

}

void PASCAL SetAutoView(HWND hwnd)

{

static UINT auPriorityList[] = {

CF_OWNERDISPLAY,

CF_TEXT,

CF_ENHMETAFILE,

CF_BITMAP

};

uFormat = GetPriorityClipboardFormat(auPriorityList, 4);

fAuto = TRUE;

InvalidateRect(hwnd, NULL, TRUE);

UpdateWindow(hwnd);

}

void PASCAL InitMenu(HWND hwnd, HMENU hmenu)

{

UINT uFormat;

char szFormatName[80];

LPCSTR lpFormatName;

UINT fuFlags;

UINT idMenuItem;

/*

* If a menu is not the display menu, no initialization is necessary. */

*/

if (GetMenuItemID(hmenu, 0) != IDM_AUTO)

return;

/* Delete all menu items except the first. */

while (GetMenuItemCount(hmenu) > 1)

DeleteMenu(hmenu, 1, MF_BYPOSITION);

/* Check or uncheck the Auto menu item. */

fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :

MF_BYCOMMAND | MF_UNCHECKED;

CheckMenuItem(hmenu, IDM_AUTO, fuFlags);

/* If there are no clipboard formats, return. */

if (CountClipboardFormats() == 0)

return;

/* Open the clipboard. */

if (!OpenClipboard(hwnd))

return;

/* Add a separator and then a menu item for each format. */

AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);

uFormat = EnumClipboardFormats(0);

while (uFormat) {

/*

* Call an application-defined function to get the name

* of the clipboard format.

*/

lpFormatName = GetPredefinedClipboardFormatName(uFormat);

/* For registered formats, get the registered name. */

if (lpFormatName == NULL) {

if (GetClipboardFormatName(uFormat, szFormatName,

sizeof(szFormatName)))

lpFormatName = szFormatName;

else

lpFormatName = "(unknown)";

}

/*

* Add a menu item for the format. For displayable

* formats, use the format ID for the menu ID.

*/

if (IsDisplayableFormat(uFormat)) {

fuFlags = MF_STRING;

idMenuItem = uFormat;

}

else {

fuFlags = MF_STRING | MF_GRAYED;

idMenuItem = 0;

}

AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);

uFormat = EnumClipboardFormats(uFormat);

}

CloseClipboard();

}

BOOL PASCAL IsDisplayableFormat(UINT uFormat)

{

switch (uFormat) {

case CF_OWNERDISPLAY:

case CF_TEXT:

case CF_ENHMETAFILE:

case CF_BITMAP:

return TRUE;

}

return FALSE;

}

+$#AK Clipboard Reference

The following functions and messages are associated with the Clipboard.

+$#A Clipboard Functions

Following are the functions used with the Clipboard.

ChangeClipboardChain
CloseClipboard
CountClipboardFormats
EmptyClipboard
EnumClipboardFormats
GetClipboardData
GetClipboardFormatName
GetClipboardOwner
GetClipboardViewer
GetOpenClipboardWindow
GetPriorityClipboardFormat
IsClipboardFormatAvailable
OpenClipboard
RegisterClipboardFormat
SetClipboardData
SetClipboardViewer

+$#A Clipboard Messages

Following are the messages used with the clipboard.

WM_ASKCBFORMATNAME
WM_CHANGECBCHAIN
WM_COPY
WM_CUT
WM_DESTROYCLIPBOARD
WM_DRAWCLIPBOARD
WM_HSCROLLCLIPBOARD
WM_PAINTCLIPBOARD
WM_PASTE
WM_RENDERALLFORMATS
WM_RENDERFORMAT
WM_SIZECLIPBOARD
WM_VSCROLLCLIPBOARD

Créé avec HelpNDoc Personal Edition: Générateur de documentation d'aide HTML gratuit


Clipping.rtf

+$#AK Clipping

Clipping is the process of limiting output to a region or path within the client area of an application's window.

+$#A About Clipping

Clipping is used by Microsoft® Win32®-based applications in a variety of ways. Word processing and spreadsheet applications clip keyboard input to keep it from appearing in the margins of a page or spreadsheet. Computer-aided design (CAD) and drawing applications clip graphics output to keep it from overwriting the edges of a drawing or picture.

A clipping region is a region with edges that are either straight lines or curves. A clip path is a region with edges that are straight lines, Bézier curves, or combinations of both. For more information about regions, see Regions. For more information about paths, see Paths.

+$#A Clipping Regions

A clipping region is one of the graphic objects that an application can select into a device context (DC). It is typically rectangular. Some device contexts provide a predefined or default clipping region while others do not. For example, if you obtain a device context handle from the BeginPaint function, the DC contains a predefined rectangular clipping region that corresponds to the invalid rectangle that requires repainting. However, if you obtain a device context handle by calling the CreateDC or GetDC function, the DC does not contain a default clipping region. For more information about device contexts returned by the BeginPaint function, see Painting and Drawing. For more information about device contexts returned by the CreateDC and GetDC functions, see Device Contexts.

Applications can perform a variety of operations on clipping regions. Some of these operations require a handle identifying the region and some do not. For example, an application can perform the following operations directly on a device context's clipping region:

• Determine whether graphics output appears within the region's borders by passing coordinates of the corresponding line, arc, bitmap, text, or filled shape to the PtVisible function.

• Determine whether part of the client area intersects a region by calling the RectVisible function.

• Move the existing region by a specified offset by calling the OffsetClipRgn function.

• Exclude a rectangular part of the client area from the current clipping region by calling the ExcludeClipRect function.

• Combine a rectangular part of the client area with the current clipping region by calling the IntersectClipRect function.

After obtaining a handle identifying the clipping region, an application can perform any operation that is common with regions; for example:

• Combining a copy of the current clipping region with a second region by calling the CombineRgn function.

• Compare a copy of the current clipping region to a second region by calling the EqualRgn function.

• Determine whether a point lies within the interior of a copy of the current clipping region by calling the PtInRegion function.

+$#A Clip Paths

Like a clipping region, a clip path is another graphics object that an application can select into a device context. Unlike a clipping region, a clip path is always created by an application and it is used for clipping to one or more irregular shapes. For example, an application can use the lines and curves that form the outlines of characters in a string of text to define a clip path.

To create a clip path, it's first necessary to create a path that describes the required irregular shape. Paths are created by calling the appropriate graphics device interface (GDI) drawing functions after calling the BeginPath function and before calling the EndPath function. This collection of functions is called a path bracket. For more information about paths and path brackets, see Paths.

After the path is created, it can be converted to a clip path by calling the SelectClipPath function, identifying a device context, and specifying a usage mode. The usage mode determines how Windows combines the new clip path with the device context's original clipping region. The following table describes the usage modes.

Mode | Description
RGN_AND | The clip path includes the intersection (overlapping areas) of the device context's clipping region and the current path.
RGN_COPY | The clip path is the current path.
RGN_DIFF | The clip path includes the device context's clipping region with any intersecting parts of the current path excluded.
RGN_OR | The clip path includes the union (combined areas) of the device context's clipping region and the current path.
RGN_XOR | The clip path includes the union of the device context's clipping region and the current path but excludes the intersection.

+$#A Using Clipping

This section contains example code that shows how to generate a clip path consisting of a character string. The example creates a logical font and uses it to draw a string within a clip path, then fills the path by drawing horizontal and vertical lines.

/*

* DoClipPat - Draws a clip path using the specified string

* Return value - TRUE if successful; FALSE otherwise

* lplf - address of a LOGFONT structure that defines the font to

* use to draw the clip path

* lpsz - address of a string to use for the clip path

*/

BOOL DoClipPath(LPLOGFONT lplf, LPSTR lpsz)

{

LOGFONT lf; /* logical font structure */

HFONT hfont; /* new logical font handle */

HFONT hfontOld; /* original logical font handle */

HDC hdc; /* display DC handle */

int nXStart, nYStart; /* drawing coordinates */

RECT rc; /* rectangle structure for painting window */

SIZE sz; /* size structure that receives text extents */

int nStrLen; /* length of the string */

int i; /* loop counter */

/* Retrieve a cached DC for the window. */

hdc = GetDC(hwnd);

/* Erase the current window contents. */

GetClientRect(hwnd, &rc);

FillRect(hdc, &rc, GetStockObject(WHITE_BRUSH));

/*

* Use the specified font to create a logical font and select it

* into the DC.

*/

hfont = CreateFontIndirect(lplf);

if (hfont == NULL)

return FALSE;

hfontOld = SelectObject(hdc, hfont);

/* Create a clip path. */

nStrLen = lstrlen(lpsz);

BeginPath(hdc);

TextOut(hdc, nXStart, nYStart, lpsz, nStrLen);

EndPath(hdc);

SelectClipPath(hdc, RGN_DIFF);

/*

* Retrieve the dimensions of the rectangle surrounding

* the text.

*/

GetTextExtentPoint32(hdc, lpsz, nStrLen, &sz);

/* Draw horizontal lines through the clip path. */

for (i = nYStart + 1; i < (nYStart + sz.cy); i += 3) {

MoveToEx(hdc, nXStart, i, (LPPOINT) NULL);

LineTo(hdc, (nXStart + sz.cx), i);

}

/* Draw vertical lines through the clip path. */

for (i = nXStart + 1; i < (nXStart + sz.cx); i += 3){

MoveToEx(hdc, i, nYStart, (LPPOINT) NULL);

LineTo(hdc, i, (nYStart + sz.cy));

}

/* Select the original font into the DC and release the DC. */

SelectObject(hdc, hfontOld);

DeleteObject(hfont);

ReleaseDC(hwnd, hdc);

return TRUE;

}

For an example that demonstrates how an application creates a rectangular clipping region, see Regions.

+$#A Clipping Functions

Following are the functions that can be used with clipping.

ExcludeClipRect
ExtSelectClipRgn
GetClipBox
GetClipRgn
GetMetaRgn
IntersectClipRect
OffsetClipRgn
PtVisible
RectVisible
SelectClipPath
SelectClipRgn
SetMetaRgn

Créé avec HelpNDoc Personal Edition: Générateur d'aides CHM gratuit


Colors.rtf

+$#AK Colors

Color is an important element in the pictures and images generated by Windows-based applications. This overview describes how Windows-based applications can manage and use colors with pens, brushes, text, or bitmaps.

+$#A About Colors

Color can be used to communicate ideas, show relationships between items, and improve the appeal and quality of output. Windows enables applications to discover the color capabilities of given devices and to choose from the available colors those that best suit their needs.

Although not described in this overview, image color matching is an important feature of color management that helps ensure that color images look the same whether displayed on screen or printed on paper. For more information, see Image Color Matching.

+$#A Color Basics

The color capabilities of devices, such as displays and printers, can range from monochrome to thousands of colors. Because an application may need to generate output for devices throughout this range, it should be prepared be prepared to handle varying color capabilities.

An application can discover the number of colors available for a given device by using the GetDeviceCaps function to retrieve the NUMCOLORS value. This value specifies the count of colors available for use by the application. Usually, this count corresponds to a physical property of the output device, such as the number of inks in the printer or the number of distinct color signals the display adapter can transmit to the monitor.

Although the NUMCOLORS value specifies the count of colors, it does not identify what the available colors are. An application can discover what colors are available by enumerating all pens having the PS_SOLID type. Because the device driver that supports a given device usually has a full range of solid pens and because Windows requires that solid pens have only colors that the device can generate, enumerating these pens is often equivalent to enumerating the colors. An application can enumerate the pens by using the EnumObjects function.

+$#A Color Values

By default, Windows defines color as a combination of three primary colors ⎯ red, green, and blue. Windows identifies a color by giving it a color value (sometimes called an RGB triplet), which consists of three 8-bit values specifying the intensities of its color components. Black has the minimum intensity for red, green, and blue, so the color value for black is (0, 0, 0). White has the maximum intensity for red, green, and blue, so its color value is (255, 255, 255).

Note If image color matching is enabled, the definition of color and the meaning of a color value depends on the type of color space that is currently set for the device context.

Windows and applications use parameters and variables having the COLORREF type to pass and store color values. For example, the EnumObjects function identifies the color of each pen by setting the lopnColor member in a LOGPEN structure to a color value. Applications can extract the individual values of the red, green, and blue components from a color value by using the GetRValue, GetGValue, and GetBValue macros, respectively. Applications can create a color value from individual component values by using the RGB macro. When creating or examining a logical palette, an application uses the RGBQUAD structure to define color values and to examine individual component values.

+$#A Color Approximations and Dithering

Although an application can use color without regard to the color capabilities of the device, the resulting output may not be as informative and pleasing as output for which color is carefully chosen. Few, if any, devices guarantee an exact match for every possible color value; therefore, if an application requests a color that the device cannot generate, Windows approximates that color by using a color that the device can generate. For example, if an application attempts to create a red pen for a black and white printer, it will receive a black pen instead ⎯ Windows uses black as the approximation for red.

An application can discover whether Windows will approximate a given color by using the GetNearestColor function. The function takes a color value and returns the color value of the closest matching color the device can generate. The method Windows uses to determine this approximation depends on the device driver and its color capabilities. In most cases, the approximated color's overall intensity is closest to that of the requested color.

When an application creates a pen or sets the color for text, Windows always approximates a color if no exact match exists. When an application creates a solid brush, Windows may attempt to simulate the requested color by dithering. Dithering simulates a color by alternating two or more colors in a pattern. For example, different shades of pink can be simulated by alternating different combinations of red and white. Depending on the colors and the pattern, dithering can produce reasonable simulations. It is most useful for monochrome devices, because it expands the number of available "colors" well beyond simple black and white.

The method used to create dithered colors depends on the device driver. Most device drivers use a standard dithering algorithm, which generates a pattern based on the intensity values of the requested red, green, and blue colors. In general, any requested color that cannot be generated by the device is subject to simulation, but an application is not notified when Windows simulates a color. Furthermore, an application cannot modify or change the dithering algorithm of the device driver. An application, however, can bypass the algorithm by creating and using pattern brushes. In this way, the application creates its own dithered colors by combining solid colors in the bitmap that it uses to create the brush.

+$#A Color in Bitmaps

Windows handles colors in bitmaps differently than colors in pens, brushes, and text. Compatible bitmaps, created by using the CreateBitmap or CreateCompatibleBitmap function, are device specific and retain color information in a device-dependent format. No color values are used, and the colors are not subject to approximations and dithering.

Device-independent bitmaps (DIBs) retain color information either as color values or color palette indices. If color values are used, the colors are subject to approximation, but not dithering. Color palette indices can only be used with devices that support color palettes. Although Windows does not approximate or dither colors identified by indices, the resulting color may be different than that intended, because the indices yield valid results only in the context of the color palette that was current at the time the bitmap was created. If the palette changes, so do the colors in the bitmap.

+$#A Color Mixing

Color mixing lets an application create new colors by combining the pen or brush color with colors in the existing image. The application can choose either to draw the pen or brush color as is (effectively drawing over any existing image) or to mix the color with the colors already present.

The foreground mix mode, sometimes called the binary raster operation, determines how these colors are mixed. An application can merge colors, preserving all components of both colors; mask colors, removing or moderating components that are not common; or exclusively mask colors, removing or moderating components that are common. There are several variations on these basic mixing operations.

Color mixing is subject to color approximation. If the result of color mixing is a color that the device cannot generate, Windows approximates the result, using a color it can generate. If an application mixes dithered colors, the individual colors used to create the dithered color are mixed, and the results are subject to color approximation.

An application sets the foreground mix mode by using the SetROP2 function and retrieves the current mode by using the GetROP2 function.

Although there is a background mix mode, that mode does not control the mixing of colors. Instead, it specifies whether a background color is used when drawing styled lines, hatched brushes, and text.

+$#A Color Palettes

A color palette is an array that contains color values identifying the colors that can currently be displayed or drawn on the output device. Color palettes are used by devices that are capable of generating many colors but that can only display or draw a subset of these at any given time. For such devices, Windows maintains a system palette to track and manage the current colors of the device. Applications do not have direct access to the system palette. Instead, Windows associates a default palette with each device context. Applications can use the colors in the default palette or define their own colors by creating logical palettes and associating them with individual device contexts.

An application can determine whether a device supports color palettes by checking for the RC_PALETTE bit in the RASTERCAPS value returned by the GetDeviceCaps function.

+$#A Default Palette

The default palette is an array of color values identifying the colors that can be used with a device context by default. Windows associates the default palette with a context whenever an application creates a context for a device that supports color palettes. The default palette ensures that colors are available for use by an application without any further action.

The default palette typically has 20 entries (colors), but the exact number of entries may vary from device to device. This number is equal to the NUMCOLORS value returned by the GetDeviceCaps function. An application can retrieve the color values for colors in the default palette by enumerating solid pens, the same technique used to discover the colors available on nonpalette devices. The colors in the default palette depend on the device. Display devices, for example, often use the 16 standard colors of the VGA display and 4 other colors defined by Windows. Printer devices may use other default colors.

When using the default palette, applications use color values to specify pen and text colors. If the requested color is not in the palette, Windows approximates the color by using the closest color in the palette. If an application requests a solid brush color that is not in the palette, Windows simulates the color by dithering with colors that are in the palette.

To avoid approximations and dithering, applications can also specify pen, brush, and text colors by using color palette indices rather than color values. A color palette index is an integer value that identifies a specific palette entry. Applications can use color palette indices in place of color values but must use the PALETTEINDEX macro to create the indices.

Color palette indices are only useful for devices that support color palettes. To avoid this device dependence, applications that use the same code to draw to both palette and nonpalette devices should use palette-relative color values to specify pen, brush, and text colors. These values are identical to color values except when creating solid brushes. (On palette devices, a solid brush color specified by a palette-relative color value is subject to color approximation instead of dithering.) Applications must use the PALETTERGB macro to create palette-relative color values.

Windows does not allow an application to change the entries in the default palette. To use colors other than those in the default palette, an application must create its own logical palette and select the palette into the device context.

+$#A Logical Palette

A logical palette is a color palette that an application creates and associates with a given device context. Logical palettes let applications define and use colors that meet their specific needs. Applications can create any number of logical palettes, using them for separate device contexts or switching between them for a single device context. The maximum number of palettes that an application can create depends on the resources of the system.

An application creates a logical palette by using the CreatePalette function. The application fills a LOGPALETTE structure, which specifies the number of entries and the color values for each entry, and then the application passes the structure to CreatePalette. The function returns a palette handle that the application uses in all subsequent operations to identify the palette. To use colors in the logical palette, the application selects the palette into a device context by using the SelectPalette function and then realizes the palette by using the RealizePalette function. The colors in the palette are available as soon as the logical palette is realized.

An application should limit the size of its logical palettes to just enough entries to represent the colors needed. Applications cannot create logical palettes larger than the maximum palette size, a device-dependent value. Applications can obtain the maximum size by using the GetDeviceCaps function to retrieve the SIZEPALETTE value.

Although an application can specify any color value for a given entry in a logical palette, not all colors can be generated by the given device. Windows does not provide a way to discover which colors are supported, but the application can discover the total number of these colors by retrieving the color resolution of the device. The color resolution, specified in color bits per pixel, is equal to the COLORRES value returned by the GetDeviceCaps function. A device that has a color resolution of 18 has 262,144 possible colors. If an application requests a color that is not supported, Windows chooses an appropriate approximation.

Once a logical palette is created, an application can change colors in the palette by using the SetPaletteEntries function. If the logical palette has been selected and realized, changing the palette does not immediately affect the colors being displayed. The application must use the UnrealizeObject and RealizePalette functions to update the colors. In some cases, the application may need to deselect, unrealize, select, and realize the logical palette to ensure that the colors are updated exactly as requested. If an application selects a logical palette into more than one device context, changes to the logical palette affect all device contexts for which it is selected.

An application can change the number of entries in a logical palette by using the ResizePalette function. If the application reduces the size, the remaining entries are unchanged. If the application extends the size, Windows sets the color for each new entry to black (0, 0, 0) and the flag to zero.

An application can retrieve the color and flag values for entries in a given logical palette by using the GetPaletteEntries function. An application can retrieve the index for the entry in a given logical palette that most closely matches a specified color value by using the GetNearestPaletteIndex function.

When an application no longer needs a logical palette, it can delete it by using the DeleteObject function. The application must make sure the logical palette is no longer selected into a device context before deleting the palette.

+$#A Palette Animation

Palette animation is a technique to simulate motion by rapidly changing the colors of selected entries in a color palette. An application can carry out palette animation by creating a logical palette that contains "reserved" entries and then using the AnimatePalette function to change colors in those reserved entries.

An application creates a reserved entry in a logical palette by setting the peFlags member of the PALETTEENTRY structure to the PC_RESERVED flag. Once this logical palette is selected and realized, the application can call the AnimatePalette function to change one or more reserved entries. If the given palette is associated with the active window, Windows updates the colors on the screen immediately.

+$#A System Palette

Windows maintains a system palette for each device that uses palettes. The system palette contains the color values for all colors that can currently be displayed or drawn by the device. Other than viewing the contents of the system palette, applications cannot access the system palette directly. Instead, Windows has complete control of the system palette and permits access only through the use of logical palettes.

An application can view the contents of the system palette by using the GetSystemPaletteEntries function. This function retrieves the contents of one or more entries, up to the total number of entries in the system palette. The total is always equal to the number returned for the SIZEPALETTE value by the GetDeviceCaps function and is the same as the maximum size for any given logical palette.

Although applications cannot change colors in the system palette directly, they may cause changes when realizing logical palettes. To realize a palette, Windows examines each requested color and attempts to find an entry in the system palette that contains an exact match. If Windows finds a matching color, it maps the logical palette index to the corresponding system palette index. If Windows does not find an exact match, it copies the requested color to an unused system palette entry before mapping the indices. If all system palette entries are in use, Windows maps the logical palette index to the system palette entry whose color most closely matches the requested color. Once this mapping is set, applications cannot override it. For example, applications cannot use system palette indices to specify colors; only logical palette indices are permitted.

Applications can modify the way indices are mapped by setting the peFlags member of the PALETTEENTRY structure to selected values when creating the logical palette. For example, the PC_NOCOLLAPSE flag directs Windows to immediately copy the requested color to an unused system palette entry regardless of whether a system palette entry already contains that color. Also, the PC_EXPLICIT flag directs Windows to map the logical palette index to an explicitly given system palette index. (The application gives the system palette index in the low-order word of the PALETTEENTRY structure.)

Palettes can be realized as either a background palette or a foreground palette by specifying TRUE or FALSE respectively for the bForceBackground parameter in the SelectPalette function. There can be only one foreground palette in the system at a time. If the window is the currently active window or a descendent of the currently active window, it can realize a foreground palette. Otherwise the palette is realized as a background palette regardless of the value of the bForceBackground parameter. The critical property of a foreground palette is that, when realized, it can overwrite all entries (except for the static entries) in the system palette. Windows accomplishes this by marking all of the entries that are not static in the system palette as unused before the realization of a foreground palette, thereby eliminating all of the used entries. No preprocessing occurs on the system palette for a background palette realization. The foreground palette sets all of the possible nonstatic colors. Background palettes can set only what remains open and are prioritized in a first-come, first-serve manner. Typically, applications use background palettes for child windows which realize their own individual palettes. This helps minimize the number of changes that occur to the system palette.

An unused system palette entry is any entry that is not reserved and does not contain a static color. Reserved entries are explicitly marked with the PC_RESERVED value. These entries are created when an application realizes a logical palette for palette animation. Static color entries are created by Windows and correspond to the colors in the default palette. The GetDeviceCaps function can be used to retrieve the NUMRESERVED value, which specifies the number of system palette entries reserved for static colors.

Because the system palette has a limited number of entries, selecting and realizing a logical palette for a given device may affect the colors associated with other logical palettes for the same device. These color changes are especially dramatic when they occur on the display. An application can make sure that reasonable colors are used for its currently selected logical palette by resetting the palette before each use. An application resets the palette by calling the UnrealizeObject and RealizePalette functions. Using these functions causes Windows to remap the colors in the logical palette to reasonable colors in the system palette.

+$#A System Palette and Static Colors

Ordinarily, the system palette entries that Windows reserves for static colors cannot be changed. An application can override this default behavior by using the SetSystemPaletteUse function to reduce the number of static color entries and, thereby, increase the number of unused system palette entries. However, because changing the static colors can have an immediate and dramatic effect on all windows on the display, an application should not call SetSystemPaletteUse, unless it has a maximized window and the input focus.

When an application calls SetSystemPaletteUse with the SYSPAL_NOSTATIC value, Windows frees all but two of the reserved entries, allowing those entries to receive new color values when the application subsequently realizes its logical palette. The remaining two static color entries remain reserved and are set to white and black. An application can restore the reserved entries by calling SetSystemPaletteUse with the SYSPAL_STATIC value. It can discover the current system palette usage by using the GetSystemPaletteUse function.

Furthermore, after setting the system palette usage to SYSPAL_NOSTATIC, the application must immediately realize its logical palette, call the GetSysColor function to save the current system color settings, call the SetSysColors function to set the system colors to reasonable values using black and white, and finally send the WM_SYSCOLORCHANGE message to other top-level windows to allow them to be redrawn with the new system colors. When setting system colors using black and white, the application should make sure adjacent or overlapping items, such as window frames and borders, are set to black and white, respectively.

Before the application loses the input focus, closes its window, or terminates, it must immediately call SetSystemPaletteUse with the SYSPAL_STATIC value, realize its logical palette, restore the system colors to their previous values, and send the WM_SYSCOLORCHANGE message. Windows sends a WM_PAINT message to any window that is affected by a system color change. Applications that have brushes using the existing system colors should delete those brushes and recreate them using the new system colors.

+$#A Palette Messages

Changes to the system palette for the display device can have dramatic and sometimes undesirable effects on the colors used in windows on the desktop. To minimize the impact of these changes, Windows provides a set of messages that help applications manage their logical palettes while ensuring that colors in the active window are as close as possible to the colors intended.

Windows sends a WM_QUERYNEWPALETTE message to a top-level or overlapped window just before activating the window. This message gives an application the opportunity to select and realize its logical palette so that it receives the best possible mapping of colors for its logical palette. When the application receives the message, it should use the SelectPalette, UnrealizeObject, and RealizePalette functions to select and realize the logical palette. Doing so directs Windows to update colors in the system palette so that its colors match as many colors in the logical palette as possible.

When an application causes changes to the system palette as a result of realizing its logical palette, Windows sends a WM_PALETTECHANGED message to all top-level and overlapped windows. This message gives applications the opportunity to update the colors in the client areas of their windows, replacing colors that have changed with colors that more closely match the intended colors. An application that receives the WM_PALETTECHANGED message should use UnrealizeObject and RealizePalette to reset the logical palettes associated with all inactive windows and then update the colors in the client area for each inactive window by using the UpdateColors function. This technique does not guarantee the greatest number of exact color matches; however, it does ensure that colors in the logical palette are mapped to reasonable colors in the system palette.

Note To avoid creating an infinite loop, an application should never realize the palette for the window whose handle matches the handle passed in the wParam parameter of the WM_PALETTECHANGED message.

The UpdateColors function typically updates a client area of an inactive window faster than redrawing the area. However, because UpdateColors performs color translation based on the color of each pixel before the system palette changed, each call to this function results in the loss of some color accuracy. This means UpdateColors cannot be used to update colors when the window becomes active. In such cases, the application should redraw the client area.

Windows may send the WM_QUERYNEWPALETTE message when changes to the logical palette are made. Also, Windows may send the WM_PALETTEISCHANGING message to all top-level and overlapped windows when the system palette is about to change.

+$#A Halftone Palette and Color Adjustment

Halftone palettes are intended to be used whenever the stretching mode of a device context is set to HALFTONE. An application creates a halftone palette by using the CreateHalftonePalette function. The application must select and realize this palette into the device context before calling the StretchBlt or StretchDIBits function.

Windows automatically adjusts the input color of source bitmaps whenever applications call the StretchBlt and StretchDIBits functions and the stretching mode of a device context is set to HALFTONE. These color adjustments affect certain attributes of the image, such as contrast and brightness. An application can set the color adjustment values by using the SetColorAdjustment function. The application can retrieve the color adjustment values for the specified device context by using the GetColorAdjustment function.

+$#A Using Color

• Enumerating colors

• Creating colored pens and brushes

+$#A Enumerating Colors

You can determine how many colors a device supports and what those colors are by retrieving the count of colors for the device and enumerating the colors of the solid pens. To retrieve the number of colors, use the GetDeviceCaps function with the NUMCOLORS value. To enumerate solid pens, use the EnumObjects function and a corresponding callback function that receives information about each pen.

// GetTheColors - returns the count and color values of solid colors
// Returns the address of array containing colors
// hdc - handle of device context

COLORREF *GetTheColors(HDC hdc)
{
int cColors;
COLORREF *aColors;

// Get the number of colors.
cColors = GetDeviceCaps(hdcCurrent, NUMCOLORS);

// Allocate space for the array.
aColors = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) *
(cColors+1));

// Save the count of colors in first element.
aColors[0] = (LONG)cColors;

// Enumerate all pens and save solid colors in the array.
EnumObjects(hdc, OBJ_PEN, (GOBJENUMPROC)MyEnumProc, (LONG)aColors);

// Refresh the count of colors.
aColors[0] = (LONG)cColors;

return aColors;
}

int MyEnumProc(LPVOID lp, LPBYTE lpb)
{
LPLOGPEN lopn;
COLORREF *aColors;
int iColor;

lopn = (LPLOGPEN)lp;
aColors = (COLORREF *)lpb;

if (lopn->lopnStyle==PS_SOLID) {
// Check for too many colors.
if ((iColor = (int)aColors[0]) <= 0)
return 0;
aColors[iColor] = lopn->lopnColor;
aColors[0]--;
}
return 1;
}

+$#A Creating Colored Pens and Brushes

Although you can specify any color for a pen when creating it, Windows uses only colors that are available on the device. This means Windows uses the closest matching color when it realizes the pen for drawing. When creating brushes, Windows generates a dithered color if you specify a color that the device does not support. In either case, you can use the RGB macro to specify a color when creating a pen or brush.

// DrawARectangle - draws a red rectangle with a green border
// No return value.
// hdc - handle of the device context

void DrawARectangle(HDC hdc)

{
HPEN hpen, hpenOld;
HBRUSH hbrush, hbrushOld;

// Create a green pen.
hpen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0));
// Create a red brush.
hbrush = CreateSolidBrush(RGB(255, 0, 0));

// Select the new pen and brush, and then draw.
hpenOld = SelectObject(hdc, hpen);
hbrushOld = SelectObject(hdc, hbrush);
Rectangle(hdc, 100,100, 200,200);

// Do not forget to clean up.
SelectObject(hdc, hpenOld);
DeleteObject(hpen);
SelectObject(hdc, hbrushOld);
DeleteObject(hbrush);
}

+$#AK Color Reference

The following functions and messages are associated with color.

+$#A Color Functions

Following are the functions used with color.

AnimatePalette
CreateHalftonePalette
CreatePalette
GetColorAdjustment
GetNearestColor
GetNearestPaletteIndex
GetPaletteEntries
GetSystemPaletteEntries
GetSystemPaletteUse
RealizePalette
ResizePalette
SelectPalette
SetColorAdjustment
SetPaletteEntries
SetSystemPaletteUse
UnrealizeObject
UpdateColors

+$#A Color Messages

Following are the messages used with color.

WM_PALETTECHANGED
WM_PALETTEISCHANGING
WM_QUERYNEWPALETTE
WM_SYSCOLORCHANGE

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide facilement


Combo Boxes.rtf

+$#AK Combo Boxes

A combo box is a unique type of control, defined by the COMBOBOX class, that combines much of the functionality of a list box and an edit control.

This overview describes the types and styles of combo box, the parts of a combo box, the use of an owner-drawn combo box, and how to subclass a combo box. Additional features of combo boxes are also discussed.

+$#A About Combo Boxes

The Microsoft® Win32® application programming interface (API) provides three types of combo box: simple combo boxes (CBS_SIMPLE), drop-down combo boxes (CBS_DROPDOWN), and drop-down list boxes (CBS_DROPDOWNLIST).

There are also a number of combo box styles that define specific properties. For example, two styles enable an application to create an owner-drawn combo box, making the application responsible for displaying information in the control.

A combo box consists of a list and a selection field. The list presents the options a user can select and the selection field displays the current selection. Except in drop-down list boxes, the selection field is an edit control and can be used to enter text not in the list.

+$#A Combo Box Types and Styles

Combo boxes can be characterized by type and style. Combo box types determine whether the combo box list is a drop-down list and whether the selection field is an edit control. A drop-down list appears only when the user opens it, so it uses less screen space than a list that is always visible. If the selection field is an edit control, the user can enter information not in the list; otherwise, the user can only select items in the list.

The following table shows the three combo box types and indicates whether each includes a drop-down list and an edit control:

Combo box type | Drop-down list | Edit control
Drop-down combo box | Yes | Yes
Drop-down list box | Yes | No
Simple combo box | No | Yes

Combo box styles define specific properties of a combo box. An application can combine styles; however some styles only apply to certain combo box types. The following table describes the combo box styles:

Style | Description
CBS_AUTOHSCROLL | Automatically scrolls the text in an edit control to the right when the user types a character at the end of the line. If this style is not set, only text that fits within the rectangular boundary is allowed.
CBS_DISABLENOSCROLL | Shows a disabled vertical scroll bar in the list box when the box does not contain enough items to scroll. Without this style, the scroll bar is hidden when the list box does not contain enough items.
CBS_DROPDOWN | Specifies a drop-down combo box.
CBS_DROPDOWNLIST | Specifies a drop-down list box.
CBS_HASSTRINGS | Specifies that an owner-drawn combo box contains items consisting of strings. The combo box maintains the memory and address for the strings so the application can use the CB_GETLBTEXT message to retrieve the text for a particular item.
CBS_LOWERCASE | Converts to lowercase all text in both the selection field and the list.
CBS_NOINTEGRALHEIGHT | Specifies that the size of the combo box is exactly the size specified by the application when it created the combo box. Normally, Windows sizes a combo box so that it does not display partial items.
CBS_OEMCONVERT | Converts text entered in the combo box edit control from the Windows character set to the OEM character set and then back to the Windows set. This ensures proper character conversion when the application calls the CharToOem function to convert a Windows string in the combo box to OEM characters. This style is most useful for combo boxes that contain filenames and applies only to combo boxes created with the CBS_SIMPLE or CBS_DROPDOWN style.
CBS_OWNERDRAWFIXED | Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are all the same height. The owner window receives a WM_MEASUREITEM message when the combo box is created and a WM_DRAWITEM message when a visual aspect of the combo box has changed.
CBS_OWNERDRAWVARIABLE | Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are variable in height. The owner window receives a WM_MEASUREITEM message for each item in the combo box when you create the combo box and a WM_DRAWITEM message when a visual aspect of the combo box has changed.
CBS_SIMPLE | Specifies a simple combo box.
CBS_SORT | Automatically sorts strings added to the list box.
CBS_UPPERCASE | Converts to uppercase all text in both the selection field and the list.

+$#A Combo Box List

A list is the portion of a combo box that displays the items a user can select. Typically, an application initializes the contents of the list when it creates a combo box. Any list item selected by the user is the current selection. In simple and drop-down combo boxes, the user can type in the selection field instead of selecting a list item. In these cases, there is no current selection. For more information, see Current Selection.

+$#A List Contents

When an application creates a combo box, it typically initializes the combo box by adding one or more items to the list. Later, an application may add or delete list items, reinitialize the list, or retrieve item information from it.

An application adds list items to a combo box by sending the CB_ADDSTRING message to it. The specified item is added to the end of the list or, in a sorted combo box, in its correct sorted position based on the item's string. In an unsorted combo box, an application can use the CB_INSERTSTRING message to insert an item at a specific position. Once added, a list item is identified by its position.

By using the CB_FINDSTRING or CB_FINDSTRINGEXACT message, an application can determine the position of a list item. CB_FINDSTRING finds an item whose string begins with the specified string. CB_FINDSTRINGEXACT finds an item whose string matches the string exactly. Neither message is case sensitive.

An application can remove a list item by using the CB_DELETESTRING message. If an application needs to reinitialize the combo box list, it can first clear its entire contents by using the CB_RESETCONTENT message. When adding multiple items to the list after a combo box has already been shown, an application can clear the redraw flag to prevent the combo box from being repainted after each item is added. For more information about redrawing, see the description of the WM_SETREDRAW message.

To retrieve the string associated with a list item, an application can use the CB_GETLBTEXT message. The item's string is copied to the buffer specified by the application. To ensure that the buffer is large enough to receive the string, the application can first use the CB_GETLBTEXTLEN message to determine the length of the string. To get the number of list items in a combo box, an application can use the CB_GETCOUNT message.

+$#A Current Selection

The current selection is a list item the user has selected; the selected text appears in the selection field of the combo box. However, in the case of a simple combo box or a drop down combo box, the current selection is only one form of possible user input in a combo box. The user can also type text in the selection field.

The current selection is identified by the zero-based index of the selected list item. An application can set and retrieve it at any time. The parent window or dialog box procedure receives notifications when user changes the current selection for a combo box. The parent window or dialog box is not notified when the application changes the selection.

When a combo box is created, there is no current selection. This is also true for a simple or drop-down combo box, if the user has edited the contents of the selection field. To set the current selection, an application sends the CB_SETCURSEL message to the combo box. An application can also use the CB_SELECTSTRING message to set the current selection to a list item whose string begins with a specified string. To determine the current selection, an application sends the CB_GETCURSEL message to the combo box. If there is no current selection, this message returns CB_ERR.

When the user changes the current selection in a combo box, the parent window or dialog-box procedure receives a WM_COMMAND message with the notification message CBN_SELCHANGE in the high-order word of the wParam parameter. As a result, the application can respond to this notification message and execute a specific process each time the user selects a list item. This notification message is not sent when the current selection is set using the CB_SETCURSEL message.

In a drop-down combo box or drop-down list box, an application wait for the use to close the list box before processing a change in the current selection. This can be useful when significant processing is required. For example, to update a directory list based on the selected drive, an application can process the CBN_CLOSEUP message instead of CBN_SELCHANGE to update a directory list after the user selects a drive.

An application could also process the notification messages CBN_SELENDOK and CBN_SELENDCANCEL. The system sends CBN_SELENDOK when the user selects a list item, or selects an item and then closes the list. This indicates the user has finished, and the selection should be processed. CBN_SELENDCANCEL is sent when the user selects an item, but then selects another control, presses the ESCAPE key while the drop-down list is open, or closes the dialog box. This indicates the user's selection should be ignored. In a simple combo box, CBN_SELENDOK is sent before every CBN_SELCHANGE message. If the WS_EX_NOPARENTNOTIFY window style is specified for the combo box, the system does not send CBN_SELENDOK and CBN_SELENDCANCEL messages.

In a simple combo box, the system sends the CBN_DBLCLK notification message when the user double-clicks a list item. In a drop-down combo box or drop-down list, a single click hides the list so it is not possible to double-click an item.

+$#A Drop-Down Lists

Certain notifications and messages apply only to combo boxes containing drop-down lists. When a drop-down list is open or closed, the parent window of a combo box receives a notification in the form of a WM_COMMAND message. If the list is being opened, the high-order word of wParam is CBN_DROPDOWN. If the list is being closed, it is CBN_CLOSEUP.

An application can open the list of a drop-down combo box or drop-down list box by using the CB_SHOWDROPDOWN message. It can determine whether the list is open by using the CB_GETDROPPEDSTATE message and can determine the coordinates of a drop-down list by using the CB_GETDROPPEDCONTROLRECT message. An application can also increase the width of a drop-down list by using the CB_SETDROPPEDWIDTH message.

+$#A Edit Control Selection Fields

The selection field is the portion of a combo box that displays the currently selected list item. In simple and drop-down combo boxes, the selection field is an edit control and can be used to enter text that is not in the list.

An application can retrieve or set the contents of the selection field and can determine or set the edit selection. The application can also limit the amount of text a user can type in the selection field. When the contents of the selection field change, Windows sends notification messages to the parent window or dialog box procedure.

To retrieve the content of the selection field, an application can send the WM_GETTEXT message to the combo box. To set the contents of the selection field of a simple or drop-down combo box, an application can send the WM_SETTEXT message to the combo box.

The edit selection is the range of selected text, if any, in the selection field of a simple or drop-down combo box. An application can determine the starting and ending character positions of the current selection by using the CB_GETEDITSEL message. It can also select characters in the edit selection by using the CB_SETEDITSEL message.

Initially, the amount of text that the user can type into the selection field is limited by the size of the selection field. However, if the combobox has the CBS_AUTOHSCROLL style, the text can continue beyond the size of the selection field. An application can use the CB_LIMITTEXT message to limit the amount of text a user can type into the selection field, regardless of whether the control has the CBS_AUTOHSCROLL style.

When the user edits the content of the selection field, the parent window or dialog box procedure receives notification messages. CBN_EDITUPDATE is sent first, indicating that the text in the selection field has been edited. After the altered text is displayed, Windows sends CBN_EDITCHANGE. When the selection field content changes as the result of a list item being selected, these messages are not sent.

+$#A Owner-Drawn Combo Boxes

An application can create an owner-drawn combo box to take responsibility for painting list items. The parent window or dialog box of an owner-drawn combo box (its owner) receives WM_DRAWITEM messages when a portion of the combo box needs to be painted. An owner-drawn combo box can list information other than, or in addition to, text strings. Owner-drawn combo boxes can be of any type. However, the edit control in a simple or drop-down combo box can only display text, while the owner paints the selection field in a drop-down list box.

The owner of an owner-drawn combo box must process the WM_DRAWITEM message. This message is sent whenever a portion of the combo box must be redrawn. The owner may need to process other messages, depending on the styles specified for the combo box.

An application can create an owner-drawn combo box by specifying the CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE style. If all list items in the combo box are the same height, such as strings or icons, an application can use the CBS_OWNERDRAWFIXED style. If list items are of varying height, bitmaps of different size, for example, an application can use the CBS_OWNERDRAWVARIABLE style.

The owner of an owner-drawn combo box can process a WM_MEASUREITEM message to specify the dimensions of list items in the combo box. If the application creates the combo box by using the CBS_OWNERDRAWFIXED style, Windows sends the WM_MEASUREITEM message only once. The dimensions specified by the owner are used for all list items. If the CBS_OWNERDRAWVARIABLE style is used, Windows sends a WM_MEASUREITEM message for each list item added to the combo box. The owner can determine or set the height of a list item at any time by using the CB_GETITEMHEIGHT and CB_SETITEMHEIGHT messages, respectively.

If the information displayed in an owner-drawn combo box includes text, an application can keep track of the text for each list item by specifying the CBS_HASSTRINGS style. Combo boxes with the CBS_SORT style are sorted based on this text. If a combo box is sorted and not of the CBS_HASSTRINGS style, the owner must process the WM_COMPAREITEM message.

In an owner-drawn combo box, the owner must keep track of list items containing information other than or in addition to text. One convenient way to do this is to save the handle of the information as item data. For more information about item data, see Data Associated with List Items. To free data objects associated with items in a combo box, the owner can process the WM_DELETEITEM message.

For an example of an owner-drawn combo box, see Creating an Owner-Drawn Combo Box.

+$#A Subclassed Combo Boxes

Subclassing is a procedure that allows an application to intercept and process messages sent or posted to a window. By using subclassing, an application can substitute its own processing for certain messages, while leaving most message processing to the class-defined window procedure.

When the operating system creates a window, it saves information about it in an internal data structure that includes the address of the window procedure. To subclass a window, an application calls the SetClassLong function to replace the address of that procedure with the instance address of an application-defined subclass procedure. Thereafter, all messages to the window are sent to the subclass procedure. This procedure then uses the CallWindowProc function to pass unprocessed messages to the original window procedure. For a description of the message processing performed by the COMBOBOX class window procedure, see Default Combo Box Behavior.

When the combo box is outside a dialog box, an application cannot process the TAB, ENTER, and ESCAPE keys unless it uses a subclass procedure. When a simple or drop-down combo box receives the input focus, it immediately sets the focus to its child edit control. Therefore, an application must subclass the edit control to intercept keyboard input for a simple or drop-down combo box. For an example of this, see Subclassing a Combo Box.

If a subclass procedure processes the WM_PAINT message, it must use the BeginPaint function to prepare for painting. Before calling the EndPaint function, it passes the device-context (DC) handle as the wParam parameter for the window procedure. If EndPaint is called first, the class window procedure does no painting because EndPaint validates the entire window.

A technique related to subclassing is superclassing. A superclass resembles any other class except that its window procedure does not call DefWindowProc to handle unprocessed messages. Instead, it passes unprocessed messages to the window procedure for the parent window class. Follow the guidelines in Window Procedures to avoid problems that can occur with subclassing and superclassing.

+$#A Special Combo Box Features

The Win32 API provides special-purpose messages and functions that enable an application to display a directory listing in a combo box, associate data with list items in a combo box, and change the keyboard interface for a drop-down combo box or drop-down list box.

+$#A Directory Lists

An application can add the names of files or subdirectories to a combo box by sending the CB_DIR message to it. The wParam parameter for the window procedure specifies the attributes of the files to add, and the lParam parameter points to the text string that defines the file specification.

Within a dialog box, an application can also use DlgDirListComboBox. If the specified filename includes a drive, path, or both, DlgDirListComboBox changes the current drive or directory and removes the drive and path from the filename string. To show the directory name, the function updates the specified static control, if any. It then resets the contents of the combo box, and sends the CB_DIR message to the combo box to add the specified filenames.

To remove the selected filename, directory name, or drive letter from a combo box that has been filled by using DlgDirListComboBox, an application can use the DlgDirSelectComboBoxEx function.

The DlgDirListComboBox and DlgDirSelectComboBoxEx functions and the CB_DIR message are similar to the DlgDirList and DlgDirSelectEx functions and the LB_DIR message used with list boxes.

+$#A Data Associated with List Items

An application can associate data with the list items in a combo box. The CB_SETITEMDATA message associates a DWORD value with a list item, and the CB_GETITEMDATA retrieves the value associated with a list item.

The example in Creating an Owner-Drawn Combo Box uses item data to associate a constant with each item in a drop-down list box. Such a unique value identifies each item independent of its sorted position.

Other applications might use item data to associate a handle or pointer with a list item. If so, an application can process a WM_DELETEITEM message to delete or free the specified object when the list item is deleted.

+$#A The Extended User Interface

Drop-down combo boxes and drop-down list boxes support an alternative keyboard interface called the extended user interface. By default, the F4 key opens or closes the list, and the DOWN ARROW changes the current selection. In a combo box with the extended user interface, however, the F4 key is disabled and pressing the DOWN ARROW key opens the drop-down list.

To select the user interface for a combo box, an application can send the CB_SETEXTENDEDUI message to the combo box. A TRUE value for the wParam parameter enables the extended user interface; a FALSE value sets the default user interface. To determine whether a combo box uses the extended user interface, an application can send the CB_GETEXTENDEDUI message to the combo box.

+$#A Combo Box Notifications

Messages from combo boxes are sent as notifications in the form of WM_COMMAND messages. The notification message is stored in the high word of the wParam parameter, and an application can process the following combo box notification messages.

Notification message | Description
CBN_CLOSEUP | Indicates the list in a drop-down combo box or drop-down list box is about to close.
CBN_DBLCLK | Indicates the user has double-clicked a list item in a simple combo box.
CBN_DROPDOWN | Indicates the list in a drop-down combo box or drop-down list box is about to open.
CBN_EDITCHANGE | Indicates the user has changed the text in the edit control of a simple or drop-down combo box. This notification message is sent after the altered text is displayed.
CBN_EDITUPDATE | Indicates the user has changed the text in the edit control of a simple or drop-down combo box. This notification message is sent before the altered text is displayed.
CBN_ERRSPACE | Indicates the combo box cannot allocate enough memory to carry out a request, such as adding a list item.
CBN_KILLFOCUS | Indicates the combo box is about to lose the input focus.
CBN_SELCHANGE | Indicates the current selection has changed.
CBN_SELENDCANCEL | Indicates that the selection made in the drop down list, while it was dropped down, should be ignored.
CBN_SELENDOK | Indicates that the selection made drop down list, while it was dropped down, should be accepted.
CBN_SETFOCUS | Indicates the combo box has received the input focus.

+$#A Default Combo Box Behavior

This section contains a table that describes the messages specifically handled by the predefined COMBOBOX class window procedure.

Message | Description
CB_ADDSTRING | Sends an LB_ADDSTRING message to the list window to add a list item.
CB_DELETESTRING | Sends an LB_DELETESTRING message to the list window to delete a list item.
CB_DIR | Adds the filenames matching the specified attributes and path to the list.
CB_FINDSTRING | Sends an LB_FINDSTRING message to the list window. This message returns the index of the first list item that begins with the specified text.
CB_FINDSTRINGEXACT | Sends an LB_FINDSTRING message to the list window. This message returns the index of the first list item exactly matching the specified text.
CB_GETCOUNT | Sends an LB_GETCOUNT message to the list window. It returns the number of list items.
CB_GETCURSEL | Sends an LB_GETCURSEL message to the list window. It returns the index of the currently selected item, if any.
CB_GETDROPPEDCONTROLRECT | Fills the specified rectangle structure with the screen coordinates of a drop-down list.
CB_GETDROPPEDSTATE | Returns TRUE if a drop-down list is open; otherwise, it returns FALSE.
CB_GETDROPPEDWIDTH | Returns the minimum allowable width, in pixels, of the drop down list.
CB_GETEDITSEL | Sends an EM_GETSEL message to the edit control, and it returns the starting and ending position of the current selection. In drop-down list boxes, the window procedure returns CB_ERR.
CB_GETEXTENDEDUI | Returns TRUE if the combo box is a drop-down combo box or drop-down list box and the extend user-interface flag is set; otherwise, it returns FALSE.
CB_GETHORIZONTALEXTENT | Sends an LB_GETHORIZONTALEXTENT message to the list window. It returns the scrollable width, in pixels, of the drop down list.
CB_GETITEMDATA | Sends an LB_GETITEMDATA message to the list window. It returns the 32-bit value associated with the specified list item.
CB_GETITEMHEIGHT | Sends an LB_GETITEMHEIGHT message to the list window. It returns the height, in pixels, of the specified owner-drawn list item.
CB_GETLBTEXT | Sends an LB_GETTEXT message to the list window. It copies the specified list text to the specified buffer.
CB_GETLBTEXTLEN | Sends an LB_GETTEXTLEN message to the list window. It returns the length, in bytes, of the specified list text.
CB_GETLOCALE | Sends an LB_GETLOCALE message to the list window. It returns the current locale for the list.
CB_GETTOPINDEX | Sends an LB_GETTOPINDEX message to the list window. It returns the index of the first visible item in the drop down list.
CB_INITSTORAGE | Sends an LB_INITSTORAGE message to the list window. It initializes space for the specified number of items and the specified number of bytes for item strings.
CB_INSERTSTRING | Sends an LB_INSERTSTRING message to the list window. It inserts a list item at the specified position.
CB_LIMITTEXT | Sends an EM_LIMITTEXT message to the edit control. It sets the maximum number of characters a user can enter in the edit control. In drop-down list boxes, the window procedure returns CB_ERR.
CB_RESETCONTENT | Sends an LB_RESETCONTENT message to the list window, and it removes the contents of the list.
CB_SELECTSTRING | Sends an LB_SELECTSTRING message to the list window. It selects the first list item, if any, that begins with the characters in the specified text.
CB_SETCURSEL | Sends an LB_SETCURSEL message to the list window, and it sets the current selection.
CB_SETDROPPEDWIDTH | Sets the minimum allowable width, in pixels, of the drop down list.
CB_SETEDITSEL | Sends an EM_SETSEL message to the edit control. It selects the specified range of text. In drop-down list boxes, the window procedure returns CB_ERR.
CB_SETEXTENDEDUI | Sets or clears the extended user-interface flag. This flag changes the keys that open and close the list in a drop-down combo box or drop-down list box. If the combo box is a simple combo box, the window procedure returns CB_ERR.
CB_SETHORIZONTALEXTENT | Sends an LB_SETHORIZONTALEXTENT message to the list window. It sets the scrollable width, in pixels, of the drop down list.
CB_SETITEMDATA | Sends an LB_SETITEMDATA message to the list window. It associates the specified 32-bit value with a list item.
CB_SETITEMHEIGHT | Sends an LB_SETITEMHEIGHT message to the list window. It sets the height of the specified owner-drawn list item or the selection field.
CB_SETLOCALE | Sends an LB_SETLOCALE message to the list window, and it sets the current locale for the list. The locale affects how the list is sorted if it has the CBS_SORT style and strings are added using CB_ADDSTRING.
CB_SETTOPINDEX | Sends an LB_SETTOPINDEX message to the list window. It scrolls the drop down list so the specified item is at the top of the visible range.
CB_SHOWDROPDOWN | Shows or hides the drop-down list. This message has no effect on simple combo boxes.
WM_CHAR | Processes character input. In drop-down list boxes, this message is passed to the list window, which moves the selection to the first item beginning with the specified character. In simple and drop-down combo boxes, this message is passed to the edit control.
WM_CLEAR | Deletes the edit selection. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_COMMAND | Processes notification messages from the edit control and list window and sends corresponding combo box notification messages to the parent window.
 | For edit control notifications, the window procedure may update the list window's current selection, caret index, and top index. For list notification messages, the window procedure may update the content of the selection field.
WM_COMPAREITEM | Passes the message to the parent window, enabling the application to specify the relative sort position of two owner-drawn list items. The combo box window receives this message from the list window.
WM_COPY | Copies the edit selection to the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_CREATE | Initializes the combo box.
WM_CUT | Deletes the edit selection and places it on the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_DELETEITEM | Passes the message to the parent window, notifying the application that a list item has been deleted. The combo box window receives this message from the list window.
WM_DRAWITEM | Passes the message on to the parent window enabling the application to paint the specified list item. The combo box window receives this message from the list window. The window procedure can also originate this message to have the application paint the selection field of a drop-down list box.
WM_ENABLE | Sets the state to enable or prohibit mouse and keyboard input.
WM_ERASEBKGND | Returns 1, indicating that the background is erased.
WM_GETDLGCODE | Returns a combination of the DLG_WANTCHARS and DLGC_WANTARROWS values.
WM_GETFONT | Returns the handle of the current font with which the combo box will draw its text.
WM_GETTEXT | Copies the contents of the selection field to the specified buffer. In simple and drop-down combo boxes, the edit control processes this message.
WM_GETTEXTLENGTH | Returns the length, in characters, of the text in the selection field. In simple and drop-down combo boxes, the edit control processes this message.
WM_KEYDOWN | Processes noncharacter keyboard input. In drop-down list boxes, this message is sent to the list window, which may show or hide itself, or change its current selection or caret index. In simple and drop-down combo boxes, this message is passed to the edit control. The edit control passes certain keys to the list window, such as the UP and DOWN ARROW keys and the F4 key.
WM_KILLFOCUS | Hides the highlight in the selection field and closes the drop-down list, if necessary. If the window receiving the input focus is part of the combo box (for example, the edit control), this message is ignored.
WM_LBUTTONDBLCLK | Same as WM_LBUTTONDOWN.
WM_LBUTTONDOWN | Sets the focus to the combo box and, for drop-down combo boxes and drop-down lists, can open or close the list. If it opens the list, the window procedure captures the mouse to enable selection by dragging and releasing the mouse button.
WM_LBUTTONUP | Releases the mouse capture if the mouse opened the list.
WM_MEASUREITEM | Posts the message to the parent window, enabling the application to modify the contents of the specified MEASUREITEMSTRUCT structure. The combo box window receives this message from the list window.
WM_MOUSEMOVE | Posts the message to the list window if the mouse has opened the list and the mouse button is still down. This enables a user to select an item by dragging the mouse pointer to a list item and then releasing the button.
WM_NCCREATE | Allocates an internal data structure used by the combo box window procedure.
WM_NCDESTROY | Frees resources allocated in response to the WM_NCCREATE message.
WM_PAINT | Paints the invalid region of the combo box. If wParam is not NULL, it is assumed to be a DC handle passed from a subclass function. The window procedure uses the specified DC instead of calling BeginPaint and EndPaint.
WM_PASTE | Replaces the edit selection with the contents of the clipboard. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_SETFOCUS | Sets the focus to the edit control or, in drop-down list boxes, inverts the selection field and turns on the caret in the list window.
WM_SETFONT | Saves the specified font handle in an internal structure, adjusts the dimensions of the selection field and list, and invalidates the combo box window. Text in the selection field and the list is displayed in the saved font.
WM_SETREDRAW | Sets or clears the redraw flag. If the redraw flag is cleared, the combo box is not repainted until the flag is set again.
WM_SETTEXT | Sets the contents of the edit control. In simple and drop-down combo boxes, the edit control processes this message. In drop-down list boxes, the window procedure returns CB_ERR.
WM_SIZE | Resizes the child windows, if necessary.
WM_SYSKEYDOWN | Opens or closes the drop-down list depending on which arrow key the user pressed.

All other messages are passed to the DefWindowProc function for default processing.

+$#A Using Combo Boxes

• Creating a simple combo box

• Creating an owner-drawn combo box

• Subclassing a combo box

+$#A Creating a Simple Combo Box

This section describes how to use a simple combo box in a dialog box. The example in Creating a Spell Dialog Box uses a simple combo box in a dialog box for a spelling checker. When it finds a misspelled word, the spelling checker suggests alternative spellings. The user can ignore the misspelling, change to one of the suggested spellings, or type a spelling not suggested. A simple combo box is well suited for receiving this kind of user input.

+$#A Creating the Simple Dialog Box

The dialog box template defines the window styles and control identifier for the combo box. In this example, the combo box uses the CBS_SIMPLE, CBS_SORT, WS_VSCROLL, and WS_TABSTOP styles.

The dialog box also contains three buttons: Change (IDOK), Ignore (IDSKIP), and Cancel (IDCANCEL). The IDOK and IDCANCEL constants are defined by the Win32 API. The IDSKIP constant is defined in the application's header file, as is the control identifier for the combo box, IDCOMBO.

For more information about dialog boxes, see Dialog Boxes.

+$# Processing the WM_INITDIALOG and WM_DESTROY Messages in a Simple Dialog Box

When you use a combo box in a dialog box, you usually respond to the WM_INITDIALOG message by initializing the combo box. The code example in Creating a Spell Dialog Box calls the application-defined OpenDictionary function to load the dictionary. It then calls the application-defined SelectNextWord and InitSpellList functions to select the first misspelled word.

SelectNextWord selects the next word in the edit control and copies it to the specified buffer. InitSpellList determines whether the selected word is in the dictionary. If not, it places the word in the selection field of the combo box and adds suggested spellings to the list.

The dialog box procedure processes the WM_DESTROY message to free the resources allocated to the spelling dictionary.

+$# Processing the WM_COMMAND Message in a Simple Dialog Box

When an event occurs in a dialog box control, the control sends a WM_COMMAND message to the dialog box procedure. The high-order word of the wParam parameter is a notification code, indicating the type of event that occurred. The low-order word of wParam is a constant identifying the control. The lParam parameter is the window handle for the control.

When processing the WM_COMMAND message, the spelling checker example examines the control identifier in the low-order word of wParam to determine the origin of the message. The constants IDCOMBO, IDOK, IDIGNORE, and IDCANCEL identify the combo box, the Change button, the Ignore button, and the Cancel button controls, respectively.

A combo box may send a WM_COMMAND message for a number of different reasons. To determine the type of event, the dialog box procedure examines the notification code in the high-order word of wParam. The example processes only the CBN_DBLCLK notification message, which is sent when the user double-clicks a list item. The dialog box procedure processes this notification message in the same way as a Change button click.

The buttons in this example send WM_COMMAND messages only when they are chosen by the user. When the user chooses the Change button, the dialog box procedure replaces the current selection in the application's edit control with the content of the selection field in the combo box. The selection field may contain either the selected list item or text the user has typed. The dialog box procedure then selects the next misspelled word in the same way it processes an Ignore button click.

When the user chooses the Ignore button, the dialog box procedure calls the application-defined SelectNextWord and InitSpellList functions to select the next misspelled word in the application's edit control. InitSpellList places the misspelled word in the selection field of the combo box and adds suggested spellings to the combo box list.

When the user chooses the Cancel button, the dialog box procedure calls the EndDialog function to close the dialog box.

+$#A Creating a Spell Dialog Box

Following are the dialog box procedure and supporting functions for the spelling checker dialog box.

HWND hwndMain;

HWND hwndEdit;

char achTemp[256]; /* temporary buffer */

LPSTR lpstrDict; /* buffer for dictionary file */

LPSTR *paLpDict; /* array of pointers to words */

UINT cWords; /* number of words */

/********************************************************

FUNCTION: SpellDlgProc

PURPOSE: Dialog procedure for Spell dialog box.

*********************************************************/

BOOL CALLBACK SpellDlgProc(hwndDlg, msg, wParam, lParam)

HWND hwndDlg;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

switch (msg) {

case WM_INITDIALOG:

if (!OpenDictionary()) {

EndDialog(hwndDlg, 0);

break;

}

SendMessage(hwndEdit, EM_SETSEL, 0, 0);

do

if (!SelectNextWord(hwndEdit, achTemp)) {

GlobalFree((HGLOBAL) lpstrDict);

LocalFree((HLOCAL) paLpDict);

EndDialog(hwndDlg, 0);

break;

}

while (!InitSpellList(

GetDlgItem(hwndDlg, IDCOMBO), achTemp));

break;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDCOMBO:

if (HIWORD(wParam) != CBN_DBLCLK)

break;

/* For a double-click, process the OK case. */

case IDOK:

SendDlgItemMessage(hwndDlg, IDCOMBO,

WM_GETTEXT, sizeof(achTemp),

(LPARAM) achTemp);

SendMessage(hwndEdit, EM_REPLACESEL, 0,

(LPARAM) achTemp);

/* Fall through to get the next word. */

case IDIGNORE:

do

if (!SelectNextWord(hwndEdit, achTemp)) {

EndDialog(hwndDlg, 0);

break;

}

while (!InitSpellList(GetDlgItem(hwndDlg,

IDCOMBO), achTemp));

break;

case IDCANCEL:

EndDialog(hwndDlg, 0);

}

break;

case WM_DESTROY:

GlobalFree((HGLOBAL) lpstrDict);

LocalFree((HLOCAL) paLpDict);

break;

default:

return FALSE;

}

return TRUE;

}

/********************************************************

FUNCTION: InitSpellList

PURPOSE: Initializes the selection field and list

of suggestions for the specified word, if

it is not in the dictionary.

RETURNS: If the list is initialized, the return

value is TRUE. If an error occurs or the

word is in the dictionary, the return

value is FALSE.

*********************************************************/

BOOL PASCAL InitSpellList(HWND hwndCombo, LPSTR lpszWord)

{

int min = 0; /* beginning of search range */

int max = cWords; /* end of search range */

int n; /* index of word */

int cmp; /* result of comparison */

char ch; /* first character in word */

ch = *lpszWord;

CharLowerBuff(&ch, 1);

/*

* Perform a binary search for the word.

*

* The global array paLpDict contains pointers to words

* in the global array lpstrDict. These two variables are

* initialized by the OpenDictionary function.

*/

n = max / 2;

while (min < max) {

cmp = lstrcmpi(lpszWord, paLpDict[n]);

if (cmp == 0)

return FALSE; /* not misspelled */

if (cmp < 0)

max = n;

else

min = n + 1;

n = (min + max) / 2;

}

/* List words beginning with the same letter as lpszWord. */

SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);

while (n > 0 && *paLpDict[n - 1] == ch)

n--;

while (*paLpDict[n] == ch)

SendMessage(hwndCombo, CB_ADDSTRING,

0, (LPARAM) paLpDict[n++]);

/* Place the word in the selection field. */

SendMessage(hwndCombo, WM_SETTEXT, 0, (LPARAM) lpszWord);

return TRUE;

}

+$#A Creating an Owner-Drawn Combo Box

This section describes how to use an owner-drawn combo box. The example in Creating a Square Meal Dialog Box uses an owner-drawn drop-down list box to display the four food groups, each represented by a bitmap and a name. Selecting a food group causes the foods in that group to appear in a list.

+$#A Creating the Owner-Drawn Dialog Box

The dialog box template defines the window styles, buttons, and control identifiers for the combo box. The combo box in this example uses the CBS_DROPDOWNLIST, CBS_OWNERDRAWFIXED, CBS_SORT, CBS_HASSTRINGS, WS_VSCROLL, and WS_TABSTOP styles.

{ewc msdncd, EWGraphic, group10612 0 /a "SDK_01.bmp"}

The dialog box also contains a list box (IDLIST) and two buttons: OK (IDOK) and Cancel (IDCANCEL). The IDOK and IDCANCEL constants are defined by the Win32 API. The constant IDLIST is defined in the application's header file, as is the control identifier, IDCOMBO.

For more information about dialog boxes, see Dialog Boxes.

+$# Processing the WM_INITDIALOG and WM_DESTROY Messages in an Owner-Drawn Dialog Box

When you use a combo box in a dialog box, you usually respond to a WM_INITDIALOG message by initializing the combo box. The example in Creating a Square Meal Dialog Box loads the bitmaps used for the owner-drawn combo box, then calls the application-defined InitGroupList function to initialize the combo box. It also selects the first list item in the combo box, then calls the application-defined InitFoodList function to initialize the list box.

In the example, the owner-drawn combo box is a drop-down list box containing the names of each of the four food groups. InitGroupList adds the name of each food group, and calls the application-defined SetItemData function to associate a constant with each list item that identifies a corresponding food group.

The list box in the example contains the names of foods in the selected food group. InitFoodList resets the contents of the list box, then adds the names of the current food selection in the current food group drop-down list box.

The dialog box procedure processes the WM_DESTROY message to delete the bitmaps in the owner-drawn combo box.

+$#A Processing the WM_MEASUREITEM Message

An owner-drawn combo box sends the WM_MEASUREITEM message to its parent window or dialog box procedure so the application can set the dimensions of each list item. Because the example combo box has the CBS_OWNERDRAWFIXED style, the system sends the WM_MEASUREITEM message only once. Combo boxes with the CBS_OWNERDRAWVARIABLE style send a WM_MEASUREITEM message for each list item.

The lParam parameter points to a MEASUREITEMSTRUCT structure that identifies the control and list item. It also contains the default dimensions of the list item. The example in Creating a Square Meal Dialog Box modifies the itemHeight structure member to ensure that the list items are high enough to accommodate the food-group bitmaps.

+$#A Processing the WM_DRAWITEM Message

An owner-drawn combo box sends the WM_DRAWITEM message to its parent window or dialog box procedure each time the application must repaint a list item. The lParam parameter points to a DRAWITEMSTRUCT structure that identifies the control and list item. It also contains information needed to paint the item.

The example in Creating a Square Meal Dialog Box displays the list-item text and the bitmap associated with the food group. If the item has the focus, it also draws a focus rectangle. Before displaying the text, the example sets the foreground and background colors, based on the item selected. Because the combo box has the CBS_HASSTRINGS style, the combo box maintains the text for each list item that can be retrieved using the CB_GETLBTEXT message.

The bitmaps used for the list item depend on the food group. InitGroupList uses the CB_SETITEMDATA message to associate a constant with each list item, identifying the corresponding food group. The window procedure uses this value, contained in the itemData member of the DRAWITEMSTRUCT structure, to determine which bitmaps to display. The system uses two bitmaps for each food group symbol: a monochrome bitmap with the SRCAND raster operation to erase the irregular region behind the image, and a color bitmap with the SRCPAINT raster operation to paint the image.

+$# Processing the WM_COMMAND Message in an Owner-Drawn Dialog Box

When an event occurs in a dialog box control, the control notifies the dialog box procedure by means of a WM_COMMAND message. The example in Creating a Square Meal Dialog Box processes notification messages from the combo box, the list box, and the OK button. The control identifier is in the low-order word of wParam, and the notification message is in the high-order word of wParam.

If the control identifier is IDCOMBO, an event has occurred in the combo box. In response, the dialog box procedure ignores all other combo box events except CBN_SELENDOK, which indicates that a selection was made, the drop down was closed up, and the changes made should be accepted. The dialog box procedure calls InitFoodList to reset the contents of the list box and to add the names of the current selection in the drop-down list box.

If the control identifier is IDLIST, an event has occurred in the list box. This causes the dialog box procedure to ignore all list box events except LBN_DBLCLK, which indicates that the user has double-clicked a list item. This event is processed in the same way as if an OK button has been chosen.

If the control identifier is IDOK, the user has chosen the OK button. In response, the dialog box procedure inserts the name of the selected food into the application's multiline edit control, then calls the EndDialog function to close the dialog box.

If the control identifier is IDCANCEL, the user has clicked the Cancel button. In response, the dialog box procedure calls EndDialog to close the dialog box.

+$#A Creating a Square Meal Dialog Box

Following are the dialog box procedure and supporting functions for the Square Meal dialog box.

HWND hwndMain;

HWND hwndEdit;

char achTemp[256]; /* temporary buffer */

HBITMAP hbmBread;

HBITMAP hbmDairy;

HBITMAP hbmFruit;

HBITMAP hbmMeat;

HBITMAP hbmBreadMask;

HBITMAP hbmDairyMask;

HBITMAP hbmFruitMask;

HBITMAP hbmMeatMask;

/********************************************************

FUNCTION: FoodDlgProc

PURPOSE: Dialog procedure for Food dialog box.

*********************************************************/

BOOL CALLBACK FoodDlgProc(hwndDlg, msg, wParam, lParam)

HWND hwndDlg;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

LPMEASUREITEMSTRUCT lpmis;

LPDRAWITEMSTRUCT lpdis;

HBITMAP hbmIcon;

HBITMAP hbmMask;

COLORREF clrBackground;

COLORREF clrForeground;

TEXTMETRIC tm;

HDC hdc;

HWND hwnd;

int x;

int y;

switch (msg) {

case WM_INITDIALOG:

/*

* Call an application-defined function to load

* bitmap resources.

*/

if (!LoadIconBitmaps()) {

EndDialog(hwndDlg, -1);

break;

}

/* Initialize the drop-down list box. */

if (!InitGroupList(hwndDlg)) {

DeleteIconBitmaps();

EndDialog(hwndDlg, -1);

break;

}

/* Select the first food group. */

SendDlgItemMessage(hwndDlg, IDCOMBO, CB_SETCURSEL,

0, 0);

/* List the foods and select the first food. */

InitFoodList(hwndDlg);

SendDlgItemMessage(hwndDlg, IDLIST, LB_SETCURSEL,

0, 0);

break;

case WM_MEASUREITEM:

lpmis = (LPMEASUREITEMSTRUCT) lParam;

if (lpmis->itemHeight < CY_BITMAP + 2)

lpmis->itemHeight = CY_BITMAP + 2;

break;

case WM_DRAWITEM:

lpdis = (LPDRAWITEMSTRUCT) lParam;

if (lpdis->itemID == -1) /* empty item */

break;

/* Determine the bitmaps used to draw the icon. */

switch (lpdis->itemData) {

case ID_BREAD:

hbmIcon = hbmBread;

hbmMask = hbmBreadMask;

break;

case ID_DAIRY:

hbmIcon = hbmDairy;

hbmMask = hbmDairyMask;

break;

case ID_FRUIT:

hbmIcon = hbmFruit;

hbmMask = hbmFruitMask;

break;

default: /* meat */

hbmIcon = hbmMeat;

hbmMask = hbmMeatMask;

break;

}

/*

* The colors depend on whether the item is

* selected.

*/

clrForeground = SetTextColor(lpdis->hDC,

GetSysColor(lpdis->itemState & ODS_SELECTED ?

COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));

clrBackground = SetBkColor(lpdis->hDC,

GetSysColor(lpdis->itemState & ODS_SELECTED ?

COLOR_HIGHLIGHT : COLOR_WINDOW));

/* Calculate the vertical and horizontal position. */

GetTextMetrics(lpdis->hDC, &tm);

y = (lpdis->rcItem.bottom + lpdis->rcItem.top -

tm.tmHeight) / 2;

x = LOWORD(GetDialogBaseUnits()) / 4;

/* Get and display the text for the list item. */

SendMessage(lpdis->hwndItem, CB_GETLBTEXT,

lpdis->itemID, (LPARAM) (LPCSTR) achTemp);

ExtTextOut(lpdis->hDC, CX_BITMAP + 2 * x, y,

ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,

achTemp, lstrlen(achTemp), NULL);

/* Restore the previous colors. */

SetTextColor(lpdis->hDC, clrForeground);

SetBkColor(lpdis->hDC, clrBackground);

/* Show the icon. */

hdc = CreateCompatibleDC(lpdis->hDC);

if (hdc == NULL)

break;

SelectObject(hdc, hbmMask);

BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,

CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCAND);

SelectObject(hdc, hbmIcon);

BitBlt(lpdis->hDC, x, lpdis->rcItem.top + 1,

CX_BITMAP, CY_BITMAP, hdc, 0, 0, SRCPAINT);

DeleteDC(hdc);

/* If the item has the focus, draw focus rectangle. */

if (lpdis->itemState & ODS_FOCUS)

DrawFocusRect(lpdis->hDC, &lpdis->rcItem);

break;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDCOMBO:

if (HIWORD(wParam) == CBN_SELENDOK) {

InitFoodList(hwndDlg);

SendDlgItemMessage(hwndDlg, IDLIST,

LB_SETCURSEL, 0, 0);

}

break;

case IDLIST:

if (HIWORD(wParam) != LBN_DBLCLK)

break;

/* For a double-click, process the OK case. */

case IDOK:

/* Get the text for the selected list item. */

hwnd = GetDlgItem(hwndDlg, IDLIST);

SendMessage(hwnd, LB_GETTEXT,

SendMessage(hwnd, LB_GETCURSEL, 0, 0),

(LPARAM) achTemp);

/* Insert the text into the edit window. */

SendMessage(hwndEdit, EM_REPLACESEL, 0,

(LPARAM) achTemp);

EndDialog(hwndDlg, 0);

break;

case IDCANCEL:

hwnd = GetDlgItem(hwndDlg, IDCOMBO);

if (SendMessage(hwnd, CB_GETDROPPEDSTATE,

0, 0))

SendMessage(hwnd, CB_SHOWDROPDOWN,

FALSE, 0);

else

EndDialog(hwndDlg, 0);

}

break;

case WM_DESTROY:

/*

* Call the application-defined function to free

* bitmap resources.

*/

DeleteIconBitmaps();

break;

default:

return FALSE;

}

return TRUE;

}

/********************************************************

FUNCTION: InitGroupList

PURPOSE: Initializes the "food groups" drop-down

list box.

COMMENTS: The ID of the food group associated with

each list item is saved as item data.

*********************************************************/

BOOL PASCAL InitGroupList(HWND hwndDlg)

{

HWND hwndCombo = GetDlgItem(hwndDlg, IDCOMBO);

DWORD dwIndex;

/* Add an item for each food group. */

LoadString(hinst, ID_BREAD, achTemp, sizeof(achTemp));

dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,

(LPARAM) (LPCSTR) achTemp);

SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_BREAD);

LoadString(hinst, ID_DAIRY, achTemp, sizeof(achTemp));

dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,

(LPARAM) (LPCSTR) achTemp);

SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_DAIRY);

LoadString(hinst, ID_FRUIT, achTemp, sizeof(achTemp));

dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,

(LPARAM) (LPCSTR) achTemp);

SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_FRUIT);

LoadString(hinst, ID_MEAT, achTemp, sizeof(achTemp));

dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0,

(LPARAM) (LPCSTR) achTemp);

SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, ID_MEAT);

return TRUE;

}

/********************************************************

FUNCTION: InitFoodList

PURPOSE: Clears the contents of the food list, and

adds the names of foods for the current

food group.

*********************************************************/

void PASCAL InitFoodList(HWND hwndDlg)

{

HWND hwndCombo = GetDlgItem(hwndDlg, IDCOMBO);

HWND hwndList = GetDlgItem(hwndDlg, IDLIST);

UINT idFoodGroup;

LPSTR lpsz;

LPSTR lpszEnd;

/* Determine the current food group. */

idFoodGroup = SendMessage(

hwndCombo,

CB_GETITEMDATA,

SendMessage(hwndCombo, CB_GETCURSEL, 0, 0),

0

);

/* Clear the list contents. */

SendMessage(hwndList, LB_RESETCONTENT, 0, 0);

.

. /* Add food names for the current food group. */

.

}

+$#A Subclassing a Combo Box

This section demonstrates how to subclass combo boxes so your application can use them outside a dialog box. The example in Creating a Combo-box Toolbar shows the window procedure for a toolbar window containing two combo boxes. By subclassing the edit controls within the combo boxes, the toolbar window intercepts TAB, ENTER, and ESC keys that would otherwise be ignored.

The following illustration shows the toolbar window.

{ewc msdncd, EWGraphic, group10612 1 /a "SDK_02.bmp"}

+$#A Processing the WM_CREATE Message

The example in Creating a Combo-box Toolbar processes the WM_CREATE message to create two combo box controls as child windows. It then subclasses the edit controls (selection fields) in each combo box because they receive the character input for simple and drop-down combo box. The application gets the handle of each edit control by using the ChildWindowFromPoint function.

To subclass the edit controls, the application calls the SetWindowLong function, replacing the address of the class window procedure with the address of the application-defined SubClassProc function. The address of the original window procedure is saved in the global variable lpfnEditWndProc.

SubClassProc intercepts TAB, ESC, and ENTER keys and notifies the toolbar window by sending application-defined messages (WM_TAB, WM_ESC, and WM_ENTER). SubClassProc uses the CallWindowProc function to pass most messages to the original window procedure, lpfnEditWndProc.

+$#A Processing the WM_SETFOCUS Message

When the toolbar window receives the input focus, it immediately sets the focus to the first combo box in the toolbar. To do so, the example in Creating a Combo-box Toolbar calls the SetFocus function in response to the WM_SETFOCUS message.

+$#A Processing the Application-Defined Messages

In the example in Creating a Combo-box Toolbar, SubClassProc sends application-defined messages to the toolbar window when the user presses the TAB, ESC, or ENTER key in a combo box. The WM_TAB message is sent for the TAB key, the WM_ESC message for the ESC key, and the WM_ENTER message for the ENTER key.

The example processes the WM_TAB message by setting the focus to the next combo box in the toolbar. It processes the WM_ESC message by setting the focus to the main application window.

In response to the WM_ENTER message, the example ensures that the current selection for the combo box is valid and then sets the focus to the main application window. If the combo box contains no current selection, the example uses the CB_FINDSTRINGEXACT message to search for a list item that matches the contents of the selection field. If there is a match, the example sets the current selection; otherwise, it adds a new list item.

+$#A Creating a Combo-box Toolbar

Following are the window procedure for the toolbar and the subclass procedure for the two combo boxes.

#define WM_TAB (WM_USER)

#define WM_ESC (WM_USER + 1)

#define WM_ENTER (WM_USER + 2)

HWND hwndMain;

HWND hwndEdit;

WNDPROC lpfnEditWndProc; /* original window procedure for */

/* the combo box edit windows */

int cyToolbar; /* toolbar window height */

/********************************************************

FUNCTION: ToolbarWindowProc

PURPOSE: Window procedure for the toolbar window

*********************************************************/

LRESULT CALLBACK ToolbarWindowProc(hwnd, msg, wParam, lParam)

HWND hwnd;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

static HWND hwndEdit1;

static HWND hwndEdit2;

static HWND hwndCombo1;

static HWND hwndCombo2;

.

.

.

POINT pt;

DWORD dwBaseUnits;

HWND hwndCombo;

DWORD dwIndex;

switch (msg) {

case WM_CREATE:

/* Create two combo box child windows. */

dwBaseUnits = GetDialogBaseUnits();

hwndCombo1 = CreateWindow("COMBOBOX", "",

CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,

(6 * LOWORD(dwBaseUnits)) / 4,

(2 * HIWORD(dwBaseUnits)) / 8,

(100 * LOWORD(dwBaseUnits)) / 4,

(50 * HIWORD(dwBaseUnits)) / 8,

hwnd, NULL, hinst, NULL);

hwndCombo2 = CreateWindow("COMBOBOX", "",

CBS_DROPDOWN | WS_CHILD | WS_VISIBLE,

(112 * LOWORD(dwBaseUnits)) / 4,

(2 * HIWORD(dwBaseUnits)) / 8,

(100 * LOWORD(dwBaseUnits)) / 4,

(50 * HIWORD(dwBaseUnits)) / 8,

hwnd, NULL, hinst, NULL);

/* Get the edit window handle for each combo box. */

pt.x = 1;

pt.y = 1;

hwndEdit1 = ChildWindowFromPoint(hwndCombo1, pt);

hwndEdit2 = ChildWindowFromPoint(hwndCombo2, pt);

/*

* Change the window procedure for both edit windows

* to the subclass procedure.

*/

lpfnEditWndProc = (WNDPROC) SetWindowLong(hwndEdit1,

GWL_WNDPROC, (DWORD) SubClassProc);

SetWindowLong(hwndEdit2, GWL_WNDPROC,

(DWORD) SubClassProc);

break;

case WM_SETFOCUS:

SetFocus(hwndCombo1);

break;

case WM_TAB:

if (GetFocus() == hwndEdit1)

SetFocus(hwndCombo2);

else

SetFocus(hwndCombo1);

break;

case WM_ESC:

hwndCombo = GetFocus() == hwndEdit1 ?

hwndCombo1 : hwndCombo2;

/* Clear the current selection. */

SendMessage(hwndCombo, CB_SETCURSEL,

(WPARAM) (-1), 0);

/* Set the focus to the main window. */

SetFocus(hwndMain);

break;

case WM_ENTER:

hwndCombo = GetFocus() == hwndEdit1 ?

hwndCombo1 : hwndCombo2;

SetFocus(hwndMain);

/* If there is no current selection, set one. */

if (SendMessage(hwndCombo, CB_GETCURSEL, 0, 0)

== CB_ERR) {

if (SendMessage(hwndCombo, WM_GETTEXT,

sizeof(achTemp), (LPARAM) achTemp) == 0)

break; /* empty selection field */

dwIndex = SendMessage(hwndCombo,

CB_FINDSTRINGEXACT, (WPARAM) (-1),

(LPARAM) achTemp);

/* Add the string, if necessary, and select it. */

if (dwIndex == CB_ERR)

dwIndex = SendMessage(hwndCombo, CB_ADDSTRING,

0, (LPARAM) achTemp);

if (dwIndex != CB_ERR)

SendMessage(hwndCombo, CB_SETCURSEL,

dwIndex, 0);

}

break;

.

. /* Process additional messages. */

.

default:

return DefWindowProc(hwnd, msg, wParam, lParam);

}

return 0;

}

/********************************************************

FUNCTION: SubClassProc

PURPOSE: Process TAB and ESCAPE keys, and pass all

other messages to the class window

procedure.

*********************************************************/

LRESULT CALLBACK SubClassProc(hwnd, msg, wParam, lParam)

HWND hwnd;

UINT msg;

WPARAM wParam;

LPARAM lParam;

{

switch (msg) {

case WM_KEYDOWN:

switch (wParam) {

case VK_TAB:

SendMessage(hwndToolbar, WM_TAB, 0, 0);

return 0;

case VK_ESCAPE:

SendMessage(hwndToolbar, WM_ESC, 0, 0);

return 0;

case VK_RETURN:

SendMessage(hwndToolbar, WM_ENTER, 0, 0);

return 0;

}

break;

case WM_KEYUP:

case WM_CHAR:

switch (wParam) {

case VK_TAB:

case VK_ESCAPE:

case VK_RETURN:

return 0;

}

}

/*

* Call the original window procedure for default

* processing.

*/

return CallWindowProc(lpfnEditWndProc, hwnd,

msg, wParam, lParam);

}

+$#AK Combo Box Reference

The following functions and messages are associated with combo boxes.

+$#A Combo Box Functions

Following are the functions used with combo boxes.

DlgDirListComboBox
DlgDirSelect
DlgDirSelectComboBox
DlgDirSelectComboBoxEx

+$#A Combo Box Messages

Following are the messages used with combo boxes.

CB_ADDSTRING
CB_DELETESTRING
CB_DIR
CB_FINDSTRING
CB_FINDSTRINGEXACT
CB_GETCOUNT
CB_GETCURSEL
CB_GETDROPPEDCONTROLRECT
CB_GETEDITSEL
CB_GETEXTENDEDUI
CB_GETITEMDATA
CB_GETITEMHEIGHT
CB_GETLBTEXT
CB_GETLBTEXTLEN
CB_GETLOCALE
CB_INSERTSTRING
CB_LIMITTEXT
CB_RESETCONTENT
CB_SELECTSTRING
CB_SETCURSEL
CB_SETEDITSEL
CB_SETEXTENDEDUI
CB_SETITEMDATA
CB_SETITEMHEIGHT
CB_SETLOCALE
CB_SHOWDROPDOWN
CBN_CLOSEUP
CBN_DBLCLK
CBN_DROPDOWN
CBN_EDITCHANGE
CBN_EDITUPDATE
CBN_ERRSPACE
CBN_KILLFOCUS
CBN_SELCHANGE
CBN_SELENDCANCEL
CBN_SELENDOK
CBN_SETFOCUS
WM_COMPAREITEM
WM_DRAWITEM
WM_MEASUREITEM

Créé avec HelpNDoc Personal Edition: Produire des livres électroniques facilement


Common Controls.rtf

+$#AK Common Controls

Common controls are a set of windows that are supported by the common control library, which is a dynamic-link library (DLL) included with the Microsoft® Windows® operating system. Like other control windows, a common control is a child window that an application uses in conjunction with another window to perform input and output (I/O) tasks.

+$#A About Common Controls

Most common controls belong to a window class defined in the common control DLL. The window class and the corresponding window procedure define the properties, appearance, and behavior of the control. To ensure that the common control DLL is loaded, include the InitCommonControls function in your application. You create a common control by specifying the name of the window class when calling the CreateWindowEx function or by specifying the appropriate class name in a dialog box template.

For more detailed information about common controls, see the following topics.

• Animation Controls

• Drag List Boxes

• Header Controls

• Hot-Key Controls

• Image Lists

• List View Controls

• Progress Bars

• Property Sheets

• Rich Edit Controls

• Status Windows

• Tab Controls

• Toolbars

• Tooltip Controls

• Trackbars

• Tree View Controls

• Up-Down Controls

+$#A About Common Control Styles

Each type of common control has a set of control styles that you can use to vary the appearance and behavior of the control. The common control library also includes a set of control styles that apply to two or more types of common controls. The common control styles are described in the following topics.

+$#A About Common Control Messages

Because common controls are windows, an application can manipulate them by using messages, such as WM_GETFONT or WM_SETTEXT. In addition, the window class of each common control supports a set of control-specific messages that an application can use to manipulate the control. An application can use any of the message sending or posting functions to pass messages to the control. In addition, some common controls have a set of macros that an application can use instead of the sending or posting functions. The macros are typically easier to use than the functions.

When a change is made to the system color settings, Windows sends a WM_SYSCOLORCHANGE message to all top level windows. Your top level window must forward the WM_SYSCOLORCHANGE message to its common controls; otherwise, the controls will not be notified of the color change. This ensures that the colors used by your common controls are consistent with those used by other user interface objects. For example, a toolbar control uses the "3D Objects" color to draw its buttons. If the user changes the 3D Objects color but the WM_SYSCOLORCHANGE message is not forwarded to the toolbar, the toolbar buttons will remain in their original color while the color of other buttons in the system changes.

+$#A About Common Control Notification Messages

Common controls are child windows that send notification messages to the parent window when events, such as input from the user, occur in the control. The application relies on these notification messages to determine what action the user wants it to take. Except for trackbars, which use WM_HSCROLL message, common controls send notification messages as WM_NOTIFY messages. The lParam parameter of WM_NOTIFY is either the address of an NMHDR structure or the address of a larger structure that includes NMHDR as its first member. The structure contains the notification code and identifies the common control that sent the notification message. The meaning of the remaining structure members, if any, varies depending on the notification code.

Each type of common control has a corresponding set of notification codes. The common control library also provides notification codes that can be sent by more than one type of common control. Values for common notification values are described in the following topics.

+$#A Common Control Reference

The following functions, messages, notification messages, macros, and structures apply to all common controls.

Notification Processing

FORWARD_WM_NOTIFY
HANDLE_WM_NOTIFY
NMHDR

Mouse Click Notifications

NM_CLICK
NM_DBLCLK
NM_RCLICK
NM_RDBLCLK

Operation-Related Notifications

NM_OUTOFMEMORY

Focus Notifications

NM_KILLFOCUS
NM_RETURN
NM_SETFOCUS

Miscellaneous

GetEffectiveClientRect
INDEXTOSTATEIMAGEMASK
InitCommonControls
ShowHideMenuCtl

+$#A Common Control Functions

The following functions are used with common controls.

GetEffectiveClientRect
InitCommonControls
ShowHideMenuCtl

+$#A Common Control Message

Common controls send the following message to notify the parent window of events that affect the control.

WM_NOTIFY

+$#A Common Control Notification Messages

Common controls send notification messages to the parent window to inform it about events. The following notifications are used with common controls.

NM_CLICK
NM_DBLCLK
NM_KILLFOCUS
NM_OUTOFMEMORY
NM_RCLICK
NM_RDBLCLK
NM_RETURN
NM_SETFOCUS

+$#A Common Control Macros

The following macros are used with common controls.

FORWARD_WM_NOTIFY
HANDLE_WM_NOTIFY
INDEXTOSTATEIMAGEMASK

+$#A Common Control Structure

The following structure is used with common controls.

NMHDR

+$#A Common Control Constants

The following window classes and control styles are used with common controls.

+$#A Common Control Window Classes

The following window class names are provided by the common control library.

Class name | Description
ANIMATE_CLASS | Creates animation controls. These controls silently display an Audio Video Interleaved (AVI) clip.
HOTKEY_CLASS | Creates hot-key controls. These controls make it easy for the user to define hot keys.
PROGRESS_CLASS | Creates progress bars. These controls indicate the progress of a lengthy operation.
STATUSCLASSNAME | Creates status windows. These controls display status information in a horizontal window.
TOOLBARCLASSNAME | Creates toolbars. These controls contain buttons that carry out menu commands.
TOOLTIPS_CLASS | Creates tooltip controls. These controls display a small pop-up window containing a line of text that describes the purpose of a tool in an application.
TRACKBAR_CLASS | Creates trackbars. These controls let the user select from a range of values by moving a slider.
UPDOWN_CLASS | Creates up-down controls. These controls combine a pair of arrows with an edit control. Clicking the arrows increments or decrements the value in the edit control.
WC_HEADER | Creates header controls. These controls display headings at the top of columns of information and let the user sort the information by clicking the headings.
WC_LISTVIEW | Creates list-view controls. These controls display a collection of items, each consisting of an icon and a label, and provide several ways to arrange the items.
WC_TABCONTROL | Creates tab controls. These controls define multiple pages for the same area of a window or dialog box. Each page consists of a set of information or a group of controls that an application displays when the user selects the corresponding tab.
WC_TREEVIEW | Creates tree-view controls. These controls display a hierarchical list of items. Each item consists of a label and an optional bitmap.

+$#A Common Control Styles

Following are the common control styles. Except where noted, these styles apply to header controls, toolbar controls, and status windows.

Common control style | Description
CCS_ADJUSTABLE | Enables a toolbar's built-in customization features, which allow the user to drag a button to a new position or to remove a button by dragging it off the toolbar. In addition, the user can double-click the toolbar to display the Customize Toolbar dialog box, allowing the user to add, delete, and rearrange toolbar buttons.
CCS_BOTTOM | Causes the control to position itself at the bottom of the parent window's client area and sets the width to be the same as the parent window's width. Status windows have this style by default.
CCS_NODIVIDER | Prevents a two-pixel highlight from being drawn at the top of the control.
CCS_NOHILITE | Prevents a one-pixel highlight from being drawn at the top of the control.
CCS_NOMOVEY | Causes the control to resize and move itself horizontally, but not vertically, in response to a WM_SIZE message. If CCS_NORESIZE is used, this style does not apply. Header windows have this style by default.
CCS_NOPARENTALIGN | Prevents the control from automatically moving to the top or bottom of the parent window. Instead, the control keeps its position within the parent window despite changes to the size of the parent. If CCS_TOP or CCS_BOTTOM is also used, the height is adjusted to the default, but the position and width remain unchanged.
CCS_NORESIZE | Prevents the control from using the default width and height when setting its initial size or a new size. Instead, the control uses the width and height specified in the request for creation or sizing.
CCS_TOP | Causes the control to position itself at the top of the parent window's client area and sets the width to be the same as the parent window's width. Toolbars have this style by default.

Créé avec HelpNDoc Personal Edition: Générateur de documentations PDF gratuit


Common Dialog Box Library (Windows NT).rtf

+$#AK Common Dialog Box Library (Windows NT)

The following Windows NT overview discusses the Common Dialog Box Library. Various common dialog boxes are described, and examples are included to help you use common dialog boxes in your Win32-based applications.

+$#A About Common Dialog Boxes

{ewl msdncd, EWGraphic, group10797 0 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview About Common Dialog Boxes.

Common dialog boxes make it easier to develop applications based on the Microsoft® Win32® application programming interface (API). A common dialog box is a dialog box that an application displays by calling a single function rather than by supplying a dialog box procedure and a resource file containing a dialog box template. The dynamic-link library COMDLG32.DLL provides a default procedure and a default template for each type of common dialog box. Each default dialog box procedure processes messages and notifications for a common dialog box and its controls. The default dialog box template defines the appearance of a common dialog box and its controls.

In addition to simplifying the development of Win32-based applications, common dialog boxes are helpful to users because they provide a standard set of controls for performing certain operations. Once Windows developers begin using common dialog boxes extensively in their applications, users will find that they can easily perform the same operations in all applications after mastering one.

This chapter describes the various common dialog boxes and includes examples to help you use common dialog boxes in your Win32-based applications.

Following are the types of common dialog boxes in the order in which they are presented in this chapter.

Name | Description
Color | Displays available colors as well as controls that allow the user to define custom colors.
Font | Displays lists of fonts and point sizes that correspond to available fonts. After the user selects a font, the dialog box displays sample text rendered with that font.
Open | Displays a list of filenames matching any specified extensions, directories, and drives. By selecting one of the listed filenames, the user indicates the file an application should open.
Save As | Displays a list of filenames matching any specified extensions, directories, and drives. By selecting one of the listed filenames, the user indicates the file an application should save.
Print | Displays information about the installed printer and its configuration. By altering and selecting controls in this dialog box, the user specifies how output should be printed and starts the printing process.
Page Setup | Displays the current page configuration and provides options for setting the paper orientation, size, source, and margins (when the printer driver supports these options).
Print Setup | This common dialog box has been superseded by the Page Setup dialog box. Win-32 applications should not use the Print Setup dialog box.
Find | Displays an edit control in which the user can type a string for an application to search. The user can specify which direction to search and can indicate whether the application should match the case of the specified string, and whether the string to match is an entire word.
Replace | Displays two edit controls in which the user can type strings. The first string identifies a word or value that the application should replace, and the second string identifies the replacement word or value.

+$#A Color Dialog Box

{ewl msdncd, EWGraphic, group10797 1 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Color Dialog Box.

The Color dialog box contains controls that enable a user to select and create colors.

Following is a Color dialog box.

{ewc msdncd, EWGraphic, group10797 2 /a "SDK_06.bmp"}

The Basic Colors control displays up to 48 colors. The actual number of colors displayed is determined by the display driver. For example, a VGA driver displays 48 colors, and a monochrome display driver displays only 16. By using the Basic Colors control, the user can select a displayed color.

To display the Custom Colors control, the user clicks the Define Custom Colors button. The Custom Colors control displays custom colors. The user can select one of the 16 rectangles in this control and then create a new color by using one of the following methods:

• Specifying red, green, and blue (RGB) values by using the Red, Green, and Blue edit controls, and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.

• Moving the cursor in the color spectrum control (the box at the upper right of the dialog box) to select hue and saturation values, moving the cursor in the luminosity control (the rectangle to the right of the spectrum control), and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.

• Specifying hue, saturation, and luminosity (HSL) values by using the Hue, Sat, and Lum edit controls, and then choosing the Add to Custom Colors button to display the new color in the selected rectangle.

The Color|Solid control displays the dithered and solid colors that correspond to the user's selection. (A dithered color is a color created by combining one or more pure or solid colors.) The Flags member of the CHOOSECOLOR structure contains a flag that, when specified, displays a Help button.

An application can display the Color dialog box in one of two ways: fully open or partially open. When the Color dialog box is displayed partially open, the user cannot change the custom colors.

+$#A Color Models Used by the Color Dialog Box

The Color dialog box uses two models for specifying colors: the RGB model and the HSL model. Regardless of the model used, internal storage is accomplished by use of the RGB model.

RGB Color Model

The RGB model is used to designate colors for displays and other devices that emit light. Valid red, green, and blue values are in the range 0 through 255, with 0 indicating minimum intensity and 255 indicating maximum intensity. The following illustration shows how the primary colors red, green, and blue can be combined to produce four additional colors. (For display devices, the color black results when the red, green, and blue values are set to 0; in display technology, black is the absence of all colors.)

{ewc msdncd, EWGraphic, group10797 3 /a "SDK_02.bmp"}

Following are eight colors and their associated RGB values.

Color | RGB values
Red | 255, 0, 0
Green | 0, 255, 0
Blue | 0, 0, 255
Cyan | 0, 255, 255
Magenta | 255, 0, 255
Yellow | 255, 255, 0
White | 255, 255, 255
Black | 0, 0, 0

Windows stores internal colors as 32-bit RGB values. The high-order byte of the high-order word is reserved; the low-order byte of the high-order word specifies the intensity of the blue component; the high-order byte of the low-order word specifies the intensity of the green component; and the low-order byte of the low-order word specifies the intensity of the red component.

HSL Color Model

The Color dialog box provides controls for specifying HSL values. The following illustration shows the color spectrum control and the luminosity control that appear in the Color dialog box and shows the ranges of values the user can specify with these controls.

{ewc msdncd, EWGraphic, group10797 4 /a "SDK_03.bmp"}

In the Color dialog box, the saturation and luminosity values must be in the range 0 through 240, and the hue value must be in the range 0 through 239.

Converting HSL Values to RGB Values

The dialog box procedure provided in COMDLG32.DLL for the Color dialog box contains code that converts HSL values to the corresponding RGB values. Following are several colors and their associated HSL and RGB values:

Color | HSL values | RGB values
Red | (0, 240, 120) | (255, 0, 0)
Yellow | (40, 240, 120) | (255, 255, 0)
Green | (80, 240, 120) | (0, 255, 0)
Cyan | (120, 240, 120) | (0, 255, 255)
Blue | (160, 240, 120) | (0, 0, 255)
Magenta | (200, 240, 120) | (255, 0, 255)
White | (0, 0, 240) | (255, 255, 255)
Black | (0, 0, 0) | (0, 0, 0)

+$#A Font Dialog Box

{ewl msdncd, EWGraphic, group10797 5 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Font Dialog Box.

The Font dialog box contains controls that allow a user to choose a font, a font style (such as bold, italic, or regular), a point size, an effect (such as underline or strikeout), or a text color. Following is a Font dialog box.

{ewc msdncd, EWGraphic, group10797 6 /a "SDK_07.bmp"}

+$#A Open and Save As Dialog Boxes

{ewl msdncd, EWGraphic, group10797 7 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Open and Save As Dialog Boxes.

The Open dialog box and the Save As dialog box are similar in appearance. Each contains controls that enable a user to specify the location (drive and directory) and the name of a file or a set of files. In the case of the Open dialog box, the user selects the file or files to be opened. In the case of the Save As dialog box, however, the user selects the file or files to be saved. Following is an Open dialog box.

{ewc msdncd, EWGraphic, group10797 8 /a "SDK_03.bmp"}

+$#A Print Dialog Box

{ewl msdncd, EWGraphic, group10797 9 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Print Dialog Box.

The Print dialog box contains controls that allow a user to configure a printer for a particular print job. The user can make selections, such as print quality, print range, and number of copies (if the printer supports multiple copies).

The Print dialog box differs from other common dialog boxes in that part of its dialog box procedure resides in COMDLG32.DLL and part in a printer driver. A printer driver is a program that configures a printer, converts graphics device interface (GDI) commands to low-level printer commands, and stores commands for a particular print job in a printer's queue.

A printer driver exports a function called ExtDeviceMode that displays a dialog box and its controls. Applications written for previous versions of Windows called the LoadLibrary function to load a device driver, and the GetProcAddress function to obtain the address of the ExtDeviceMode function. Win32-based applications no longer need to call LoadLibrary and GetProcAddress. Instead, an application calls a single function, PrintDlg, to display the Print dialog box and begin a print job. The code for PrintDlg resides in COMDLG32.DLL. The dialog box that appears when an application calls PrintDlg differs slightly from the dialog box that appears when the application calls directly into the device driver. The functionality is very similar in spite of the different appearance.

+$#A Page Setup Dialog Box

{ewl msdncd, EWGraphic, group10797 10 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Page Setup Dialog Box.

The Page Setup dialog box allows the user to set the attibutes of the printed page, including the paper type (envelope, legal, letter, and so on), the paper source (manual feed, tractor feed, sheet feeder, and so on), the page orientation (portrait or landscape), and the width of the page margins. The attributes presented in the dialog box vary depending on the capabilities of the printer.

The dialog box includes an image of a sample page that shows how the user's selections affect the appearance of the printed output. The image consists of a rectangle that represents the selected paper or envelope type. The system uses a dotted-line rectangle to represent the current margins, and fills the rectangle with greeking characters to show how text will look on a printed page.

The system uses a multiple-step process to draw the sample page. You can customize the appearance of the sample page by specifying a hook function that the system calls at each step in the drawing process. When the system calls the hook procedure, it sends a message that contains information that the hook procedure uses to draw in a particular area within the sample page.

+$#A Find and Replace Dialog Boxes

{ewl msdncd, EWGraphic, group10797 11 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Find and Replace Dialog Boxes.

The Find dialog box and the Replace dialog box are similar in appearance. You can use the Find dialog box to add string-search capabilities to an application and the Replace dialog box to add both string-search and string-substitution capabilities.

The Find and the Replace dialog boxes differ from other common dialog boxes in that they are modeless. This means the user can switch between the dialog box and the window that created that dialog box.

+$#A Using Common Dialog Boxes

{ewl msdncd, EWGraphic, group10797 12 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Using Common Dialog Boxes.

This section explains how to perform the following tasks:

• Using the Color dialog box to display basic colors.

• Using the Color dialog box to display custom colors.

• Displaying the Font dialog box in an application.

• Displaying the Open dialog box in an application.

• Displaying the Save As dialog box in an application.

• Monitoring List Box Controls in an Open or Save As dialog box.

• Monitoring Filenames in an Open or Save As dialog box.

• Displaying Explorer-Style Open and Save As Dialog Boxes.

• Displaying a Print dialog box for the default printer.

• Displaying the Find dialog box.

• Displaying the Replace dialog box.

• Processing Messages for a Find or Replace dialog box.

• Customizing common dialog boxes.

+$#A Using the Color Dialog Box to Display Basic Colors

You can have your application display the Color dialog box so that a user can select one color from a list of basic screen colors. This section describes how you can provide code and structures in your application to make this possible.

+$# Initializing the CHOOSECOLOR Structure to Display Basic Colors

Before displaying a Color dialog box, an application must initialize a CHOOSECOLOR structure. This structure should be global or be declared as a static variable. The members of this structure contain information about the following items:

• The structure size

• The window that owns the dialog box

• Any customization of the common dialog box by the application

• The hook function and custom dialog box template to use for a customized version of the Color dialog box

• The RGB values for the selected basic color

If your application does not customize the dialog box and you want the user to be able to select a single color from the basic colors, initialize the CHOOSECOLOR structure in the following manner.

/* Color variables */

CHOOSECOLOR cc; /* common dialog box structure */

COLORREF acrCustClr[16] = {

RGB(255, 255, 255), RGB(239, 239, 239),

RGB(223, 223, 223), RGB(207, 207, 207),

RGB(191, 191, 191), RGB(175, 175, 175),

RGB(159, 159, 159), RGB(143, 143, 143),

RGB(127, 127, 127), RGB(111, 111, 111),

RGB(95, 95, 95), RGB(79, 79, 79),

RGB(63, 63, 63), RGB(47, 47, 47),

RGB(31, 31, 31), RGB(15, 15, 15)

}; /* array of gray colors */

/* Initialize the necessary members. */

cc.lStructSize = sizeof(CHOOSECOLOR);

cc.hwndOwner = hwnd;

cc.lpCustColors = (LPDWORD) acrCustClr;

cc.Flags = CC_PREVENTFULLOPEN;

/* Call the ChooseColor function. */

if (ChooseColor(&cc)){

.

. /* Use cc.lpCustColors to select the requested color. */

.

}

In this example, the array pointed to by lpCustColors contains 16 doubleword RGB values that specify the color white, and the CC_PREVENTFULLOPEN flag is specified in the Flags member to disable the Define Custom Colors button and prevent the user from selecting a custom color.

+$#A Calling the ChooseColor Function to Display Basic Colors

After you initialize the structure, call the ChooseColor function. If the function succeeds and the user chooses the OK button to close the dialog box, the rgbResult member of the CHOOSECOLOR structure contains the RGB values for the basic color the user selected.

+$#A Using the Color Dialog Box to Display Custom Colors

You can have your application display the Color dialog box so that the user can create and select a custom color. This section describes how you can provide code and structures in your application to make this possible.

+$# Initializing the CHOOSECOLOR Structure to Display Custom Colors

Before displaying a Color dialog box, an application must initialize a CHOOSECOLOR structure. This structure should be global or declared as a static variable. The members of this structure contain information about the following items:

• The structure size

• The window that owns the dialog box

• Any customization of the common dialog box by the application

• The hook function and custom dialog box template to use for a customized version of the Color dialog box

• The RGB values for the custom color control

If your application does not customize the dialog box and you want the user to be able to create and select custom colors, initialize the CHOOSECOLOR structure in the following manner.

/* Color variables */

CHOOSECOLOR cc; /* common dialog box structure */

COLORREF acrCustClr[16] = {

RGB(255, 255, 255), RGB(239, 239, 239),

RGB(223, 223, 223), RGB(207, 207, 207),

RGB(191, 191, 191), RGB(175, 175, 175),

RGB(159, 159, 159), RGB(143, 143, 143),

RGB(127, 127, 127), RGB(111, 111, 111),

RGB(95, 95, 95), RGB(79, 79, 79),

RGB(63, 63, 63), RGB(47, 47, 47),

RGB(31, 31, 31), RGB(15, 15, 15)

}; /* array of gray colors */

/* Initialize the necessary members. */

cc.lStructSize = sizeof(CHOOSECOLOR);

cc.hwndOwner = hwnd;

cc.lpCustColors = (LPDWORD) acrCustClr;

cc.Flags = CC_FULLOPEN;

In the previous example, the array pointed to by the lpCustColors member contains sixteen 32-bit RGB values that specify 16 scales of gray, and the CC_FULLOPEN flag is specified in the Flags member to display the complete Color dialog box.

+$#A Calling the ChooseColor Function to Display Custom Colors

After you initialize the structure, you should call the ChooseColor function. If the function succeeds and the user chooses the OK button to close the dialog box, the lpCustColors member points to an array that contains the RGB values for the custom colors requested by the user.

You can give your application more control over custom colors by creating a new message identifier for the string defined by COLOROKSTRING . The application creates the new message identifier by calling RegisterWindowMessage and passing this value as the single parameter. After calling RegisterWindowMessage, the application receives a message immediately before the dialog box closes. The lParam parameter of this message contains a pointer to the CHOOSECOLOR structure. The application can use the lpCustColors member of this structure to check the current color. If the application returns a nonzero value when it processes this message, the dialog box does not close.

Similarly, you can have your application create a new message identifier for the string defined by the SETRGBSTRING value. The application's hook function can use the message identifier returned by calling RegisterWindowMessage with the SETRGBSTRING value to set a color in the dialog box. For example, the following line sets the color selection to blue.

SendMessage(hwndDlg, wSetRGBMsg, 0, (LPARAM) RGB(0, 0, 255));

In this example, the wSetRGBMsg argument is the message identifier returned by RegisterWindowMessage. The lParam parameter of the SendMessage function is set to the RGB values of the desired color. The wParam parameter is not used.

You can have your application specify any valid RGB values in the call to SendMessage. If the RGB values match one of the basic colors, the operating system selects the basic color and updates the spectrum and luminosity controls. If the RGB values do not match one of the basic colors, the system updates the spectrum and luminosity controls, but the basic color selection remains unchanged.

Note that if the Color dialog box is not fully open and the application sends RGB values that do not match one of the basic colors, the operating system does not update the dialog box. Updates are unnecessary because the spectrum and luminosity controls are not visible when the dialog box is only partially open.

For more information about processing registered window messages, see Displaying the Find Dialog Box.

+$#A Displaying the Font Dialog Box in An Application

The Font dialog box appears after you initialize the members in a CHOOSEFONT structure and call the ChooseFont function. This structure should be global or be declared as a static variable. The members of the CHOOSEFONT structure contain information about the following items:

• Which attributes of the font initially appear in the dialog box.

• Which attributes of the font the user has selected.

• Which point size of the font the user has selected.

• Whether the list of fonts corresponds to a printer, a screen, or both.

• Whether the available fonts listed are TrueType only.

• Whether the Effects box should appear in the dialog box.

• Whether dialog box messages should be processed by an application-defined hook function.

• Whether the point sizes of the selectable fonts should be limited to a specified range.

• Whether the dialog box displays only what-you-see-is-what-you-get (WYSIWYG) fonts. (These fonts are resident on both the screen and the printer.)

• Which color ChooseFont uses to render text in the Sample box the first time the application displays the dialog box.

• Which color the user selected for text output.

To display the Font dialog box, include the following steps in your application:

1. If the application requires printer fonts, obtain a device context (DC) handle for the printer and use this handle in the hDC member of the CHOOSEFONT structure. (If the Font dialog box displays only screen fonts, this member should be NULL.)

2. Use appropriate flags in the Flags member of the CHOOSEFONT structure. They must include CF_SCREENFONTS, CF_PRINTERFONTS, or CF_BOTH.

3. Set the rgbColors member of the CHOOSEFONT structure if the default color (black) is not appropriate.

4. Set the nFontType member of the CHOOSEFONT structure by using the appropriate constant.

5. Set the nSizeMin and nSizeMax members of the CHOOSEFONT structure if the CF_LIMITSIZE flag is specified in the Flags member.

6. Call the ChooseFont function.

The following example initializes the CHOOSEFONT structure, calls ChooseFont, and uses the requested font to draw text on the display.

/* Font variables */

CHOOSEFONT cf; /* common dialog box structure */

LOGFONT lf; /* logical font structure */

HDC hdc; /* display DC handle */

HFONT hfont; /* new logical font handle */

HFONT hfontOld; /* original logical font handle */

COLORREF crOld; /* original text color */

/* Initialize the necessary members. */

cf.lStructSize = sizeof (CHOOSEFONT);

cf.hwndOwner = hwnd;

cf.lpLogFont = &lf;

cf.Flags = CF_SCREENFONTS | CF_EFFECTS;

cf.rgbColors = RGB(0, 255, 255);

cf.nFontType = SCREEN_FONTTYPE;

/*

* Display the dialog box, allow the user to

* choose a font, and render the text in the

* window with that selection.

*/

if (ChooseFont(&cf)){

hdc = GetDC(hwnd);

hfont = CreateFontIndirect(cf.lpLogFont);

hfontOld = SelectObject(hdc, hfont);

crOld = SetTextColor(hdc, cf.rgbColors);

TextOut(hdc, 50, 150,

"AaBbCcDdEeFfGgHh", 16);

SetTextColor(hdc, crOld);

SelectObject(hdc, hfontOld);

DeleteObject(hfont);

ReleaseDC(hwnd, hdc);

}

An application can also use the WM_CHOOSEFONT_GETLOGFONT message to retrieve the current LOGFONT structure for the Font dialog box before the user closes the dialog box.

+$#A Displaying the Open Dialog Box in An Application

The Open dialog box appears after you initialize the members of an OPENFILENAME structure and call the GetOpenFileName function.

Before you call GetOpenFileName, structure members contain certain data, such as the name of the directory and the filter that appears in the dialog box. (A filter is a filename extension. The common dialog box code uses the extension to filter appropriate filenames from a directory.) After the call, structure members contain other data, such as the name of the selected file and the number of characters in that filename.

To display an Open dialog box, include the following steps in your application:

1. Store the valid filters in a character array.

2. Set the lpstrFilter member to point to this array.

3. Set the nFilterIndex member to the value of the index that identifies the default filter.

4. Set the lpstrFile member to point to an array that contains the initial filename and receives the selected filename.

5. Set the nMaxFile member to the value that specifies the length of the filename array.

6. Set the lpstrFileTitle member to point to a buffer that receives the title of the selected file.

7. Set the nMaxFileTitle member to specify the length of the buffer.

8. Set the lpstrInitialDir member to point to a string that specifies the initial directory. (If this member does not point to a valid string, it must be set to zero or point to a string set to NULL.)

9. Set the lpstrTitle member to point to a string specifying the name that appears in the title bar of the dialog box. (If this pointer is NULL, the title is Open by default.)

10. Initialize the lpstrDefExt member to point to the default extension. (This extension can be 0, 1, 2, or 3 characters long.)

11. Call the GetOpenFileName function.

The following example initializes an OPENFILENAME structure, calls GetOpenFileName, and opens the file by using the lpstrFile member of the structure. The OPENFILENAME structure should be global or be declared as a static variable.

OPENFILENAME ofn; /* common dialog box structure */

char szDirName[MAX_PATH]; /* directory string */

char szFile[260]; /* filename string */

char szFileTitle[260]; /* file title string */

char szFilter[260]; /* filter string */

char chReplace; /* string separator for szFilter */

int i, cbString; /* integer count variables */

HANDLE hf; /* file handle */

.

.

.

/*

* Obtain the system directory name and

* store it in szDirName.

*/

GetSystemDirectory(szDirName, sizeof(szDirName));

/* Place the terminating null character in the szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,

szFilter, sizeof(szFilter));

/*

* Add a terminating null character to the filter string.

*/

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++){

if (szFilter[i] == chReplace)

szFilter[i] = '\0';

}

/* Set the members of the OPENFILENAME structure. */

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.hwndOwner = hwnd;

ofn.lpstrFilter = szFilter;

ofn.nFilterIndex = 1;

ofn.lpstrFile = szFile;

ofn.nMaxFile = sizeof(szFile);

ofn.lpstrFileTitle = szFileTitle;

ofn.nMaxFileTitle = sizeof(szFileTitle);

ofn.lpstrInitialDir = szDirName;

ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |

OFN_FILEMUSTEXIST;

/* Display the Open dialog box. */

if (GetOpenFileName(&ofn)) {

/* Open the file. */

hf = CreateFile(ofn.lpstrFile, GENERIC_READ,

0, (LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

.

. /* Perform the necessary file operations. */

.

/* Close the file. */

CloseHandle(hf);

} /* end if */

The string referred to by the IDS_FILTERSTRING value in the preceding example is defined as follows in the resource-definition file.

STRINGTABLE

BEGIN

IDS_FILTERSTRING "Bitmap File(*.BMP)|*.bmp|"

END

The vertical bars in this string are used as wildcards. After the LoadString function is used to retrieve the string, the wildcards are replaced with NULL. The wildcard can be any unique character and must be included as the last character in the string. Initializing strings in this manner guarantees that the parts of the string are contiguous in memory and that the string is terminated with two null characters.

An application that can open files over a network can create a new message identifier for the string defined by the SHAREVISTRING value. The application creates the new message identifier by calling RegisterWindowMessage and passing this value as the single parameter. After calling RegisterWindowMessage, the application is notified whenever a sharing violation occurs during a call to the OpenFile function. For more information about processing registered window messages, see Displaying the Find Dialog Box.

+$#A Displaying the Save As Dialog Box in An Application

The Save As dialog box appears after you initialize the members of an OPENFILENAME structure and call the GetSaveFileName function.

Before you call GetSaveFileName, structure members contain certain data, such as the name of the initial directory and a filter string. After the call, structure members contain other data, such as the name of the file to be saved and the number of characters in that filename.

The following example initializes an OPENFILENAME structure, calls GetSaveFileName, and saves the file. The OPENFILENAME structure should be global or be declared as a static variable.

OPENFILENAME ofn; /* common dialog box structure */

char szDirName[MAX_PATH]; /* directory string */

char szFile[260]; /* filename string */

char szFileTitle[260]; /* file title string */

char szFilter[260]; /* filter string */

char chReplace; /* string separator for szFilter */

int i, cbString; /* integer count variables */

HANDLE hf; /* file handle */

/*

* Obtain the system directory name and

* store it in szDirName.

*/

GetSystemDirectory(szDirName, sizeof(szDirName));

/* Place the terminating null character in szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,

szFilter, sizeof(szFilter));

/*

* Add a terminating null character to the filter string.

*/

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++){

if (szFilter[i] == chReplace)

szFilter[i] = '\0';

}

/* Set the members of the OPENFILENAME structure. */

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.hwndOwner = hwnd;

ofn.lpstrFilter = szFilter;

ofn.nFilterIndex = 1;

ofn.lpstrFile = szFile;

ofn.nMaxFile = sizeof(szFile);

ofn.lpstrFileTitle = szFileTitle;

ofn.nMaxFileTitle = sizeof(szFileTitle);

ofn.lpstrInitialDir = szDirName;

ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;

/* Display the Save As dialog box. */

if (GetSaveFileName(&ofn)) {

.

. /* Perform the necessary file operations. */

.

/* Close the file. */

CloseHandle(hf);

} /* end if */

The string referred to by the IDS_FILTERSTRING value in the preceding example is defined in the resource-definition file. It is used in exactly the same way as the IDS_FILTERSTRING value discussed in Displaying the Open Dialog Box in An Application.

+$# Monitoring List Box Controls in an Open or Save As Dialog Box

An application can monitor list box selections to process and display data in custom controls. For example, an application can use a custom control to display the total length, in bytes, of all files selected in the File Name box. One method that the application can use to obtain this value is to recompute the total count of bytes each time the user selects a file or cancels the selection of a file. A faster method is for the application to use the LBSELCHSTRING message to identify a new selection and to add the corresponding file length to the value that appears in the custom control. (Note that, in this example, the custom control is a standard Windows control that is identified in a resource file template for one of the common dialog boxes.)

An application uses RegisterWindowMessage to register the selection change message. After registering the message, the application uses this function's return value to identify messages from the dialog box. The message is processed in the application-defined hook function for the common dialog box. The wParam parameter of each message identifies the list box in which the selection occurred. The low-order word of the lParam parameter identifies the list box item. The high-order word of the lParam parameter is one of the following values.

Value | Meaning
CD_LBSELCHANGE | The item identified by the low-order word of lParam was the item in a single-selection list box.
CD_LBSELSUB | The item identified by the low-order word of lParam is no longer selected in a multiple-selection list box.
CD_LBSELADD | The item identified by the low-order word of lParam was selected from a multiple-selection list box.
CD_LBSELNOITEMS | No items exist in a multiple-selection list box.

For an example that registers a common dialog box message, see Displaying the Find Dialog Box.

+$#A Monitoring Filenames in an Open or Save As Dialog Box

An application can alter the normal processing of an Open or Save As dialog box by monitoring the filename the user types and by performing other unique operations. For example, one application can prevent the user from closing the dialog box if the selected filename is prohibited. Another application, however, can enable the user to select multiple filenames.

To monitor filenames, register the FILEOKSTRING message. Do this by calling the RegisterWindowMessage function and passing the message name as its single parameter. After the message has been registered, the dialog box procedure in COMDLG32.DLL uses it to signal that the user has selected a filename and chosen the OK button and that the dialog box has checked the filename and is ready to return. The dialog box procedure signals these actions by sending the message to the application's hook function. After receiving the message, the hook function should return a value to the dialog box procedure that called it. If the hook function did not process the message, it should return 0; if the hook function processed the message and the dialog box should close, the hook function should return 0; and if the hook function processed the message but the dialog box should not close, the hook function should return 1. All other return values are reserved.

+$#A Displaying Explorer-Style Open and Save As Dialog Boxes

If you specify OFN_EXPLORER in the Flags member of the OPENFILENAME structure used to create the Open or Save As common dialog box, the system creates a dialog box that incorporates many of the user-interface features of the Windows Explorer. In addition, specifying OFN_EXPLORER allows you to customize the Open and Save As dialog boxes by adding a dialog template that includes the items that you want to add to the dialog box.

The OFN_EXPLORER value affects the way that the system interprets the hInstance, lpfnHook, and lpTemplateName members of the OPENFILENAME structure. If the Flags member includes OFN_ENABLETEMPLATE, the lpTemplateName member must point to the name of a dialog template resource in the module identified by hInstance. The dialog template must have the WS_CHILD style, and should also have the DS_3DLOOK style to ensure that the child dialog box incorporates the same user interface style as the common dialog box. The system uses the dialog template to create a child dialog box and positions it at the upper left corner of the standard file dialog box, growing the child dialog box if necessary to fit all controls for both dialog boxes.

If the child dialog box has a control with a identifier equal to stc32, the system attempts to place all of the standard dialog box controls within the stc32 control. If the stc32 control is too small to fit the standard controls, the system moves other controls in the child dialog box to the right of or below the stc32 control to make room ⎯ you don't need to know the exact dimensions of the common dialog box. If there is no stc32 control, the system positions all controls defined by the child dialog template below the standard controls. The system resizes the stc32 control to the size of the standard dialog box.

If the Flags member includes OFN_ENABLETEMPLATEHANDLE, the hInstance member is the memory handle of the dialog template. The system uses this template exactly as it uses the dialog template specified in lpTemplateName when Flags includes OFN_ENABLETEMPLATE, as described in the previous paragraphs.

The system communicates with the child dialog box by sending messages to the dialog procedure of the child dialog box. To specify the dialog box procedure, you must include OFN_ENABLEHOOK in the Flags member of the OPENFILENAME structure, and specify the address the procedure in the lpfnHook member.

When the system creates the dialog box, it sends WM_INITDIALOG message to the child dialog procedure specified in the lpfnHook member. The child dialog procedure receives the message before the system has moved any controls. The lParam parameter specifies the address of the OPENFILENAME structure that was passed to the GetOpenFileName or GetSaveFileName function.

The system also sends notification messages to the child dialog procedure to notify it of user and system activity affecting the child dialog box. The notification messages, sent using the WM_NOTIFY message, consist of the following:

Notification message | Description
CDN_FILEOK | The user click the Open or Save button; the common dialog box is about to close.
CDN_FOLDERCHANGE | A new folder has been opened in the Open or Save As dialog box.
CDN_HELP | The user clicked the Help button.
CDN_INITDONE | The system has finished initializating the dialog box, and the dialog box has finished processing the WM_INITDIALOG message. Also, the system has finished arranging controls in the common dialog box to make room for the controls of the child dialog box.
CDN_SELCHANGE | The user has selected a new file or folder from the file list.
CDN_SHAREVIOLATION | The common dialog box encountered a sharing violation on the file about to be returned.
CDN_TYPECHANGE | The user selected a new file type from the "Files of type" combo box.

If the child dialog procedure needs to return a value to the common dialog box, the procedure must use the SetWindowLong function to set the DWL_MSGRESULT value.

An application can send messages to an explorer-style Open or Save As dialog box to retrieve information about the status of the dialog box. The messages include:

Message | Description
CDM_GETFILEPATH | Retrieves the path and filename of the selected file.
CDM_GETFOLDERIDLIST | Retrieves the address of the item identifier list corresponding to the folder that the dialog box currently has open.
CDM_GETFOLDERPATH | Retrieves the path and name of the currently open folder or directory for the dialog box.
CDM_GETSPEC | Retrieves the filename (not including the path) of the currently selected file in the dialog box.

+$#A Displaying a Print Dialog Box for the Default Printer

To display a Print dialog box for the default printer, initialize a PRINTDLG structure and then call the PrintDlg function.

The members of the PRINTDLG structure can contain information about the following items:

• The printer device context

• The values that should appear in the dialog box controls

• The hook function and custom dialog box template to use for a customized version of the Print dialog box or Print Setup dialog box

To display a Print dialog box for the currently installed printer, include the following steps in your application:

1. Use the PD_RETURNDC flag in the Flags member of the PRINTDLG structure. This flag should be specified only if the application requires a device context (DC) handle.

2. Initialize the lStructSize, hDevMode, and hDevNames members.

3. Call the PrintDlg function and pass a pointer to the PRINTDLG structure just initialized.

Using the PD_RETURNDC flag causes PrintDlg to display the Print dialog box and return a handle identifying a printer device context in the hDC member of the PRINTDLG structure. The application passes the DC handle as the first parameter to the graphics device interface (GDI) functions that render output on the printer.

+$#A Displaying the Page Setup Dialog Box

The Page Setup dialog box allows the user to set the attibutes of the printed page, including the paper type, the paper source, the page orientation, and the width of the page margins. To display the Page Setup dialog box, initialize a PAGESETUPDLG structure and call the PageSetupDlg function.

One of the ways an application can customize the Page Setup dialog box is to use a hook function to paint the sample page of the dialog box. To use such a hook function, you must specify the PSD_ENABLEPAGEPAINTHOOK flag in the Flags member of the PAGESETUPDLG structure, and the name of your hook function in the lpfnPagePaintHook member.

After the dialog box is created, the system makes a sequence of calls to the hook function. Each call corresponds to a step in the drawing process, and includes a message that identifies the area of the sample page to be drawn, and the device context for the sample page. By processing these messages, the hook function can draw in one or more areas of the sample page. For a list of messages, see the description of the PAGESETUPDLG structure.

The following example creates a Page Setup dialog box and uses a hook function to draw the margin rectangle in the sample page area:

void DoPage(HWND hwnd, RECT rcMarg, RECT rcMinMarg, POINT ptPaperSize)

{

PAGESETUPDLG psd = {0};

psd.lStructSize = sizeof(PAGESETUPDLG);

psd.hwndOwner = hwnd;

psd.hDevMode = hDevMode;

psd.hDevNames = hDevNames;

psd.Flags = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS |

PSD_MINMARGINS | PSD_ENABLEPAGEPAINTHOOK;

psd.ptPaperSize = ptPaperSize;

psd.rtMargin = rcMarg;

psd.rtMinMargin = rcMinMarg;

psd.hInstance= g_hinst;

psd.lCustData = 0;

psd.lpfnPagePaintHook = PaintHook;

PageSetupDlg(&psd);

}

BOOL CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

LPRECT lprc;

COLORREF crMargRect;

HDC hdc, hdcOld;

switch (uMsg) {

// Drawing the margin rectangle.

case WM_PSD_MARGINRECT:

hdc = (HDC) wParam;

lprc = (LPRECT) lParam;

// Get the system highlight color.

crMargRect = GetSysColor(COLOR_HIGHLIGHT);

// Create a dash-dot pen of the system highlight color and

// select it into the DC of the sample page.

hdcOld = SelectObject(hdc, CreatePen(PS_DASHDOT, .5,

crMargRect));

// Draw the margin rectangle.

Rectangle(hdc, lprc->left, lprc->top, lprc->right,

lprc->bottom);

// Restore the previous pen to the DC.

SelectObject(hdc, hdcOld);

return TRUE;

default:

return FALSE;

}

return TRUE;

}

+$#A Displaying the Find Dialog Box

The Find dialog box contains controls that allow a user to specify the following items:

• Which string the application should find

• Whether the string specifies a complete word or part of a word

• Whether the application should match the case of the specified string

• Which direction the application should search (preceding or following the current cursor location)

• Whether the application should resume the search, searching for the next occurrence of the string

To display the Find dialog box, initialize a FINDREPLACE structure and call the FindText function. Members of the FINDREPLACE structure contain information about the following items:

• Which window owns the dialog box

• How the application should perform the search

• Which character buffer should receive the string

To initialize the FINDREPLACE structure, perform the following tasks:

1. Set the lStructSize member by using the sizeof operator.

2. Set the hwndOwner member by using the handle that identifies the owner window of the dialog box.

3. If you are customizing the Find dialog box, set the hInstance member to identify the instance of the module that contains your custom dialog box template.

4. Set the Flags member to indicate the selection state of the dialog box options. (For example, specifying the FR_NOUPDOWN flag disables the Up and Down buttons, specifying the FR_NOWHOLEWORD flag disables the Match Whole Word Only check box, and specifying the FR_NOMATCHCASE flag disables the Match Case check box.)

5. If you are supplying a custom dialog box template or hook function, specify additional flags in the Flags member.

6. Set the lpstrFindWhat member to point to the buffer receiving the string to be found.

7. Set the wFindWhatLen member to specify the size, in bytes, of the buffer pointed to by lpstrFindWhat.

8. Set the lCustData member with any custom data your application may need to access.

9. If your application customizes the Find dialog box, set the lpfnHook member to point to your hook function.

10. If your application uses a custom dialog box template, set the lpTemplateName member to point to the string that identifies the template.

The following example initializes the FINDREPLACE structure and then calls FindText. This structure should be global or be declared as a static variable.

/* Find/Replace variables */

FINDREPLACE fr; /* common dialog box structure */

FINDREPLACE FAR* lpfr; /* pointer to dialog box structure */

CHAR szFindWhat[80]; /* buffer receiving string */

HWND hdlg; /* handle of dialog window */

UINT uFindReplaceMsg; /* returned by RegisterMessage() */

/* Initialize the necessary members. */

fr.lStructSize = sizeof(FINDREPLACE);

fr.hwndOwner = hwnd;

fr.lpstrFindWhat = szFindWhat;

fr.wFindWhatLen = sizeof(szFindWhat);

/* Display the modeless Find dialog box. */

hdlg = FindText(&fr);

+$#A Displaying the Replace Dialog Box

The Replace dialog box is similar to the Find dialog box. However, the Replace dialog box has no Direction check box and has three additional controls that enable the user to specify the following:

• Which string to replace

• Whether the application should replace the occurrence of the string currently highlighted

• Whether the application should replace all occurrences of the string

To display the Replace dialog box, initialize a FINDREPLACE structure and call the ReplaceText function.

+$#A Processing Messages for a Find or Replace Dialog Box

The Find and Replace dialog boxes differ from the other common dialog boxes in two respects: they are modeless and their respective dialog box procedures send messages to the application that calls the FindText or ReplaceText function. These messages contain data specified by the user in the dialog box controls, including which direction the application should search for a string, whether the application should match the case of the specified string, and whether the application should match the entire string.

To process messages from a Find or Replace dialog box, an application must register the dialog box's unique message, FINDMSGSTRING.

The application registers this message with the RegisterWindowMessage function. After the application registers the message, it uses the function's return value to identify messages from the Find or Replace dialog box. The following example registers the message with RegisterWindowMessage.

/* Register the FindReplace message. */

uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING);

After the application registers this message, it can process messages for the Find or Replace dialog box by using the RegisterWindowMessage function's return value. The following example processes messages for the Find dialog box and then calls its own SearchFile function to locate the string of text. If the user is closing the dialog box (that is, if the Flags member of the FINDREPLACE structure is FR_DIALOGTERM), the handle is invalidated and the procedure returns zero.

if (message == uFindReplaceMsg){

/*

* Extract a pointer to the FINDREPLACE

* structure from lParam.

*/

lpfr = (FINDREPLACE FAR*) lParam;

/*

* If the system has set the FR_DIALOGTERM

* flag, invalidate the handle identifying

* the dialog box.

*/

if (lpfr->Flags & FR_DIALOGTERM){

hdlg = NULL;

return 0;

}

/*

* If the dialog box is still valid, call the

* application-defined search routine (SearchFile) to

* search for the requested string.

*/

SearchFile((BOOL) (lpfr->Flags & FR_DOWN),

(BOOL) (lpfr->Flags & FR_MATCHCASE));

return 0;

}

+$#A Customizing Common Dialog Boxes

A custom common dialog box is a common dialog box altered to suit a particular Windows-based application. The customization may be complex and include the hiding of original controls, the addition of new controls, or a change in the size of the original dialog box; or it may be simple, involving the alteration of a single existing control.

If you want to customize a common dialog box, you must provide a special hook function and, in most cases, a custom dialog box template. Customizations of this kind require a significant amount of additional code ⎯ displaying a customized common dialog box is not as simple as initializing the members of a structure and calling a single function.

Applications that subclass controls in any of the common dialog boxes must do so while processing the WM_INITDIALOG message in the application's hook function. This method allows the application to receive the control-specific messages first, because it subclasses the control after the common dialog box has installed its subclassing procedures. (The previous hook function should be called for all messages not handled by the application's subclass function, as is standard for subclassing.)

An application cannot subclass a control by defining a local class to override a specific control type. This is not possible because the data segment will not be initialized correctly when the class is called ⎯ the data segment will be the common dialog box's data segment, not the application's data segment.

+$#A Appropriate and Inappropriate Customizations

From the user's perspective, the chief benefit of the common dialog box is its consistent appearance and functionality from application to application. Therefore, it is important that you customize a common dialog box only when it is absolutely necessary for an application. Otherwise, the consistent appearance and simple coding interface are lost. Appropriate customizations leave intact as many of the original controls as possible. Increasing the size of the dialog box or adding new controls in available space already in the dialog box is an appropriate customization. Hiding original controls or otherwise changing the intended functionality of the original controls is a less appropriate customization.

+$#A Hook Functions and Custom Dialog Box Templates

{ewl msdncd, EWGraphic, group10797 13 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Hook Functions and Custom Dialog Box Templates.

Each common dialog box uses the dialog box procedure and dialog box template provided for it in COMDLG32.DLL. The dialog box procedure processes messages and notifications for the common dialog box and its controls. The dialog box template defines the appearance of the dialog box ⎯ its dimensions, its location, and the dimensions and locations of controls within it.

In addition to the procedure and template provided, a custom dialog box requires a hook function that you provide and, usually, a custom version of the dialog box template.

+$#A The Hook Function

{bmc bm0.BMP} Windows NT Overview
For more information, see the Windows 95 overview Hook Function.

The dialog box procedure provided in COMDLG32.DLL for a common dialog box calls the application's hook function if the application specifies the appropriate flag and pointer in the structure for that common dialog box. The structure for each common dialog box contains a Flags member that specifies whether the application defines a hook function and contains an lpfnHook member that points to the hook function, if one exists. If the application sets the Flags member to indicate that a hook function exists, it must also set the lpfnHook member. The following example sets the Flags and lpfnHook members of an OPENFILENAME structure to support an application's hook function.

#include <windows.h>

#include <commdlg.h>

#include <dlgs.h>

#include "generic.h"

OPENFILENAME ofn; /* common dialog box structure */

char szDirName[256]; /* directory string */

char szFile[256]; /* filename string */

char szFileTitle[256]; /* file title string */

char szFilter[256]; /* filter string */

char chReplace; /* string separator for szFilter */

int i, cbString; /* integer count variables */

DWORD cBufLen; /* dword count */

DWORD cTmp; /* dword count */

HANDLE hf; /* original file handle */

HANDLE hfBackup; /* backup file handle */

char szBuffer[256]; /* character buffer */

char szChar[256]; /* character buffer */

char* pszBackupFile; /* pointer to backup filename */

BOOL fBackup; /* TRUE if user checked "backup" */

/* Obtain the system directory name. */

GetSystemDirectory((LPSTR)szDirName, sizeof(szDirName));

/* Place the terminating null character in the szFile. */

szFile[0] = '\0';

/* Load the filter string from the .RC file. */

cbString = LoadString(hinst, IDS_FILTERSTRING,

szFilter, sizeof(szFilter));

/* Add a terminating null character to the string. */

chReplace = szFilter[cbString - 1];

for (i = 0; szFilter[i] != '\0'; i++) {

if (szFilter[i] == chReplace)

szFilter[i] = '\0';

}

/* Initialize the required OPENFILENAME members. */

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.hwndOwner = hwnd;

ofn.hInstance = hinst;

ofn.lpstrFilter = szFilter;

ofn.nFilterIndex = 1;

ofn.lpstrFile = szFile;

ofn.nMaxFile = sizeof(szFile);

ofn.lpstrFileTitle = szFileTitle;

ofn.nMaxFileTitle = sizeof(szFileTitle);

ofn.lpstrInitialDir = szDirName;

ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |

OFN_FILEMUSTEXIST | OFN_ENABLEHOOK |

OFN_ENABLETEMPLATE;

ofn.lpfnHook = FileOpenHookProc;

ofn.lpTemplateName = "FILEOPEN";

In the previous example, the address of the application-defined hook function is assigned to the lpfnHook member of the OPENFILENAME structure. The hook function processes any messages or notifications the custom dialog box requires. With the exception of one message (WM_INITDIALOG), the hook function receives messages and notifications before the dialog box procedure provided in COMDLG32.DLL receives them. In the case of WM_INITDIALOG, the hook function receives the message after the dialog box procedure. When the hook function finishes processing a message, it returns a value that indicates whether the dialog box procedure provided in COMDLG32.DLL should also process the message. If the dialog box procedure should process the message, the return value is FALSE. However, if the dialog box procedure should ignore the message, the return value is TRUE.

To process the message from the OK button after the dialog box procedure processes it, an application must post a message to itself when the OK message is received. When the application receives the message it has posted, the common dialog box procedure will have finished processing messages for the dialog box. This technique is particularly useful when working with the Find and Replace dialog boxes, because the Flags member of the FINDREPLACE structure does not reflect changes to the dialog box until after the messages have been processed by COMDLG32.DLL.

The following example shows a hook function for a custom Open dialog box.

UINT FAR PASCAL FileOpenHookProc(HWND hdlg, UINT msg, WPARAM wParam,

LPARAM lParam)

{

switch (msg) {

/* WM_INITDIALOG is received after commdlg

/* is processed.

*/

case WM_INITDIALOG:

return TRUE;

case WM_COMMAND:

/* Use IsDlgButtonChecked to set lCustData. */

if (wParam == IDOK)

/* Set the "backup file" flag. */

fBackup = (DWORD) IsDlgButtonChecked(hdlg,

ID_CUSTCHX);

return FALSE; /* Allow standard processing. */

break;

default:

return FALSE;

} /* end switch */

return FALSE;

}

This hook function tests a custom check box when the user chooses the OK button. If the check box is selected, the hook function sets a global variable to a nonzero value; otherwise, it sets this variable to zero.

A hook function should never call the EndDialog function. Instead, if a hook function contains code that abnormally terminates a common dialog box, this code should pass the IDABORT value to the dialog box procedure by using the PostMessage function, as shown in the following example.

PostMessage(hDlg, WM_COMMAND, IDABORT, (LONG) FALSE);

When a hook function posts the IDABORT value, the common dialog box function returns the value contained in the low-order word of the lParam parameter. For example, if the hook function for GetOpenFileName calls the PostMessage function with (LONG) 100 as the last parameter, GetOpenFileName returns 100.

A hook function must be exported in an application's module-definition (.DEF) file, as shown in the following example.

NAME Generic ; application's module name

DESCRIPTION 'Sample Win32-based Application'

EXETYPE WINDOWS ; required for all Win32-based applications

STUB 'WINSTUB.EXE' ; Generates an error message if the

; application is run without

; Windows

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

EXPORTS

MainWndProc @1 ; window processing function

About @2 ; "About" processing function

FileOpenHookProc @3; common dialog box callback

+$#A Customizing a Dialog Box Template

The dialog box template provided in COMDLG32.DLL for each common dialog box contains the data the dialog box procedure uses to display that common dialog box. Most applications that customize a common dialog box also need to create a custom dialog box template to use instead of the dialog box template in COMDLG32.DLL. (A custom dialog box template is not required for all custom dialog boxes. A template would not be necessary, for instance, if an application changed a dialog box in a relatively minor way and only in an unusual situation.)

A developer should create a custom dialog box template by modifying the appropriate dialog box template in COMDLG32.DLL. Following are the template filenames and the names of their corresponding common dialog boxes.

Template filename | Corresponding dialog box
COLOR.DLG | Color
FILEOPEN.DLG | Open (single selection)
FILEOPEN.DLG | Open (multiple selection)
FINDTEXT.DLG | Find
FINDTEXT.DLG | Replace
FONT.DLG | Font
PRNSETUP.DLG | Print
PRNSETUP.DLG | Print Setup

Note The custom dialog box templates listed above are defined in DLGS.H, except for COLOR.DLG, which is defined in COLORDLG.H.

The following example is from a custom dialog box template created for an Open dialog box.

FILEOPEN DIALOG LOADONCALL MOVEABLE DISCARDABLE

36, 24, 264, 134

CAPTION "Open"

STYLE WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_MODALFRAME

FONT 8, "Helv"

BEGIN

LTEXT "File &Name:", stc3, 6, 6, 76, 10

CONTROL "", edt1, "edit", ES_LEFT | ES_AUTOHSCROLL |

WS_BORDER | WS_TABSTOP | WS_CHILD | ES_OEMCONVERT |

ES_LOWERCASE, 6, 16, 90, 12

CONTROL "", lst1, "listbox", LBS_SORT | LBS_NOTIFY |

LBS_DISABLENOSCROLL | WS_VSCROLL | WS_BORDER |

WS_TABSTOP | LBS_EXTENDEDSEL | LBS_HASSTRINGS |

LBS_OWNERDRAWFIXED, 6, 32, 90, 68

LTEXT "&Directories:", -1, 110, 6, 92, 9

LTEXT "", stc1, 110, 16, 92, 9, SS_NOPREFIX

CONTROL "", lst2, "listbox", LBS_SORT | LBS_HASSTRINGS |

LBS_NOTIFY | LBS_DISABLENOSCROLL | WS_VSCROLL |

WS_CHILD | WS_BORDER | WS_TABSTOP |

LBS_OWNERDRAWFIXED, 110, 32, 92, 68

LTEXT "List Files of &Type:", stc2, 6, 102, 90, 9

CONTROL "", cmb1, "combobox", CBS_DROPDOWNLIST |

CBS_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |

WS_TABSTOP | WS_CHILD, 6, 112, 90, 36

LTEXT "Dri&ves:", stc4, 110, 102, 92, 9

CONTROL "", cmb2, "combobox", CBS_SORT |

CBS_HASSTRINGS | CBS_OWNERDRAWFIXED |

CBS_DROPDOWNLIST | WS_CHILD |

CBS_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |

WS_TABSTOP, 110, 112, 92, 68

DEFPUSHBUTTON "OK", IDOK, 208, 6, 50, 14, WS_GROUP

PUSHBUTTON "Cancel", IDCANCEL, 208, 24, 50, 14, WS_GROUP

PUSHBUTTON "&Help", pshHelp, 208, 46, 50, 14,

WS_GROUP

PUSHBUTTON "Net&work...", psh14, 208, 114, 50, 14,

WS_GROUP

AUTOCHECKBOX "&Read Only", chx1, 208, 68, 50, 12,

WS_TABSTOP | WS_GROUP

AUTOCHECKBOX "&Backup File", ID_CUSTCHX,

208, 86, 50, 12, BS_AUTOCHECKBOX | WS_CHILD |

WS_TABSTOP | WS_GROUP

END

The last entry supports the addition of a new Backup File check box immediately below the existing Read Only check box.

The custom template should be added to the application's resource file. You must use all of the unique control identifiers (that is, all identifiers whose values are other than −1) in the template, even if the dialog box does not use those controls. If you do not want to display all of the controls, you can specify coordinates for them outside the dialog box. You should also disable unwanted buttons and remove unnecessary tab stops.

+$#A Displaying the Custom Dialog Box

After your application creates the hook function and the dialog box template, it should set the members of the structure for the common dialog box being customized and call the appropriate function to display the custom dialog box.

The following example calls the GetOpenFileName function and creates a backup file if the user selects the Backup File check box in the custom Open dialog box.

/* Process the requested file. */

if (GetOpenFileName(&ofn)) {

/*

* If the user checked the Backup File Check box,

* create a backup file.

*/

if (fBackup) {

/* Open the requested file. */

hf = CreateFile(ofn.lpstrFile, GENERIC_READ,

0, (LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

/* Process the extension (if it exists). */

if (ofn.nFileExtension) {

for(i = 0; i < (int)ofn.nFileExtension;

i++)

szChar[i] = *ofn.lpstrFile++;

} /* end if */

else {

i = 0;

while (*ofn.lpstrFile != '\0')

szChar[i++] = *ofn.lpstrFile++;

szChar[i] = '.';

} /* end else */

/* Create the backup filename string. */

pszBackupFile = lstrcat(szChar, "BAK");

/* Create the backup file. */

hfBackup = CreateFile(pszBackupFile, GENERIC_WRITE,

0, (LPSECURITY_ATTRIBUTES) NULL,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

/*

* Copy the contents of the original file

* to the new file.

*/

ReadFile(hf, szBuffer, 256, &cBufLen,

NULL);

while (cBufLen == 256) {

WriteFile(hfBackup, szBuffer, 256, &cBufLen,

NULL);

ReadFile(hf, szBuffer, 256, &cBufLen, NULL);

}

WriteFile(hfBackup, szBuffer, cBufLen, &cTmp,

NULL);

/* Close the files. */

CloseHandle(hfBackup);

CloseHandle(hf);

} /* end if */

} /* end if */

+$#A Supporting Help for Common Dialog Boxes

An application can display a Help button in any of the common dialog boxes by specifying the appropriate flag in the Flags member of the structure for a specific common dialog box. Following are the structures for the common dialog boxes and the Help flag that corresponds to each structure.

Structure | Flag
OPENFILENAME | OFN_SHOWHELP
CHOOSECOLOR | CC_SHOWHELP
FINDREPLACE | FR_SHOWHELP
CHOOSEFONT | CF_SHOWHELP
PRINTDLG | PD_SHOWHELP

If an application displays the Help button, it must process the user's request for help. The processing can be done either in one of the application's window procedures or in a hook function.

If the application processes the request for help in one of the application's window procedures, it must first create a new message identifier for the string defined by the HELPMSGSTRING value. The application creates the new message identifier by calling the RegisterWindowMessage function and passing this value as the single parameter. (For more information about processing registered window messages, see "Displaying the Find Dialog Box" earlier in this chapter.) In addition to creating a new message identifier, the application must use the hwndOwner member of the appropriate structure for the common dialog box, so this member contains the handle of the dialog box's owner window. After the message identifier is created and the hwndOwner member is specified, the dialog box procedure notifies the window procedure of the owner window whenever the user chooses the Help button.

The following example processes a user's request for help in the window procedure of its owner window. The if statement should be in the default section of the switch statement that processes messages.

MyHelpMsg = RegisterWindowMessage(HELPMSGSTRING);

.

.

.

if (message == MyHelpMsg)

WinHelp(hWnd, "appfile.hlp", HELP_CONTEXT, ID_MY_CONTEXT);

If the application processes the request for help in a hook function, it should test for the following condition in the WM_COMMAND message.

wParam == pshHelp

When this condition is true, the hook function should call the WinHelp function, as shown in the preceding example. (To process the request for help in a hook function, you must include the DLGS.H header file in the source file that contains the hook-function code.)

+$#A Error Detection

Whenever a common dialog box function fails, an application can call the CommDlgExtendedError function to find out the cause of the failure. This function returns an error value that identifies the cause of the most recent error.

Six values are defined in the CDERR.H header file that identify the ranges of error values for categories of errors returned by CommDlgExtendedError. Following are these values in ascending order by value range.

Value | Meaning
CDERR_GENERALCODES | General error codes for common dialog boxes. These errors are in the range 0x0000 through 0x0FFF.
PDERR_PRINTERCODES | Error codes for the Print common dialog box. These errors are in the range 0x1000 through 0x1FFF.
CFERR_CHOOSEFONTCODES | Error codes for the Font common dialog box. These errors are in the range 0x2000 through 0x2FFF.
FNERR_FILENAMECODES | Error codes for the Open and Save As common dialog boxes. These errors are in the range 0x3000 through 0x3FFF.
FRERR_FINDREPLACECODES | Error codes for the Find and Replace common dialog boxes. These errors are in the range 0x4000 through 0x4FFF.
CCERR_CHOOSECOLORCODES | Error codes for the Color common dialog box. These errors are in the range 0x5000 through 0x5FFF.

+$#AK Common Dialog Box Reference

The following functions, structures, and messages are associated with common dialog boxes in Windows NT.

+$#A Common Dialog Box Functions

Following are the functions used with common dialog boxes.

ChooseColor
ChooseFont
CommDlgExtendedError
FindText
GetFileTitle
GetOpenFileName
GetSaveFileName
PageSetupDlg
PrintDlg
ReplaceText

+$#A Common Dialog Box Structures

Following are the structures used with common dialog boxes.

OFNOTIFY
PAGESETUPDLG

+$#A Common Dialog Box Messages

Following are the messages used with common dialog boxes.

CDM_GETFILEPATH
CDM_GETFOLDERIDLIST
CDM_GETFOLDERPATH
CDM_GETSPEC
CDM_HIDECONTROL
CDM_SETCONTROLTEXT
CDM_SETDEFEXT
CDN_FILEOK
CDN_FOLDERCHANGE
CDN_HELP
CDN_INITDONE
CDN_SELCHANGE
CDN_SHAREVIOLATION
CDN_TYPECHANGE
COLOROKSTRING
FILEOKSTRING
FINDMSGSTRING
LBSELCHSTRING
SETRGBSTRING
SHAREVISTRING
WM_CHOOSEFONT_SETFLAGS
WM_CHOOSEFONT_SETLOGFONT
WM_PSD_ENVSTAMPRECT
WM_PSD_FULLPAGERECT
WM_PSD_GREEKTEXTRECT
WM_PSD_MARGINRECT
WM_PSD_MINMARGINRECT
WM_PSD_PAGESETUPDLG
WM_PSD_YAFULLPAGERECT

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques EPub facilement


Common Dialog Box Library.rtf

+Chapter 1

+$#AK Common Dialog Box Library

The following Windows 95 overview discusses the Common Dialog Box Library. Various common dialog boxes are described, and examples are included to help you use common dialog boxes in your Win32-based applications.

+$#A Common Dialog Box Library Platform Differences

The overviews for common dialog box libraries in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.

+$#A About Common Dialog Boxes

{ewl msdncd, EWGraphic, group10798 0 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview About Common Dialog Boxes.

Common dialog boxes are the standard file, print, color, font, and search dialog boxes used by applications based on the Microsoft Win32 application programming interface (API). Common dialog boxes provide a uniform user interface that lets users carry out common tasks, such as opening files and printing documents, without being forced to learn new techniques with each application.

You can create common dialog boxes for your own applications by using the common dialog box functions. These functions supply the dialog box procedures and templates for the common dialog boxes; you supply the initial values for the dialog boxes and the addresses of the variables and buffers that receive the input from the dialog boxes.

+$#A Common Dialog Box Basics

Common dialog boxes offer varying levels of control to the application. When you use a common dialog box in its simplest form, you fill a structure with initial values, call a function, and retrieve the user's input from the structure after the function returns. But you can also specify options that modify the appearance and behavior of a common dialog; create hook functions that let you monitor and filter messages sent to the dialog box procedure; use registered messages to receive or send information to the dialog box; and modify or replace the template that defines the number, type, and position of controls in the dialog box. The following sections describe the basics you need to work with common dialog boxes at all levels of control.

+$#A Dialog Box Types

Although you create all common dialog boxes in essentially the same way (by filling a structure with initial values and calling a dialog box function), you use different functions and structures to create the different common dialog box types. There are these common dialog boxes:

Color | Displays available colors and lets the user select basic colors or create a select custom colors. Use the ChooseColor function and CHOOSECOLOR structure.
Font | Displays lists of facenames, point sizes, and other font attributes that the user can select. Use the ChooseFont function and CHOOSEFONT structure.
Open | Displays the names of files, directories and drives, and lets the user select the combination of names that uniquely identifies the file to be opened. Use the GetOpenFileName function and OPENFILENAME structure.
Save As | Displays the names of files, directories and drives, and lets the user select or enter the combination of names that uniquely identifies the file to be saved. Use the GetSaveFileName function and OPENFILENAME structure.
Print | Displays and lets the user select print job options, such as the range of pages to print and the number of copies. Use the PrintDlg function. and PRINTDLG structure.
Page Setup | Displays and lets the user select page configuration options, such as paper orientation, size, source, and margins. Use the PageSetupDlg function and PAGESETUPDLG structure.
Find | Displays and lets the user select a search string and search options, such as the direction of the search. Use the FindText function and FINDREPLACE structure.
Replace | Displays and lets the user select search and replacement strings and replacement options, such as the direction of the search and the scope of the replacement. Use the ReplaceText function and FINDREPLACE structure.

Although a Print Setup dialog box is also available, it has been superseded by the Page Setup dialog box. Applications written for Win32 API version 4.0 or later should not use the Print Setup dialog box.

All common dialog boxes, except the Find and Replace dialog boxes, are modal. This means the function that you use the create the dialog box does not return until the user has closed the dialog box. The Find and Replace dialog boxes are modeless; the function returns before the dialog box closes. If you use the Find and Replace dialog boxes, you must also use the IsDialogMessage function in the main message loop of your application to ensure that these dialog boxes process dialog box keys, such as TAB and ESC, correctly.

+$#A Initialization Flags

You can modify the behavior and appearance of a common dialog box by using initialization flags. These are values that you set in the Flags member of the structure used to create the dialog box. You use the flags to specify which controls in a dialog box receive initial values, to disable selected controls, and to modify the range of values the user can set with the controls. You also use the flags to enable hook functions and custom templates for the dialog boxes.

For example, you set the CF_EFFECTS value in the Flags member of the CHOOSEFONT structure to direct the Font dialog box to display controls that let the user choose the color of the logical font as well as whether to use strike through and underline effects.

The initialization flag values are unique to each common dialog box and are described in detail with the corresponding structures.

+$#A Hook Functions and Custom Dialog Box Templates

{ewl msdncd, EWGraphic, group10798 1 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Hook Functions and Custom Dialog Box Templates.

All common dialog box functions use default dialog box procedures to process messages for the dialog box and its controls. You can customize the operation of a common dialog box by providing a hook function that monitors and responds to messages sent to the default dialog box procedure. You enable a hook procedure by setting an ENABLEHOOK value in the Flags member and the address of the hook function in an lpfnHook member of the structure for the dialog box. A hook function has this general form:

UINT APIENTRY MyHook(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case WM_*:
// process the message
return TRUE;
default:
return FALSE;
}
}

The hook function receives messages and notifications before the dialog box procedure attempts to process them. (The exception is the WM_INITDIALOG message which the hook function receives after the dialog box procedure.) The hook function can process a message or let the dialog box procedure process it. If the hook function processes a message, it must return TRUE; otherwise, it must must return FALSE to direct the dialog procedure to process the message.

If the hook function processes the WM_CTLCOLORDLG message, it must return a valid brush handle for painting the background of the dialog box. In general, if it processes any WM_CTLCOLOR* message, it must return a valid brush handle for painting the background of the given control.

A hook function should never attempt to terminate the dialog box by calling the EndDialog function. Instead, the function must use the PostMessage function to post a WM_COMMAND message with the IDABORT value to the dialog box procedure. Posting IDABORT closes the dialog box and causes the dialog box function to return FALSE. If an application needs a specific error code, it must work out its own comunication mechanism with the hook function.

Although you can subclass controls in any of the common dialog boxes, you must do so from the hook function while processing the WM_INITDIALOG message. Because the common dialog box procedure may also subclass the controls, subclassing after the dialog box procedure ensures that your subclass hook procedure receives the control-specific messages before the subclass hook procedure set by the dialog box procedure.

+$#A Registered Messages and Message Strings

Common dialog boxes send registered messages to your window procedure or hook function to notify you of events and to request responses. A registered message is like any other window message except that the message identifier is not predefined. Before you can detect and process a registered message, you must retrieve the message identifier by passing the predefined message string to the RegisterWindowMessage function. There are these predefined message strings:

COLOROKSTRING | Approve or reject a color selection.
FILEOKSTRING | Approve or reject a filename.
FINDMSGSTRING | Find or replace a given string.
HELPMSGSTRING | Display help for a dialog box.
LBSELCHSTRING | Selection in a list box has changed.
SETRGBSTRING | Set the current color selection.
SHAREVISTRING | Opening a file causes a sharing violation.

The Find and Replace dialog boxes send their registered message, FINDMSGSTRING, to the window procedure of the window that owns the dialog box. Other common dialog boxes send their registered messages to hook functions. This means you must enable the hook function for these dialog boxes to receive the messages. One registered message, SETRGBSTRING, is intended to be sent to the common dialog box from the hook function.

In addition to registered messages, some common dialog boxes send and can accept other window messages. For example, the hook function for a Font dialog box can send the WM_CHOOSEFONT_GETLOGFONT message to retrieve the current logical font selections. These other messages and the registered messages are described in more detail in the sections later in this chapter that described the inidividual common dialog boxes.

+$#A Custom Templates

All common dialog box functions use default templates to define the number, type and position of controls in the dialog box. You can give users access to additional controls that are unique to your application by defining custom templates. For some dialog boxes, the custom template replaces the default template; for others, the custom template defines a "child" dialog box whose controls are combined with the controls specified by the default template.

Because a primary goal for common dialog boxes is to provide a consistent user interface for all applications, it is important that you customize a common dialog box only when it is absolutely necessary. If you do customize, always try to leave the original controls intact. In particular, avoid hiding a control or changing its function. If you add new controls, place them in empty space already in the dialog box or increase the size of the dialog box to make space.

You enable a custom template by setting a ENABLETEMPLATE or ENABLETEMPLATEHANDLE value in the Flags member in the corresponding structure for the dialog box. If the template is a resource in an application or dynamic link library, you must set the hInstance and lpTemplateName members of the structure. If the template is already in memory, the member you set depends on the given structure. In most cases, you must also enable a hook function for the dialog box to support and process input for the additional controls in your custom template.

When making a custom template, make sure the identifiers for the controls that you add are unique and do not conflict with existing controls. The default templates and identifier values are available in resource script (.DLG) and corresponding header (.H) files. Use these to determine the identifiers for the default controls.

+$#A Help Support

All common dialog boxes provide online help by default. The user can display help for individual controls by selecting the control and pressing the F1 key or by clicking the ? button in the title bar and subsequently clicking on a control.

If you customize a dialog box by adding new controls, you also need to extend help support for these controls by processing requests for help in the hook function. The hook function receives WM_HELP or WM_CONTEXTMENU messages when the user requests help by pressing the F1 key, using the ? button, or clicking the right mouse button. You should process these messages for the controls you have added, but let the default dialog box procedure process the messages for the original controls. For more information about how to process these messages, see the chapter describing the WinHelp function.

Although no longer recommended, you can supplement the default help by adding and supporting a Help button in the the dialog box. The Help button is useful for describing the general purpose of the dialog box as it applies to your application. You add a Help button to a dialog box by setting a SHOWHELP value in the Flags member of the structure for the dialog box. You support the Help button by processing the registered message associated with the HELPMSGSTRING message string. The common dialog box sends this message to the window procedure of the owner window whenever the user clicks the Help button.

+$#A Color Dialog Box

{ewl msdncd, EWGraphic, group10798 2 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Color Dialog Box.

The Color dialog box lets users select from a set of basic colors or create and select from a set of custom colors. You create and display a Color dialog box by initializing a CHOOSECOLOR structure and passing the structure to the ChooseColor function. The Color dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 3 /a "SDK_06.bmp"}

If the user chooses a color and clicks the OK button, ChooseColor returns TRUE and sets the rgbResult member of the CHOOSECOLOR structure to the RGB color value corresponding to the color selected by the user. The RGB color value specifies the intensities of the individual red, green, and blue colors that make up the selected color. The individual values range from 0 through 255. Use the RGB macro to create color values, and the GetRValue, GetBValue, and GetGValue macros to extract individual colors from a color value.

If the user cancels the Color dialog box or an error occurs, ChooseColor returns FALSE and the rgbResult member is not defined. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.

The user can select from either the basic colors or custom colors. The basic colors, set by the Color dialog box, represent a reasonable selection of the colors available on the given device. Custom colors are reserved for colors that you choose or that the user creates. You must set the initial values for the 16 custom colors by setting the elements of the array pointed to by the lpCustColors member of the CHOOSECOLOR structure to the desired RGB color values.

You can modify the appearance and behavior of the Color dialog box by setting the Flags member of the CHOOSECOLOR structure to selected values. For example, you can direct the dialog box to display only solid colors by using the CC_SOLIDCOLOR value and you can direct the dialog box to propose an initial color selection (other than black) by setting the rgbResult member to the desired color value and using the CC_RGBINIT value.

The user can create custom colors by clicking the Define Custom Colors button in the dialog box. This displays an extension to the Color dialog box with which the user can pick a color, enter an RGB color value directly, or enter an RGB value indirectly by supplying hue, saturation, and luminousity (HSL) values. You can direct the Color dialog box to always display this extension by setting the Flags member to CC_FULLOPEN. You can prevent this extension from displaying by using the CC_PREVENTFULLOPEN value to disable the Define Custom Colors button. When the user creates a custom color, the dialog box also copies the new RGB color value to the corresponding element in the array pointed to by the lpCustColors member. To preserve new custom colors between calls to ChooseFont, it is recommended that you allocate static memory for the array.

You can enable a hook function for the dialog box by setting the CC_ENABLEHOOK value in the Flags member; the lpfnHook member must specify the address of the hook function. The dialog box sends the registered message defined by the COLOROKSTRING message string to the hook function whenever the user selects a color and click the OK button. The hook function returns a nonzero value to accept this color value; zero to reject it and keep the dialog box open. You can force the dialog box to select a given color by sending the registered message defined by the SETRGBSTRING message string from the hook function to the dialog box.

You can customize the Color dialog box by setting the CC_ENABLETEMPLATE or CC_ENABLETEMPLATEHANDLE value in the Flags member. Because the Color dialog box replaces the default template with the custom template, you should always start with the default template, specified in the COLOR.DLG file, when creating the custom template.

+$#A Font Dialog Box

{ewl msdncd, EWGraphic, group10798 4 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Font Dialog Box.

The Font dialog box lets the user choose attributes for a logical font, such as typeface name, style (bold, italic, or regular), point size, effect (underline or strikeout), text color, and script (or character set). You create and display a Font dialog box by initializing a CHOOSEFONT structure and passing the structure to the ChooseFont function. The Font dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 5 /a "SDK_07.bmp"}

If the user chooses font attributes and clicks the OK button, ChooseFont returns TRUE and sets the members of the LOGFONT structure pointed to by the lpLogFont member of the CHOOSEFONT structure. You can use the LOGFONT structure with the CreateFontIndirect function to create a logical font.

If the user cancels the Font dialog box or an error occurs, ChooseFont returns FALSE and the LOGFONT structure is not defined. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.

When initializing the CHOOSEFONT structure, you must set the Flags member to CF_SCREENFONTS, CF_PRINTERFONTS, or CF_BOTH. To display the options for strikeout, underline, and color, you must use the CF_EFFECTS value.

To list device fonts for a printer, you must obtain a device context (DC) handle for the printer and use this handle in the hDC member of the CHOOSEFONT structure. To restrict the fonts the dialog box displays, you can use the CF_TTONLY, CF_FIXEDPITCHONLY, CF_NOVECTORFONTS, CF_NOVERTFONTS, CF_SCALABLEONLY, and CF_WYSIWYG values. You can also restrict the available styles, point sizes, and effects the dialog box displays for some fonts by using the CF_NOSIMULATIONS value. You can restrict the scripts represented by the displayed fonts by using the CF_SCRIPTSONLY and CF_SELECTSCRIPT values.

You can use the CF_INITTOLOGFONTSTRUCT value in Flags to initialize the controls in the dialog box with the values given in the LOGFONT structure. To initialize the text color, set the rgbColors member of CHOOSEFONT to a RGB color value. You can use the CF_USESTYLE to initialize the style control to the style name given in the buffer pointed to by the lpszStyle member. Use the CF_NOFACESEL, CF_NOSTYLESEL, CF_NOSIZESEL, and CF_NOSCRIPTSEL values to selectively prevent the dialog box from displaying initial values for the corresponding controls. This is useful when working with a selection of text that has more than one typeface, style, point size, or script value. These values will also be set in Flags when ChooseFont returns if the user did not select a corresponding value.

You can force the user to enter only valid typeface names, styles, and point sizes by using the CF_FORCEFONTEXIST value. You can further force the user to enter only point sizes in a given range by using the CF_LIMITSIZE value and setting the minimum and maximum point sizes in the nSizeMin and nSizeMax members.

You can enable the hook function for the Font dialog box by setting the CF_ENABLEHOOK value in Flags and setting the lpfnHook member to the address of the function. The dialog box procedure sends a WM_INITDIALOG message to the hook function with the lParam parameter set to the address of the CHOOSEFONT structure used to initialize the dialog box. The hook function can send the WM_CHOOSEFONT_GETLOGFONT message to the dialog box to retrieve the address of the LOGFONT structure that contains the current selections for the font. This is especially useful when processing processing the WM_COMMAND sent by the Apply button. (You can enable the Apply button by setting the Flags member to CF_APPLY.) The function can send the WM_CHOOSEFONT_SETLOGFONT to force the current selections to given values. The function can also send the WM_CHOOSEFONT_SETFLAGS message to set flags to the given value.

You can customize the Font dialog box by setting the CF_ENABLETEMPLATE or CF_ENABLETEMPLATEHANDLE value in the Flags member. Because the Font dialog box replaces the default template with the custom template, you should always start with the default template, specified in the FONT.DLG file, when creating the custom template.

+$#A Open and Save As Dialog Boxes

{ewl msdncd, EWGraphic, group10798 6 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Open and Save As Dialog Boxes.

The Open dialog box and the Save As dialog box let the user choose the location (drive and directory) and the name of a file or a set of files to open and save. The dialog boxes are nearly identical in appearance and behavior and you initialize them using the same OPENFILENAME structure. You create the Open dialog box by using the GetOpenFileName function and the Save As dialog box by using the GetSaveFileName function.

The appearance of the dialog boxes these functions create depend on the version of the Win32 API and whether you set the OFN_EXPLORER value in the Flags member of the OPENFILENAME structure. When running under version 4.0 or with OFN_EXPLORER, the dialog boxes provide user-interface features that are similar to the Windows Explorer. The Open dialog box looks like this:

{ewc msdncd, EWGraphic, group10798 7 /a "SDK_03.bmp"}

The Save As dialog box looks like this:

{ewc msdncd, EWGraphic, group10798 8 /a "SDK_12.bmp"}

Although the default title for a dialog box is either Open or Save As, you can change the title by setting the lpstrTitle member to the address of a new title. Also, you can hide the Open as read only option by using the OFN_HIDEREADONLY value. If this option is not hidden, the dialog box sets Flags to OFN_READONLY on return if the user checks this option.

+$#A Filenames and Directories

To retrieve a filename from the dialog box, you must set the lpstrFile member of the OPENFILENAME structure to the address of the buffer to receive the filename and set the nMaxFile member to the size of that buffer. Buffer sizes must be in bytes for the ANSI version of the structure and characters for the Unicode version. If the user chooses a filename and clicks the OK button, the dialog box copies the selected drive, directory, and filename to the given buffer. The function also sets the nFileOffset and nFileExtension members to the offsets, in bytes or characters, from the start of the buffer to the filename and to the filename extension, respectively. You can retrieve just the filename from the buffer pointed to by lpstrFile by using the GetFileTitle function. Alternately you can have the dialog box copy the filename to a buffer by setting the lpstrFileTitle member to the address of the buffer and the nMaxFileTitle member to size of the buffer.

If the user cancels the dialog box or an error occurs, the function returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value. If the buffer is too small to receive the full name, the CommDlgExtendedError function returns FNERR_BUFFERTOOSMALL and the first 2 bytes of the buffer pointed to by the lpstrFile member are set to an integer value specifying the size required to receive the full name.

The dialog box uses the current directory as the initial directory from which to display files and directories. You can set your own initial directory by setting the lpstrInitialDir member to the address of a string that specifies a directory. To prevent the dialog box from changing the current directory for the application, you can set the Flags member to OFN_NOCHANGEDIR. The user can still change directories to find a file, but the dialog box returns to the original directory when it closes.

You can direct the dialog box to add a default filename extension, if the user does not, by setting the lpstrDefExt member to the address of a string specifying an extension. Otherwise, the dialog box leaves filenames without extensions. If the user explicitly enters an extension or selects a file that already has an extension, the dialog box sets the Flags member to OFN_EXTENSIONDIFFERENT if that extension is different than that given by lpstrDefExt.

You can let the user select more than one file from a directory by using the OFN_ALLOWMULTISELECT value. If this case, the dialog box copies two or more consecutive, null-terminated strings to the buffer pointed to by lpstrFile. The first string specifies the selected drive and directory; all subsequent strings specify the names of files. The last string ends with an extra null character. The nFileExtension member is not used, and the nFileOffset member is the offset to the first filename.

+$#A Filters

You can restrict the number of files displayed by the dialog box by setting the lpstrFilter member to the address of a buffer that contains one or more filters. A filter consists of two null-terminated strings, a description and a pattern, one concatenated to the other. The dialog box displays the description to let the user pick which filter to use; it uses the pattern to pick out which files to display. The pattern can be a combination of valid filename characters and the asterisk (*). The asterisk is a wildcard that represents any combination of valid filename characters. The dialog box displays only those files that match the pattern. For example, if the pattern is "*.TXT", the dialog box displays only the files having the filename extension TXT. You can have multiple patterns for a given description, but must separate each pattern from the preceding with a semicolon (;).

You can supply multiple filters by concatenating them in the buffer. You must terminate the last filter with an extra null character. You can specify which filter the dialog box should initially use by setting the nFilterIndex member to the index of the filter. The first filter in the buffer has index 1, the second 2, and so on. If the user changes the filter while using the dialog box, the nFilterIndex member is set to the index of the selected filter on return.

You can create a custom filter by setting the lpstrCustomFilter member to the address of a buffer that contains a single filter and setting the nMaxCustFilter member to the size of the buffer, in characters or bytes. The dialog box always places the custom filter at the beginning of the list of filters and, on return, always updates the pattern part of the filter with the pattern from the filter selected by the user.

+$#A File and Directory Validation

The dialog box automatically checks filenames entered by the user to make sure the names contain valid characters. You can also direct the dialog to verify that the given file or drive and directory exist by using the OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST value, respectively. You can override the filename character validation by using the OFN_NOVALIDATE value.

The dialog box returns an error if opening the selected file would cause a sharing violation. You can direct the dialog box to send notification messages about sharing violations by using the OFN_SHAREAWARE value.

When using the Save As dialog box, you can direct the dialog box to prompt the user for permission to create a new file or overwrite an existing file by using the OFN_CREATEPROMPT and OFN_OVERWRITEPROMPT values. By default, the dialog box tests whether a new file can be created in the selected directory by creating a zero-length test file. You can prevent the creation of this test file by using the OFN_NOTESTFILECREATE value.

You can prevent the dialog box from return names of existing files that have the read-only attribute by using the OFN_NOREADONLYRETURN value. You can also prevent the dialog box from dereferencing link files by using the OFN_NODEREFERENCELINKS value. In this case, the dialog box returns the name of the link file rather than the name of the file referenced by the link file.

+$#A Hook Function

{ewl msdncd, EWGraphic, group10798 9 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview The Hook Function.

You can enable a hook function for the dialog box by using the OFN_ENABLEHOOK value and setting the lpfnHook member to the address of the function. The hook function lets you monitor messages sent from the dialog box procedure. It also lets you support any controls you may add to the dialog box as described in the next section. When enabling the hook function, you should also set the OFN_EXPLORER value in the Flags member to make sure the dialog box displays the Windows Explorer-style user interface. This section only describes the hook function for dialog boxes created using OFN_EXPLORER.

The hook function receives a variety of messages while the dialog box is created and eventually destroyed. For example, the hook function receives a WM_INITDIALOG message with the lParam parameter set to the address of the OPENFILENAME structure used to initialize the dialog box. Most messages, however, are WM_NOTIFY notification messages indicating actions taken by the user in the dialog box. The lParam parameter for each WM_NOTIFY message is the address of a OFNOTIFY structure that defines the action. The code member in the header for the OFNOTIFY structure contains one of these notification values:

CDN_FILEOK | The user click the Open or Save button; the dialog box is about to close.
CDN_FOLDERCHANGE | A new folder has been opened in the Open or Save As dialog box.
CDN_HELP | The user clicked the Help button.
CDN_INITDONE | The system has finished initializing the dialog box, and the dialog box has finished processing the WM_INITDIALOG message. Also, the system has finished arranging controls in the common dialog box to make room for the controls of the child dialog box (if any).
CDN_SELCHANGE | The user has selected a new file or folder from the file list.
CDN_SHAREVIOLATION | The common dialog box encountered a sharing violation on the file about to be returned.
CDN_TYPECHANGE | The user selected a new file type from the "Files of type" combo box.

These messages are useful for modifying the behavior of the dialog box. For example, the hook function receives the CDN_FILEOK message when to user chooses a filename and clicks the OK button. The hook function can accept or reject the selected name by returning 0 or 1, respectively. Rejecting the filename forces the dialog box to remain open. To return a value from the hook function, you must set the message result value by using the SetWindowLong function and the DWL_MSGRESULT value.

Although the WM_NOTIFY messages supersede the registered messages previously used with the Open and Save As dialog boxes (such as the FILEOKSTRING, LBSELCHSTRING, SHAREVISTRING, and HELPMSGSTRING messages), the hook function also receives the superseded message after the WM_NOTIFY message if it does not use SetWindowLong to set the message result value or sets this value to zero.

You can send messages to the dialog box from the hook function to retrieve information about the status of the dialog box. The messages include:

CDM_GETFILEPATH | Retrieves the path and filename of the selected file.
CDM_GETFOLDERIDLIST | Retrieves the address of the item identifier list corresponding to the folder that the dialog box currently has open.
CDM_GETFOLDERPATH | Retrieves the path and name of the currently open folder or directory for the dialog box.
CDM_GETSPEC | Retrieves the filename (not including the path) of the currently selected file in the dialog box.
CDM_HIDECONTROL | Hides the given control.
CDM_SETCONTROLTEXT | Sets the text in the given control.
CDM_SETDEFEXT | Sets the default filename extension for the dialog box.

+$#A Customizing Dialog Boxes

You can customize the Open or Save As dialog box by using the OFN_ENABLETEMPLATE or OFN_ENABLETEMPLATEHANDLE value and setting the hInstance and lpTemplateName members to values that specify a custom dialog box template. Customizing a dialog box lets you add controls to the default Open or Save As dialog box, giving users options that are specific to your application. When customizing a dialog box, you must use the OFN_EXPLORER value to make sure the controls in your custom dialog box are added to (rather than replace) the controls in the default dialog box. This section only describes customizing dialog boxes that you have created using OFN_EXPLORER.

If you use the OFN_ENABLETEMPLATE value, the lpTemplateName member must point to the name or resource identifier of a dialog template resource in the module identified by hInstance. If you use the OFN_ENABLETEMPLATEHANDLE value, hInstance must be the handle of a block of memory containing the the dialog template. In both cases, you must also enable a hook function by using the OFN_ENABLEHOOK value. This hook function serves as the dialog box procedure for the custom dialog box as well as monitors notification messages sent from the default dialog box.

You can create your custom dialog box template as you do any other template, except that you must specify the WS_CHILD and WS_CLIPSIBLINGS styles and should specify the DS_3DLOOK and DS_CONTROL styles. The system requires that the custom dialog box be created as a child of the default Open or Save As dialog box. The WS_CLIPSIBLINGS style ensures that the custom dialog box does not paint over any of the controls in the default dialog box. The DS_3DLOOK style makes sure that the controls in the custom dialog box are consistent with the controls in the default dialog box. The DS_CONTROL style makes sure that the user can use the TAB and other navigation keys to move between all controls, default or custom, in the customized dialog box.

To make room for the new controls, the system expands the default dialog box by the width and height of the custom dialog box. By default, all controls from the custom dialog box are positioned below the controls in the default dialog box. However, you can override this default positioning by including a static text control in your custom dialog box template and assigning it the control identifier value of stc32. (This value is defined in the DLG.H header file.) In this case, the system uses the control as the point of reference for determining where to position the new controls. All new controls above and to the left of the stc32 control are positioned the same amount above and to the left of the controls in the default dialog box. New controls below and to the right of the stc32 control are positioned below and to the right of the default controls. In general, each new control is positioned such that it has the same position relative to the default controls as it had to the stc32 control. To make room for these new controls, the system adds space to the left, right, bottom, and top of the default dialog box as needed.

The system requires the hook function to process all messages intended for the custom dialog box and therefore sends the same window messages to the hook function as to any other dialog box procedure. For example, the hook function receives WM_COMMAND messages when the user clicks on button controls in the custom dialog box. The hook function is responsible for initializing these controls and retrieve values from the controls when the dialog box is closed. Note that when the hook function receives the WM_INITDIALOG message, the system has not yet moved the controls to their final positions.

The default dialog box procedure handles messages for all the controls in the default dialog box, but the hook function receives the WM_NOTIFY notification messages for user actions on these controls as described in the previous section.

+$#A Print Dialog Box

{ewl msdncd, EWGraphic, group10798 10 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Print Dialog Box.

The Print dialog box lets the user select options for a particular print job, such as such as the printer to use, the range of pages to print, and the number of copies. You create and display a Print dialog box by initializing a PRINTDLG structure and passing the structure to the PrintDlg function. The Print dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 11 /a "SDK_08.bmp"}

If the user chooses options and clicks the OK button, PrintDlg returns TRUE and sets the hDevMode and hDevNames members to global memory handles for the DEVMODE and DEVNAMES structures that specify the user's selections. You can use the information in these structures to create a device or information context for the selected printer. If the user cancels the Print dialog box or an error occurs, PrintDlg returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.

When the PrintDlg function returns, you can determine which options the user selected by checking the Flags member. The PD_ALLPAGES, PD_SELECTION, or PD_PAGENUMS value is set to indicate whether the user wants to print all pages, selected pages, or pages in a range of page numbers. If PD_PAGENUMS is set, the nFromPage and nToPage members specify the page range. The PD_COLLATE value indicates that the user wants the pages to be collated when printed. The PD_PRINTTOFILE value indicates that the user selected the Print To File option. You can also set these values in the Flags member before calling PrintDlg to initially set the corresponding option. Use the PD_NOPAGENUMS and PD_NOSELECTION values to disable the corresponding options.

By default, the Print dialog box initially displays information about the current default printer. You can direct it to display information for another installed printer by initializing a DEVMODE or DEVNAMES structure and assigning the global memory handle for the structure to the hDevMode or hDevNames member. The device name you specify in the dmDeviceName member of the DEVMODE structure or in the wDriverOffset member of the DEVNAMES structure must identify a printer device that is also listed in the [Devices] section of the WIN.INI file. Otherwise, PrintDlg returns an error.

When the PrintDlg function successfully returns, the application must render the number of copies in nCopies, printing them collated or not collated depending on whether the PD_COLLATE flag is set. If the application does not support multiple copies or simulated collation, it should specify PD_USEDEVMODECOPIESANDCOLLATE to allow these options only if the printer driver supports them.

You can direct PrintDlg to automatically create a device or information context for the printer by using the PD_RETURNDC or PD_RETURNIC value. The function returns the handle of the context in the hDC member. If you use the PD_RETURNDC value, you can use the device context to generate output for the printer. If the user also selects the Print To File option, however, you should retrieve the address of the filename at the offset indicated by the wOutputOffset member of the DEVNAMES structure. You should specify the address of the filename in the lpszOutput member of the DOCINFO structure. The print subsystem will query the user for the filename. If you do not want the user to select Print To File option, set the Flags member to PD_HIDEPRINTTOFILE to hide the option or PD_DISABLEPRINTTOFILE to disable it.

You can retrieve information about the default printer without displaying the Print dialog box by using the PD_RETURNDEFAULT value. In this case, PrintDlg returns immediately after setting the hDevMode and hDevNames members to handles for structures containing the information.

You can prevent PrintDlg from displaying warnings on errors by using the PD_NOWARNING value. Otherwise, the function displays message boxes on errors such as no printers installed.

You can enable a hook function by using the PD_ENABLEPRINTHOOK value and setting the lpfnPrintHook member to the address of your hook function. You can customize the Print dialog box by using the PD_ENABLEPRINTTEMPLATE or PD_ENABLEPRINTTEMPLATEHANDLE value and specifying a custom dialog box template. PrintDlg uses the custom dialog box to replace the default dialog box. If you use PD_ENABLEPRINTTEMPLATE, you set the hInstance and lpPrintTemplateName members to specify the name and module instance for a dialog box template. If PD_ENABLEPRINTTEMPLATEHANDLE, set hPrintTemplate to the handle of a dialog box template in memory.

Although you can also create and display a Printer Setup dialog box using the PrintDlg function and the PD_PRINTSETUP value, the Printer Setup dialog box has been superseded by the Page Setup dialog box and should not be used. The PD_ENABLESETUPHOOK, PD_ENABLESETUPTEMPLATE, and PD_ENABLESETUPTEMPLATEHANDLE values apply only to the Page Setup dialog box.

+$#A Page Setup Dialog Box

{ewl msdncd, EWGraphic, group10798 12 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Page Setup Dialog Box.

The Page Setup dialog box lets the user to set the attributes of the printed page, such as the paper type (envelope, legal, letter, and so on), the paper source (manual feed, tractor feed, sheet feeder, and so on), the page orientation (portrait or landscape), and the width of the page margins. You create and display a Page Setup dialog box by initializing a PAGESETUPDLG structure and passing the structure to the PageSetupDlg function. The Page Setup dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 13 /a "SDK_09.bmp"}

If the user chooses options and clicks the OK button, PageSetupDlg returns TRUE after setting various members in the PAGESETUPDLG structure to specify the user's selections. You can use this information to prepare the output to be sent to the selected printer. If the user cancels the Page Setup dialog box or an error occurs, the function returns FALSE. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.

When PageSetupDlg returns, the ptPaperSize and rtMargin members contain the values selected by the user. The hDevMode and hDevNames members contain to global memory handles for the DEVMODE and DEVNAMES structures that contain additional page information as well as information about the printer.

By default, the Page Setup dialog box displays information about the current default printer. You can direct the dialog box to display information about a specific printer by setting the members of a DEVMODE or DEVNAMES structure and assigning the global memory handles of the structures to the corresponding member in PAGESETUPDLG . If you specify the name of a printer that is not currently installed, the dialog box displays an error message. You can prevent the dialog box from displaying error messages by using the PSD_NOWARNING value. You can use the PSD_RETURNDEFAULT value to retrieve information about the default printer without displaying the Page Setup dialog box.

By default, measurements returned by the dialog box are in thousandths of inches if inches are the default measurement, or hundredths of millimeters if metric is the default measurement system. You can set the units of measurement for the margin widths in the dialog box by using the PSD_INHUNDREDTHSOFMILLIMETERS and PSD_INTHOUSANDTHSOFINCHES value. You can set the initial values for the margins by using the PSD_MARGINS value and the rtMargin member. The default initial values are one inch. You can set the minimum values that the user can enter for the margins by using the PSD_MINMARGINS values and the rtMinMargin member. The default minimum values are the minimum margins allowed by the printer.

You can prevent users from selecting given options by using the PSD_DISABLEMARGINS, PSD_DISABLEORIENTATION, PSD_DISABLEPAPER, and PSD_DISABLEPRINTER values to disable the corresponding controls.

You can enable a hook function for the dialog box by using the PSD_ENABLEPAGESETUPHOOK value and setting the lpfnPageSetupHook member to the address of your hook function. You can customize the Page Setup dialog box by using the PSD_ENABLEPAGESETUPTEMPLATE or PSD_ENABLEPAGESETUPTEMPLATEHANDLE value and specifying a custom dialog box template. PageSetupDlg uses the custom dialog box to replace the default dialog box. If you use PD_ENABLEPAGESETUPTEMPLATE, you set the hInstance and lpPageSetupTemplateName members to specify the name and module instance for a dialog box template. If PD_ENABLEPAGESETUPTEMPLATEHANDLE, set hPageSetupTemplate to the handle of a dialog box template in memory.

The Page Setup dialog box includes an image of a sample page that shows how the user's selections affect the appearance of the printed output. The image consists of a rectangle that represents the selected paper or envelope type, with a dotted-line rectangle representing the current margins and partial (greek-text) characters to show how text looks on the printed page. You can customize the appearance of the sample page by using the PSD_ENABLEPAGEPAINTHOOK value and setting the lpfnPagePaintHook member to the address of a hook function. The dialog box sends the following paint messages to the hook function whenever it needs a portion of the sample page redrawn:

WM_PSD_PAGESETUPDLG | Notifies the hook function to carry out initialization tasks.
WM_PSD_FULLPAGERECT | Specifies the bounding rectangle of the sample page.
WM_PSD_MINMARGINRECT | Specifies the minimum margin rectangle.
WM_PSD_MARGINRECT | Specifies the margin rectangle.
WM_PSD_GREEKTEXTRECT | Specifies the greek-text rectangle.
WM_PSD_ENVSTAMPRECT | Specifies the envelope-stamp rectangle (envelopes only).
WM_PSD_YAFULLPAGERECT | Specifies the bounding rectangle of the sample page.

The hook function receives these messages in the order in which they are listed. If the hook function processes a given message, it should draw the particular area within the sample page and return TRUE. Otherwise, it should return FALSE.

You can disable the display of the sample page by using the PSD_DISABLEPAGEPAINTING in the Flags member. Disabling the sample page prevents the WM_PSD_* message from being sent to your hook function. It also prevents the default dialog box procedure from painting the same page.

+$#A Find and Replace Dialog Boxes

{ewl msdncd, EWGraphic, group10798 14 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Find and Replace Dialog Boxes.

The Find dialog box and the Replace dialog box let users enter information that you can use to search for and possibly replace text in a document. The Find dialog box lets users enter a string to search for and specify search options. The Replace dialog box lets users enter both a search and replace string and specify search-and-replace options. You create and display a Find dialog box by initializing a FINDREPLACE structure and passing the structure to the FindText function. The Find dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 15 /a "SDK_10.bmp"}

You create and display a Replace dialog box by initializing a FINDREPLACE structure and passing the structure to the ReplaceText function. The Replace dialog box typically looks like this:

{ewc msdncd, EWGraphic, group10798 16 /a "SDK_11.bmp"}

Unlike other common dialog boxes, the Find and the Replace dialog boxes are modeless. This means the user can switch between the dialog box and the window that created that dialog box. This is useful for letting the user search for a string, switch to the application window to work on the string, and switch back to the dialog box to search for another string without repeating the command needed to open the dialog box.

If the FindText or ReplaceText function successfully creates the dialog box, it returns the handle of the dialog box. You can use this handle to move and communicate with the dialog box. If the function cannot create the dialog box, it returns NULL. You can determine the cause of an error by using the CommDlgExtendedError function to retrieve the extended error value.

When the user clicks the Find Next, Replace, or Replace All button in a dialog box, the dialog box procedure sends a registered message to the window procedure of the owner window. To receive and process these messages, you must set the hwndOwner member of the FINDREPLACE structure to the handle of the window to receive the messages and you must register the FINDMSGSTRING message string to retrieve the value of the registered message.

Each registered message contains the address of the FINDREPLACE structure. Before sending the message, the dialog box sets the members of this structure with the latest user selections, including the string to search for, the replacement string (if any) and the search-and-replace options. You must check the Flags member to determine the action to carry out and the options selected.

You can determine whether the search for a string, replace a string, or replace all occurrences of a string by checking the Flags member for the the FR_FINDNEXT, FR_REPLACE, and FR_REPLACEALL value, respectively. When the user eventually closes the dialog box, the dialog box sets the FR_DIALOGTERM value in the Flags member of the FINDREPLACE structure in the last registered message sent to the owner window. After the owner window processes this message, the handle of the dialog box is no longer valid.

You can determine the search options by checking for the FR_DOWN, FR_MATCHCASE, and FR_WHOLEWORD values. If a value is not present, the opposite action is implied. For example, if FR_DOWN is present, the search should be carried out from the current location to the end of the document; but if not present, the search should be to the beginning of the document. You can disable the search options by using the FR_NOUPDOWN, FR_NOMATCHCASE, and FR_NOWHOLEWORD values. You can remove the options from the dialog box by using the FR_HIDEUPDOWN, FR_HIDEMATCHCASE, and FR_HIDEWHOLEWORD values.

You can enable a hook function for a Find dialog box or Replace dialog box by using the FR_ENABLEHOOK value and setting the lpfnHook member to the address of the hook function. If the hook function returns FALSE in response to the WM_INITDIALOG message, the function is responsible for calling the ShowWindow and UpdateWindow functions to display the dialog box any painting operations are complete. You can customize the dialog box by using the FR_ENABLETEMPLATE or FR_ENABLETEMPLATEHANDLE value. The custom dialog box replaces the default dialog box.

+$#A Using Common Dialog Boxes

{ewl msdncd, EWGraphic, group10798 17 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Using Common Dialog Boxes.

• Choosing a color.

• Choosing a font.

• Opening a file.

• Displaying the Print dialog box.

• Setting up the printed page.

• Finding text.

+$#A Choosing a Color

You can let the user choose a color, such as for a new solid brush, by displaying the Color dialog box. To display the dialog box, you initialize the lStructSize, hwndOwner, lpCustColors and Flags members in a CHOOSECOLOR structure and call the ChooseColor function as in the following example:

CHOOSECOLOR cc; // common dialog box structure
COLORREF acrCustClr[16]; // array of custom colors
HWND hwnd; // owner window
HBRUSH hbrush; // brush handle

// Initialize CHOOSECOLOR

cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD) acrCustClr;
cc.Flags = CC_FULLOPEN;

if (ChooseColor(&cc)==TRUE)
hbrush = CreateSolidBrush(cc.rgbResult);

In this example, the CC_FULLOPEN value in the Flags member directs the dialog box to display the Add Custom Colors extension to the dialog box with which the user can create new custom colors. Custom colors are copied back into the array pointed to by lpCustColors.

The ChooseColor function returns TRUE if the user selected a color. The RGB value of the selected is in the rgbResult member of the structure.

+$#A Choosing a Font

You can let the user choose the attributes of font, such as when creating a new logical font, by displaying the Font dialog box. To display the dialog box, you must initialize the members in a CHOOSEFONT structure and call the ChooseFont function as in the following example:

CHOOSEFONT cf; // common dialog box structure
LOGFONT lf; // logical font structure
HWND hwnd; // owner window
HFONT hfont; // new logical font handle

// Initialize CHOOSEFONT
cf.lStructSize = sizeof (CHOOSEFONT);
cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS;

if (ChooseFont(&cf)==TRUE)
hfont = CreateFontIndirect(cf.lpLogFont);

In this example, the CF_SCREENFONTS defines the type of font to display information for and CF_EFFECTS causes the dialog box to display options to let the user select strikethrough, underlining, and color for the font. The function copies the font attributes to the lf variable, a LOGFONT structure.

+$#A Opening a File

You can let the user select the name of a file to open by displaying an Open dialog box. You initialize the lStructSize, hwndOwner, lpstrFile, and nMaxFile members of an OPENFILENAME structure and call the GetOpenFileName function as in the following example:

OPENFILENAME ofn; // common dialog box structure
char szFile[260]; // buffer for filename
HWND hwnd; // owner window
HANDLE hf; // file handle

// Initialize OPENFILENAME
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

// Display the Open dialog box.

if (GetOpenFileName(&ofn)==TRUE)
hf = CreateFile(ofn.lpstrFile, GENERIC_READ,
0, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

In this example, the lpstrFilter member receives the address of a filter that contains two patterns that the user can choose to view all files or just text files. The nFilterIndex member specifies that the first pattern is used when the dialog box is created. The OFN_EXPLORER value ensures that the Open dialog box displays a user-interface that is similar to the Windows Explorer. The OFN_PATHMUSTEXIST and OFN_FILEMUSTEXIST values direct the function to verify the the path and filename that the user enters exists before returning.

+$#A Displaying the Print Dialog Box

You can let the user choose options for printing a document by displaying a Print dialog box. To display the dialog box for the default printer, you initialize a PRINTDLG structure and call the PrintDlg function as in the following example:

PRINTDLG pd;
HWND hwnd;

pd.lStructSize = sizeof(PRINTDLG);
pd.hwndOwner = hwnd;
pd.hDevMode = NULL; // don't forget to free or store hDevMode
pd.hDevNames = NULL; // don't forget to free or store hDevNames
pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE|PD_RETURNDC;
pd.nCopies = 1;

if (PrintDlg(&pd)==TRUE) {

// GDI calls to render output

// Delete DC when done
DeleteDC(pd.hDC);
}

In this example, the PD_RETURNDC value directs PrintDlg to return a device context handle for the selected printer. This handle is used to render output on the printer.

+$#A Setting Up the Printed Page

You can let the user select the attributes of the printed page, such as the paper type, paper source, page orientation, and page margins by displaying a Page Setup dialog box. To display the dialog box, you initialize a PAGESETUPDLG structure and call the PageSetupDlg function as in the following example:

PAGESETUPDLG psd; // common dialog box structure
HWND hwnd; // owner window

psd.lStructSize = sizeof(PAGESETUPDLG);
psd.hwndOwner = hwnd;
psd.hDevMode = NULL; // don't forget to free or store hDevMode
psd.hDevNames = NULL; // don't forget to free or store hDevNames
psd.Flags = PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS;
psd.rtMargin.top = 1000;
psd.rtMargin.left = 1250;
psd.rtMargin.right = 1250;
psd.rtMargin.bottom = 1000;

if (PageSetupDlg(&psd)==TRUE) {
// check paper size and margin values here
}

In this example, the PSD_MARGINS value directs the function to set the initial margin values as specified in the rtMargin member. In this case, the initial and returned margin values are expressed in thousandths of an inch. The initial paper size is the paper size specified in the device mode identified by the hDevMode member of the PAGESETUPDLG structure, or by the default printer's device mode if no printer was specified.

One of the ways an application can customize the Page Setup dialog box is to use a hook function to paint the sample page of the dialog box. You set the hook function by using the PSD_ENABLEPAGEPAINTHOOK value when creating the dialog box. The following example shows a sample hook function that draws the margin rectangle in the sample page area:

BOOL CALLBACK PaintHook(HWND hwndDlg, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

LPRECT lprc;

COLORREF crMargRect;

HDC hdc, hdcOld;

switch (uMsg) {

// Drawing the margin rectangle.

case WM_PSD_MARGINRECT:

hdc = (HDC) wParam;

lprc = (LPRECT) lParam;

// Get the system highlight color.

crMargRect = GetSysColor(COLOR_HIGHLIGHT);

// Create a dash-dot pen of the system highlight color and

// select it into the DC of the sample page.

hdcOld = SelectObject(hdc, CreatePen(PS_DASHDOT, .5,

crMargRect));

// Draw the margin rectangle.

Rectangle(hdc, lprc->left, lprc->top, lprc->right,

lprc->bottom);

// Restore the previous pen to the DC.

SelectObject(hdc, hdcOld);

return TRUE;

default:

return FALSE;

}

return TRUE;

}

+$#A Finding Text

You can let the user enter a string and search options for a search by displaying a Find dialog box and monitoring messages sent by the dialog box. To display and manage the Find dialog box, you need to carry out three steps: register the FINDMSGSTRING message string, call the FindText function to create and display the dialog box, and check for the registered messages in the window procedure of the window that owns the dialog box.

To register the message string, you make the following call when initializing your application:

UINT uFindReplaceMsg; // message id for FINDMSGSTRING

uFindReplaceMsg = RegisterWindowMessage();

To make sure the Find dialog box operates properly when eventually displayed, you must include a call to the IsDialogMessage function in your main message loop.

To display the Find dialog box, you initialize the FINDREPLACE structure and call the FindText function as in the following example:

FINDREPLACE fr; // common dialog box structure
HWND hwnd; // owner window
CHAR szFindWhat[80]; // buffer receiving string
HWND hdlg = NULL; // handle of Find dialog box

fr.lStructSize = sizeof(FINDREPLACE);
fr.hwndOwner = hwnd;
fr.lpstrFindWhat = szFindWhat;
fr.wFindWhatLen = 80;
fr.Flags = 0;

hdlg = FindText(&fr);

Note that the FINDREPLACE structure and the buffer for the search string should be a global or static variable. You must set the hwndOwner member to specify the window that receives the registered messages. Once the dialog box is created, you can move or manipulate it by using the returned handle.

To monitor messages sent from the dialog box, you check for the registered message and process the values passed in the FINDREPLACE structure as in the following example:

LPFINDREPLACE lpfr;

if (msg == uFindReplaceMsg) {
lpfr = (LPFINDREPLACE)lParam;
if (lpfr->Flags & FR_FINDNEXT) {
// search for string specified by lpfr->lpstrFindWhat
} else if (lpfr->FR_DIALOGTERM) {
// user has closed the dialog box
hdlg = NULL;
}
}

To fully process the message, you must check the Flags member for search options, indicated by values such as FR_DOWN, FR_WHOLEWORD, and FR_MATCHCASE.

+$#AK Common Dialog Box Reference

The following functions, structures, and messages are associated with common dialog boxes in Windows 95.

+$#A Common Dialog Box Functions

Following are the functions used with common dialog boxes.

ChooseColor
ChooseFont
CommDlgExtendedError
FindText
GetFileTitle
GetOpenFileName
GetSaveFileName
PageSetupDlg
PrintDlg
ReplaceText

+$#A Common Dialog Box Structures

Following are the structures used with common dialog boxes.

OFNOTIFY
PAGESETUPDLG

+$#A Common Dialog Box Messages

Following are the messages used with common dialog boxes.

CDM_GETFILEPATH
CDM_GETFOLDERIDLIST
CDM_GETFOLDERPATH
CDM_GETSPEC
CDM_HIDECONTROL
CDM_SETCONTROLTEXT
CDM_SETDEFEXT
CDN_FILEOK
CDN_FOLDERCHANGE
CDN_HELP
CDN_INITDONE
CDN_SELCHANGE
CDN_SHAREVIOLATION
CDN_TYPECHANGE
COLOROKSTRING
FILEOKSTRING
FINDMSGSTRING
LBSELCHSTRING
SETRGBSTRING
SHAREVISTRING
WM_CHOOSEFONT_SETFLAGS
WM_CHOOSEFONT_SETLOGFONT
WM_PSD_ENVSTAMPRECT
WM_PSD_FULLPAGERECT
WM_PSD_GREEKTEXTRECT
WM_PSD_MARGINRECT
WM_PSD_MINMARGINRECT
WM_PSD_PAGESETUPDLG
WM_PSD_YAFULLPAGERECT

Créé avec HelpNDoc Personal Edition: Produire des livres EPub gratuitement


Communications.rtf

+$#AK Communications

A communications resource is a physical or logical device that provides a single bidirectional, asynchronous data stream. Serial ports, parallel ports, fax machines, and modems are examples of communications resources. For each communications resource, there is a service provider, consisting of a library or driver, that enables applications to access the resource.

+$#A About Communications

The file input and output (I/O) functions (CreateFile, CloseHandle, ReadFile, ReadFileEx, WriteFile, and WriteFileEx) provide the basic interface for opening and closing a communications resource handle and for performing read and write operations. The Microsoft® Win32® application programming interface (API) also includes a set of communications functions that provide access to communications resources. This overview describes the use of file I/O and communications functions, which enable applications to perform the following tasks:

• Open a handle to a specified communications resource.

• Set and query the configuration of a serial communications resource.

• Read from or write to a serial communications resource.

• Monitor a specified set of events that might occur for a given serial communications resource.

• Send a control command to the device driver associated with a specified communications resource, causing the driver to execute an extended function.

+$#A Communications Resource Handles

A process uses the CreateFile function to open a handle to a communications resource. For example, specifying COM1 opens a handle to a serial port, and LPT1 opens a handle to a parallel port. If the specified resource is currently being used by another process, CreateFile fails. Any thread of the process can use the handle returned by CreateFile to identify the resource in any of the functions that access the resource.

When using CreateFile to open a handle directly to a device, an application must use the special characters " \\ .\" to identify the device. For example, to open a handle to drive A, specify " \\ .\a:" for the lpszName parameter of CreateFile. The calling process can use the handle in the DeviceIoControl function to send control codes to the device.

When the process calls CreateFile to open a communications resource, it specifies the following attributes:

• What type of read-write access exists for the specified resource.

• Whether the handle can be inherited by child processes.

• Whether the handle can be used in overlapped (asynchronous) I/O operations. (For a description of overlapped operations, see Synchronization.)

When the process uses CreateFile to open a communications resource, it must specify certain values for the following parameters:

• The fdwShareMode parameter must be zero, opening the resource for exclusive access.

• The fdwCreate parameter must specify the OPEN_EXISTING flag.

• The hTemplateFile parameter must be NULL.

+$#A Modification of Communications Resource Settings

When the CreateFile function opens a handle to a serial communications resource, the system initializes and configures the resource according to the values set up the last time the resource was opened. Preserving the previous settings enables the user to retain the settings specified through an MS-DOS® mode command when the device is reopened. The values inherited from the previous open operation include the configuration settings of the device control block (a DCB structure) and the time-out values used in I/O operations. If the device has never been opened, it is configured with the system defaults.

To determine the initial configuration of a serial communications resource, a process calls the GetCommState function, which fills in a serial port DCB structure with the current configuration settings. To modify this configuration, a process specifies a DCB structure in a call to the SetCommState function.

Members of the DCB structure specify the configuration settings such as the baud rate, the number of data bits per byte, and the number of stop bits per byte. Other DCB members specify special characters and enable parity checking and flow control. When a process needs to modify only a few of these configuration settings, it should first call GetCommState to fill in a DCB structure with the current configuration. Then the process can adjust the important values in the DCB structure and reconfigure the device by calling SetCommState and specifying the modified DCB structure. This procedure ensures that the unmodified members of the DCB structure contain appropriate values. For example, a common error is to configure a device with a DCB structure in which the structure's XonChar member is equal to the XoffChar member. Some members of the DCB structure are different from those in previous versions of Microsoft Windows®. In particular, the flags for controlling RTS (request-to-send) and DTR (data-terminal-ready) flow control have changed.

The BuildCommDCB function provides another way to modify a DCB structure. BuildCommDCB uses a string with the same form as the command-line arguments of the mode command to specify the baud rate, parity scheme, number of stop bits, and number of data bits. The remaining members of DCB are not changed by this function, except that the appropriate members are set to disable XON/XOFF and hardware flow control. BuildCommDCB only modifies a DCB structure; it does not reconfigure the device.

A process can reconfigure a communications resource by using the GetCommProperties function to get information from a device driver about the configuration settings that it supports. The process can use this information to avoid specifying a configuration that is not supported.

The SetCommState function reconfigures the communications resource, but it does not affect the internal output and input buffers of the specified driver. The buffers are not flushed, and pending read and write operations are not terminated prematurely.

A process reinitializes a communications resource by using the SetupComm function, which performs the following tasks:

• Terminates pending read and write operations, even if they have not been completed.

• Discards unread characters and frees the internal output and input buffers of the driver associated with the specified resource.

• Reallocates the internal output and input buffers.

A process is not required to call SetupComm. If it does not, the resource's driver initializes the device with the default settings the first time that the communications resource handle is used.

+$#A Communications Resource Configuration

The COMMCONFIG structure defines the configuration of a communications resource, serial or otherwise. The format of the structure varies depending on the type of communications resource (the provider subtype). The first few structure members are common to all communications resources; additional members are defined for specific provider subtypes. Specific service providers may extend the COMMCONFIG structure as well.

An application can get and set the configuration of a communications resource by using the GetCommConfig and SetCommConfig functions. When opened, a communications resource is initialized using the default configuration for its provider subtype. To get and set the default configuration for a provider subtype, use the GetDefaultCommConfig and SetDefaultCommConfig functions.

To prompt the user for configuration information, use the CommConfigDialog function. This function displays a dialog box defined by the service provider and fills in a COMMCONFIG structure based on user input.

+$#A Modem Configuration

Modem configuration functions enable you to configure a modem before making a connection. An application can set modem options and determine the features of a modem without using commands specific to any modem device. Following are the general features an application may set before making a call:

• Primary mode of operation (synchronous, asynchronous, and whether error control is enabled).

• V.42 error control (defined by CCITT recommendation V.42), including specific parameters. CCITT stands for the International Telegraph and Telephone Consultative Committee.

• V.42bis (defined by CCITT recommendation V.42bis) and MNP5 data compression.

• Time-out options, including call setup, inactivity, and buffered data delivery.

Before setting a modem's configuration, an application should determine the capabilities of the modem device by using the GetCommProperties function. This function fills in a COMMPROP structure. This structure contains both a general portion, which applies to all communications devices, and a portion that is specific to each provider subtype. For modem devices, the provider-specific portion of the COMMPROP structure is a MODEMDEVCAPS structure.

An application can get and set the current configuration of a modem by using the GetCommConfig and SetCommConfig functions, both of which use a COMMCONFIG structure. This structure contains both a general portion, which applies to all communications devices, and a portion that is specific to each provider subtype. For modem devices, the provider-specific portion of the COMMCONFIG structure is a MODEMSETTINGS structure.

After configuring a modem, an application can use the telephony application programming interface (TAPI) to actually establish a connection.

The modem configuration functions do not provide for long-term management and maintenance of a modem. Modem service providers should supply modem configuration dialog boxes for this purpose.

+$#A Read and Write Operations

The Win32 API supports both synchronous and asynchronous (overlapped) file I/O operations on serial communications resources. Overlapped operations enable the calling thread to perform other tasks while the operation executes in the background. A thread uses the ReadFile or ReadFileEx function to read from a communications resource, and the WriteFile or WriteFileEx function to write to a communications resource. ReadFile and WriteFile can be performed synchronously or asynchronously. ReadFileEx and WriteFileEx can only be performed asynchronously.

The behavior of these read and write functions is affected by whether the function is executed as an overlapped operation, whether the time-out parameters are associated with the handle, and whether flow control parameters are associated with the handle.

A thread can also write to a communications resource by using the TransmitCommChar function, which transmits a specified character ahead of any pending data in the output buffer. This function is useful for transmitting a high priority signal character to the receiving system. Transmission of the high priority character is still subject to flow control and write time-outs, and the operation is performed synchronously.

A thread can use the PurgeComm function to discard all characters in a device's output or input buffer. PurgeComm can also terminate pending read or write operations, even if the operations have not been completed. If a thread uses PurgeComm to flush an output buffer, the deleted characters are not transmitted. To empty the output buffer while ensuring that the contents are transmitted, a thread can call the FlushFileBuffers function (a synchronous operation). Note, however, that FlushFileBuffers is subject to flow control but not to write time-outs, and it will not return until all pending write operations have been transmitted.

+$#A Overlapped Operations

Overlapped operations enable a thread to execute a time-consuming I/O operation in the background, leaving the thread free to perform other tasks. To enable overlapped I/O operations on a communications resource, the thread must specify the FILE_FLAG_OVERLAPPED flag in the CreateFile function when the handle is opened. To execute the ReadFile or WriteFile function as an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED structure.The OVERLAPPED structure must contain a handle to a manual-reset (not an auto-reset) event object. The system sets the state of the event object to not-signaled when a call to the I/O function returns before the operation has been completed. The system sets the state of the event object to signaled when the operation has been completed. The thread uses a wait function to check the current state of the event object or to wait for its state to be signaled.

The ReadFileEx and WriteFileEx functions can be performed only as overlapped operations. The calling thread specifies a pointer to the FileIOCompletionRoutine function, which is executed when the overlapped operation is completed. The completion routine is executed only if the calling thread performs an alertable operation.

For more information about event objects, wait functions, alertable waits, and completion routines, see Synchronization.

+$#A Time-Outs

A handle to a communications resource has an associated set of time-out parameters that affect the behavior of read and write operations. Time-outs can cause a ReadFile, ReadFileEx, WriteFile, or WriteFileEx operation to conclude when a time-out interval elapses, even though the specified number of characters have not been read or written. It is not treated as an error when a time-out occurs during a read or write operation (that is, the read or write function's return value indicates success). The count of bytes actually read or written is reported by ReadFile or WriteFile (or by the GetOverlappedResult or FileIOCompletionRoutine function, if the I/O was performed as an overlapped operation).

When an application opens a communications resource, the operating system sets the resource's time-out values to the values in effect when the resource was last used. If the communications resource has never been opened, the operating system sets the time-out values to some default value. In either case, an application should always determine the current time-out values after opening the resource, and then explicitly set them to meet its requirements. To determine the current time-out values of a communications resource, use the GetCommTimeouts function. To change the time-out values, use the SetCommTimeouts function.

Two types of time-outs are enabled by the time-out parameters. An interval time-out occurs when the time between the receipt of any two characters exceeds a specified number of milliseconds. Timing starts when the first character is received and is restarted when each new character is received. A total time-out occurs when the total amount of time consumed by a read operation exceeds a calculated number of milliseconds. Timing starts immediately when the I/O operation begins. Write operations support only total time-outs. Read operations support both interval and total time-outs, which can be used separately or combined.

The time, in milliseconds, of the total time-out period for a read or write operation is calculated by using the multiplier and constant values from the COMMTIMEOUTS structure specified in the GetCommTimeouts or SetCommTimeouts function. The following formula is used:

Timeout = (MULTIPLIER * number_of_bytes) + CONSTANT

Using both a multiplier and a constant enables the total time-out period to vary, depending on the amount of data being requested. An application can use only the constant by setting the multiplier to zero, or use only the multiplier by setting the constant to zero. If both the constant and multiplier are zero, total time-out is not used.

If all read time-out parameters are zero, read time-outs are not used, and a read operation is not complete until the requested number of bytes have been read or an error occurs. Similarly, if all write time-out parameters are zero, a write operation is not completed until the requested number of bytes have been written or an error occurs.

If the read interval time-out parameter is the MAXDWORD value and both read total time-out parameters are zero, a read operation is completed immediately after reading whatever characters are available in the input buffer, even if it is empty. This is the same as the ReadComm function in previous versions of Windows.

Interval timing forces a read operation to return when there is a lull in reception. A process using interval time-outs can set a fairly short interval parameter, so it can respond quickly to small, isolated bursts of one or a few characters, yet it can still collect large buffers of characters with a single call when data is received in a steady stream.

Time-outs for a write operation can be useful when transmission is blocked by some kind of flow control or when the SetCommBreak function has been called to suspend character transmission.

The following table summarizes the behavior of read operations based on the values specified for total and interval time-outs.

Total | Interval | Behavior
0 | 0 | Returns when the buffer is completely filled. Time-outs are not used.
T | 0 | Returns when the buffer is completely filled or when T milliseconds have elapsed since the beginning of the operation.
0 | Y | Returns when the buffer is completely filled or when Y milliseconds have elapsed between the receipt of any two characters. Timing does not begin until the first character is received.
T | Y | Returns when the buffer is completely filled or when either type of time-out occurs.

Note, however, that timing is relative to the system controlling the physical device. For a remote device such as a modem, the timing is relative to the server system to which the modem is attached. Any network propagation delay is not factored in. For example, a client application might specify a total time-out that computes to be 500 milliseconds. When 500 milliseconds have elapsed at the server, a time-out error is returned to the client. If there is a 50 milliseconds network propagation delay, the client will not be notified of the time-out until approximately 50 milliseconds after the time-out actually occurred.

The time-out parameters affect the behavior of overlapped read and write operations on a communications device. With overlapped I/O, the ReadFile, WriteFile, ReadFileEx, or WriteFileEx function can return before the operation has been completed. The time-out parameters can determine when the operation has been completed.

+$#A Communications Errors

There are other circumstances where a read or write operation can be completed with fewer than the requested number of characters, even though a time-out has not occurred. Following are some examples:

• Some drivers support the use of special characters, which complete a read operation immediately with only the characters that have been read up to the point when they are received.

• The PurgeComm function can be called to prematurely terminate pending read or write operations. This function can also delete the contents of the output or input buffers, or both.

• If a communications error occurs during a read or write operation, all I/O operations on the communications resource are terminated. Break conditions, parity errors, or framing errors are examples of such errors. When an error occurs, the process must call the ClearCommError function to clear the error flag before it can begin additional I/O operations. ClearCommError reports the specific error that occurred and the current status of the device.

+$#A Communications Events

A process can monitor a set of events that occur in a communications resource. For example, an application can use event monitoring to determine when the CTS (clear-to-send) and DSR (data-set-ready) signals change state.

A process can monitor events on a given communications resource by using the SetCommMask function to create an event mask. To determine the current event mask for a communications resource, a process can use the GetCommMask function. The following values specify events that can be monitored.

Value | Meaning
EV_BREAK | A break was detected on input.
EV_CTS | The CTS (clear-to-send) signal changed state.
EV_DSR | The DSR (data-set-ready) signal changed state.
EV_ERR | A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING | A ring indicator was detected.
EV_RLSD | The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR | A character was received and placed in the input buffer.
EV_RXFLAG | The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY | The last character in the output buffer was sent.

After a set of events is specified, a process uses the WaitCommEvent function to wait for one of the events to occur. WaitCommEvent can be used synchronously or as an overlapped operation. For additional information about executing a function as an overlapped operation, see Synchronization.

When one of the events specified in the event mask occurs, the process completes the wait operation and sets an event mask variable to indicate the type of event detected. If the SetCommMask is called for a communications resource while a wait is pending for that resource, WaitCommEvent returns an error.

The WaitCommEvent function detects events that have occurred since the last call to SetCommMask or WaitCommEvent. For example, if you specify the EV_RXCHAR event as a wait-satisfying event, a call to WaitCommEvent will be satisfied if there are characters in the driver's input buffer that have arrived since the last call to WaitCommEvent or SetCommMask. Thus, given the following pseudo-code,

while (we_care) {

WaitCommEvent

T1: // Read bytes

// process them

T2: }

... any characters received between T1 and T2 will satisfy the next call to WaitCommEvent.

When monitoring an event that occurs when a signal (CTS, DSR, and so on) changes state, WaitCommEvent reports the change, but not the current state. To query the current state of the CTS (clear-to-send), DSR (data-set-ready), RLSD (receive-line-signal-detect), and ring indicator signals, a process can use the GetCommModemStatus function.

+$#A Extended Functions

Some communications functions can be called for a device by using the EscapeCommFunction function. This function sends a code to direct the device to execute an extended function. For example, an application can suspend character transmission with the SETBREAK code and resume transmission with the CLRBREAK code. These particular operations can also be started by calling the SetCommBreak and ClearCommBreak functions. EscapeCommFunction can also be used to implement manual modem control. For example, the CLRDTR and SETDTR codes can be used to implement manual DTR (data-terminal-ready) flow control. Note, however, that an error occurs if a process uses EscapeCommFunction to manipulate the DTR line when the device has been configured to enable DTR handshaking, or the RTS (request-to-send) line if RTS handshaking is enabled.

The DeviceIoControl function enables a process to send an extended function code directly to a specified device driver, causing the device to perform a given operation. DeviceIoControl gives a device associated with a communications resource capabilities not supported by the standard serial communications functions. It enables an application to configure a device using parameters unique to that device as well as to call any device-specific functions.

+$#A Using the Communications Functions

• Configuring a communications resource

• Monitoring communications events

+$#A Configuring a Communications Resource

The following example opens a handle to COM1 and fills in a DCB structure with the current configuration. The DCB structure is then modified and used to reconfigure the device.

DCB dcb;
HANDLE hCom;
DWORD dwError;
BOOL fSuccess;

hCom = CreateFile("COM1",
GENERIC_READ | GENERIC_WRITE,
0, /* comm devices must be opened w/exclusive-access */
NULL, /* no security attrs */
OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
0, /* not overlapped I/O */
NULL /* hTemplate must be NULL for comm devices */
);

if (hCom == INVALID_HANDLE_VALUE) {
dwError = GetLastError();

/* handle error */
}

/*
* Omit the call to SetupComm to use the default queue sizes.
* Get the current configuration.
*/

fSuccess = GetCommState(hCom, &dcb);

if (!fSuccess) {
/* Handle the error. *
}

/* Fill in the DCB: baud=9600, 8 data bits, no parity, 1 stop bit. */

dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;

fSuccess = SetCommState(hCom, &dcb);

if (!fSuccess) {
/* Handle the error. *
}

+$#A Monitoring Communications Events

The following example code opens the serial port for overlapped I/O, creates an event mask to monitor CTS and DSR signals, and then waits for an event to occur. The WaitCommEvent function should be executed as an overlapped operation so the other threads of the process cannot perform I/O operations during the wait.

HANDLE hCom;
OVERLAPPED o;
BOOL fSuccess;
DWORD dwEvtMask;

hCom = CreateFile("COM1",
GENERIC_READ | GENERIC_WRITE,
0, /* exclusive access */
NULL, /* no security attrs */
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);

if (hCom == INVALID_HANDLE_VALUE) {
/* Deal with the error. */
}

/* Set the event mask. */

fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR);

if (!fSuccess) {
/* deal with error */
}

/* Create an event object for use in WaitCommEvent. */

o.hEvent = CreateEvent(NULL, /* no security attributes */
FALSE, /* auto reset event */
FALSE, /* not signaled */
NULL /* no name */
);

assert(o.hEvent);

if (WaitCommEvent(hCom, &dwEvtMask, &o)) {
if (dwEvtMask & EV_DSR) {
/*
* . . .
*/
}

if (dwEvtMask & EV_CTS) {
/*
* . . .
*/
}
}

+$#A Communication Functions

Following are the functions used with communications devices.

BuildCommDCB
BuildCommDCBAndTimeouts
ClearCommBreak
ClearCommError
CommConfigDialog
DeviceIoControl
EscapeCommFunction
GetCommConfig
GetCommMask
GetCommModemStatus
GetCommProperties
GetCommState
GetCommTimeouts
PurgeComm
SetCommBreak
SetCommConfig
SetCommMask
SetCommState
SetCommTimeouts
SetupComm
TransmitCommChar
WaitCommEvent

Créé avec HelpNDoc Personal Edition: Sites web iPhone faciles


Consoles and Character-Mode Support.rtf

+$#AK Consoles and Character-Mode Support

The Microsoft® Win32® application programming interface (API) provides consoles that manage input and output (I/O) for character-mode applications (applications that do not provide their own graphical user interface).

This overview describes Win32 support for character-mode applications.

+$#A About Character-Mode Support

Consoles provide high-level support for simple character-mode applications that interact with the user by using functions that read from standard input and write to standard output or standard error. Consoles also provide sophisticated low-level support that gives direct access to a console's screen buffer and that enables applications to receive extended input information (such as mouse input).

+$#A Consoles

A console is an interface that provides I/O to character-mode applications. This processor-independent mechanism makes it easy to port existing character-mode applications or to create new character-mode tools and applications.

A console consists of an input buffer and one or more screen buffers. The input buffer contains a queue of input records, each of which contains information about an input event. The input queue always includes key-press and key-release events. It can also include mouse events (pointer movements and button presses and releases) and events during which user actions affect the size of the active screen buffer. A screen buffer is a two-dimensional array of character and color data for output in a console window.

The Win32 API functions enable different levels of access to a console. The high-level console I/O functions enable an application to read from standard input to retrieve keyboard input stored in a console's input buffer. The functions also enable an application to write to standard output or standard error to display text in the console's screen buffer. The high-level functions also support redirection of standard handles and control of console modes for different I/O functionality. The low-level console I/O functions enable applications to receive detailed input about keyboard and mouse events, as well as events involving user interactions with the console window. The low-level functions also enable greater control of output to the screen.

+$#A Creation of a Console

Microsoft Windows® creates a new console when it starts a console process, a character-mode process whose entry point is the main function. For example, Windows creates a new console when it starts the command processor. When the command processor starts a new console process, the user can specify whether the system creates a new console for the new process or whether it inherits the command processor's console. A process can create a console by using one of the following methods:

• A graphical user interface (GUI) or console process that is not currently attached to a console can use the AllocConsole function to create a new console.

• A GUI or console process can use the CreateProcess function to create a new console process and specify a flag to tell the system to create a new console.

Typically, a process uses AllocConsole to create a console when an error occurs requiring interaction with the user. For example, a GUI process can create a console when an error occurs that prevents it from using its normal graphical interface, or a console process that does not normally interact with the user can create a console when an error makes it necessary. The process that calls AllocConsole must not be attached to an existing console. GUI processes are not attached to any console when they are created. A console process is not attached to a console if the DETACHED_PROCESS flag was specified in a call to CreateProcess when the process was created. A process can use the FreeConsole function to detach itself from an inherited console or from a console created by AllocConsole.

A process can also create a console by specifying the CREATE_NEW_CONSOLE flag in a call to CreateProcess. This method creates a new console that is accessible to the child process but not to the parent process. Separate consoles enable both parent and child processes to interact with the user without conflict. If this flag is not specified when a console process is created, both processes are attached to the same console, and there is no guarantee that the correct process will receive the input intended for it. Applications can prevent confusion by creating child processes that do not inherit handles of the input buffer, or by enabling only one child process at a time to inherit an input buffer handle while preventing the parent process from reading console input until the child has finished.

Creating a new console results in a new console window, as well as separate I/O screen buffers. The process associated with the new console uses the GetStdHandle function to get the handles of the new console's input and screen buffers. These handles enable the process to access the console.

When a process uses CreateProcess, it can specify a STARTUPINFO structure, whose members control the characteristics of the first new console (if any) created for the child process. The STARTUPINFO structure specified in the call to CreateProcess affects a console created if the CREATE_NEW_CONSOLE flag is specified. It also affects a console created if the child process subsequently uses AllocConsole. The following console characteristics can be specified:

• The size, in character cells, and the location, in screen pixel coordinates, of the new console window

• The size, in character cells, of the new console's screen buffer

• The text and background color attributes of the new console's screen buffer

• The name appearing in the title bar of the new console's window

The system uses default values if the STARTUPINFO values are not specified. A child process can use the GetStartupInfo function to determine the values in its STARTUPINFO structure.

A process cannot change the location of its console window on the screen, but the following console functions are available to set or retrieve the other properties specified in the STARTUPINFO structure.

Function | Description
GetConsoleScreenBufferInfo | Retrieves the window size, screen buffer size, and color attributes.
SetConsoleWindowInfo | Changes the size of the console window.
SetConsoleScreenBufferSize | Changes the size of the screen buffer.
SetConsoleTextAttribute | Sets the color attributes.
SetConsoleTitle | Sets the console window title.
GetConsoleTitle | Retrieves the console window title.

+$#A Closing a Console

A process can use the FreeConsole function to detach itself from its console. If other processes share the console, the console is not destroyed, but the process that called FreeConsole cannot refer to it. After calling FreeConsole, the process can use AllocConsole to create a new console.

A console is closed when the last process attached to it terminates or calls FreeConsole.

+$#A Console Handles

A console process uses handles to access the input and screen buffers of its console. A process can use the GetStdHandle, CreateFile, or CreateConsoleScreenBuffer function to open one of these handles.

The GetStdHandle function provides a mechanism for retrieving the standard input (STDIN), standard output (STDOUT), and standard error (STDERR) handles associated with a process. During console creation, the system creates these handles. Initially, STDIN is a handle of the console's input buffer, and STDOUT and STDERR are handles of the console's active screen buffer. However, the SetStdHandle function can redirect the standard handles by changing the handle associated with STDIN, STDOUT, or STDERR. Because the parent's standard handles are inherited by any child process, subsequent calls to GetStdHandle return the redirected handle. A handle returned by GetStdHandle may, therefore, refer to something other than console I/O. For example, before creating a child process, a parent process can use SetStdHandle to set a pipe handle to be the STDIN handle that is inherited by the child process. When the child process calls GetStdHandle, it gets the pipe handle. This means that the parent process can control the standard handles of the child process. The handles returned by GetStdHandle have GENERIC_READ | GENERIC_WRITE access unless SetStdHandle has been used to set the standard handle to have lesser access.

The value of the handles returned by GetStdHandle will not be 0, 1, and 2, so the standard predefined stream constants in the STDIO.H header file (STDIN, STDOUT, and STDERR) cannot be used in functions that require a console handle.

The CreateFile function enables a process to get a handle of its console's input buffer and active screen buffer, even if STDIN and STDOUT have been redirected. To open a handle of a console's input buffer, specify the CONIN$ value in a call to CreateFile. Specify the CONOUT$ value in a call to CreateFile to open a handle of a console's active screen buffer. CreateFile enables you to specify the read-write access of the handle that it returns.

The CreateConsoleScreenBuffer function creates a new screen buffer and returns a handle, which can be used in any function that accepts a handle of console output. The new screen buffer is not active until its handle is specified in a call to the SetConsoleActiveScreenBuffer function. Note that changing the active screen buffer does not affect the handle returned by GetStdHandle. Similarly, using SetStdHandle to change the STDOUT handle does not affect the active screen buffer.

Console handles returned by CreateFile and CreateConsoleScreenBuffer can be used in any of the console functions that require a handle of a console's input buffer or of a console screen buffer. Handles returned by GetStdHandle can be used by the console functions if they have not been redirected to refer to something other than console I/O. If a standard handle has been redirected to refer to a file or a pipe, however, the handle can only be used by the ReadFile and WriteFile functions.

A process can use the DuplicateHandle function to create a duplicate console handle that has different access or inheritability from the original handle. Note, however, that a process can create a duplicate console handle only for its own use. This differs from other handle types (such as file, pipe, or mutex objects), for which DuplicateHandle can create a duplicate that is valid for a different process.

To close a console handle, a process can use the CloseHandle function.

+$#A Console Input Buffer

Each console has an input buffer that contains a queue of input event records. When a console's window has the keyboard focus, a console formats each input event (such as a single keystroke, a movement of the mouse, or a mouse-button click) as an input record that it places in the console's input buffer.

Applications can access a console's input buffer indirectly by using the high-level console I/O functions, or directly by using the low-level console I/O functions. The high-level input functions filter and process the data in the input buffer, returning only a stream of input characters. The low-level input functions enable applications to read input records directly from a console's input buffer, or to place input records into the input buffer.

An input record is a structure containing information about the type of event that occurred (keyboard, mouse, window resizing, focus, or menu event) as well as specific details about the event. The EventType member in an INPUT_RECORD structure indicates which type of event is contained in the record.

Focus and menu events are placed in a console's input buffer for internal use by the system and should be ignored by applications.

Keyboard Events

Keyboard events are generated when any key is pressed or released; this includes control keys. However, the ALT key has special meaning to Windows when pressed and released without being combined with another character, and it is not passed through to the application. Also, the CTRL+C key combination is not passed through if the input handle is in processed mode.

If the input event is a keystroke, the Event member in INPUT_RECORD is a KEY_EVENT_RECORD structure containing the following information:

• A Boolean value indicating whether the key was pressed or released.

• A repeat count that can be greater than one when a key is held down.

• The virtual-key code, identifying the given key in a device-independent manner.

• The virtual-scan code, indicating the device-dependent value generated by the keyboard hardware.

• The translated Unicode™ or ANSI character.

• A flag variable indicating the state of the control keys (the ALT, CTRL, SHIFT, NUM LOCK, SCROLL LOCK, and CAPS LOCK keys) and indicating whether an enhanced key was pressed. Enhanced keys for the IBM® 101-key and 102-key keyboards are the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad and the divide (/) and ENTER keys in the numeric keypad.

Mouse Events

Mouse events are generated whenever the user moves the mouse or presses or releases one of the mouse buttons. Mouse events are placed in the input buffer only if the following conditions are met:

• The console input mode is set to ENABLE_MOUSE_INPUT (the default mode).

• The console window has the keyboard focus.

• The mouse pointer is within the borders of the console's window.

If the input event is a mouse event, the Event member in INPUT_RECORD is a MOUSE_EVENT_RECORD structure containing the following information:

• The coordinates of the mouse pointer in terms of the character-cell row and column in the screen buffer's coordinate system.

• A flag variable indicating the state of the mouse buttons.

• A flag variable indicating the state of the control keys (ALT, CTRL, SHIFT, NUM LOCK, SCROLL LOCK, and CAPS LOCK) and indicating whether an enhanced key was pressed. Enhanced keys for the IBM 101-key and 102-key keyboards are the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad and the divide (/) and ENTER keys in the numeric keypad.

• A flag variable indicating whether the event was a normal button-press or button-release event, a mouse movement event, or the second click of a double-click event.

Note that the mouse position coordinates are in terms of the screen buffer, not the console window. The screen buffer may have been scrolled with respect to the window, so the upper left corner of the window is not necessarily the (0,0) coordinate of the screen buffer. To determine the coordinates of the mouse relative to the coordinate system of the window, subtract the window origin coordinates from the mouse position coordinates. Use the GetConsoleScreenBufferInfo function to determine the window origin coordinates.

The dwButtonState member of the MOUSE_EVENT_RECORD structure has a bit corresponding to each mouse button. The bit is 1 if the button is down and 0 if the button is up. A button-release event is detected by a 0 value for the dwEventFlags member of MOUSE_EVENT_RECORD and a change in a button's bit from 1 to 0. The GetNumberOfConsoleMouseButtons function retrieves the number of buttons on the mouse.

Buffer-Resizing Events

A console window's menu enables the user to change the size of the active screen buffer; this change generates a buffer-resizing event. Buffer-resizing events are placed in the input buffer if the console's input mode is set to ENABLE_WINDOW_INPUT (that is, the default mode is disabled).

If the input event is a buffer-resizing event, the Event member of INPUT_RECORD is a WINDOW_BUFFER_SIZE_RECORD structure containing the new size of the screen buffer, expressed in character-cell columns and rows.

If the user reduces the size of the screen buffer, any data in the discarded portion of the buffer is lost.

Changes to the screen buffer size as a result of application calls to the SetConsoleScreenBufferSize function are not generated as buffer-resizing events.

+$#A Console Screen Buffers

A screen buffer is a two-dimensional array of character and color data for output in a console window. A console can have multiple screen buffers. The active screen buffer is the one that is displayed on the screen.

The system creates a screen buffer whenever it creates a new console. A process can use the CreateConsoleScreenBuffer function to create additional screen buffers for its console. A new screen buffer is not active until its handle is specified in a call to the SetConsoleActiveScreenBuffer function. However, screen buffers can be accessed for reading and writing whether they are active or inactive.

Each screen buffer has its own two-dimensional array of character information records. The data for each character is stored in a CHAR_INFO structure that specifies the Unicode or ANSI character and the foreground and background colors in which that character is displayed.

A number of properties associated with a screen buffer can be set independently for each screen buffer. This means that changing the active screen buffer can have a dramatic effect on the appearance of the console window. The properties associated with a screen buffer include:

• Screen buffer size, in character rows and columns.

• Text attributes (foreground and background colors for displaying text to be written by the WriteFile or WriteConsole function).

• Window size and location (the rectangular region of the screen buffer that is displayed in the console window).

• Cursor position, appearance, and visibility.

• Output modes (ENABLE_PROCESSED_OUTPUT and ENABLE_WRAP_AT_EOL_OUTPUT). For more information about console output modes, see High-Level Console Modes.

When a screen buffer is created, it contains blanks. Its cursor is visible and positioned at the buffer's origin (0,0), and the window is positioned with its upper left corner at the buffer's origin. The size of the screen buffer, the window size, the text attributes, and the appearance of the cursor are determined by the user or by the system defaults. To retrieve the current values of the various properties associated with the screen buffer, use the GetConsoleScreenBufferInfo, GetConsoleCursorInfo, and GetConsoleMode functions.

Applications that change any of the screen buffer properties should either create their own screen buffer or save the state of the inherited screen buffer during startup and restore it at exit.

+$#A Window and Screen Buffer Size

The size of a screen buffer is expressed in terms of a coordinate grid based on character cells. The width is the number of character cells in each row, and the height is the number of rows. Associated with each screen buffer is a window that determines the size and location of the rectangular portion of the screen buffer displayed in the console window. A screen buffer's window is defined by specifying the character-cell coordinates of the upper left and lower right cells of the window's rectangle.

A screen buffer can be any size, limited only by available memory. The dimensions of a screen buffer's window cannot exceed the corresponding dimensions of either the screen buffer or the maximum window that can fit on the screen based on the current font size (controlled exclusively by the user).

The GetConsoleScreenBufferInfo function returns the following information about a screen buffer and its window:

• The current size of the screen buffer

• The current location of the window

• The maximum size of the window given the current screen buffer size, the current font size, and the screen size

The GetLargestConsoleWindowSize function returns the maximum size of a console's window based on the current font and screen sizes. This size differs from the maximum window size returned by GetConsoleScreenBufferInfo in that the screen buffer size is ignored.

To change a screen buffer's size, use the SetConsoleScreenBufferSize function. This function fails if either dimension of the specified size is less than the corresponding dimension of the console's window.

To change the size or location of a screen buffer's window, use the SetConsoleWindowInfo function. This function fails if the specified window-corner coordinates exceed the limits of the screen buffer or the screen. Changing the window size of the active screen buffer changes the size of the console window displayed on the screen.

A process can change its console's input mode to enable window input so that the process is able to receive input when the user changes the screen buffer size. If an application enables window input, it can use GetConsoleScreenBufferInfo to retrieve window and screen buffer size at startup. This information can then be used to determine the way data is displayed in the window. If the user changes the screen buffer size, the application can respond by changing the way data is displayed. For example, an application can adjust the way text wraps at the end of the line if the number of characters per row changes. If an application does not enable window input, it must either use the inherited window and screen buffer sizes, or set them to the desired size during startup and restore the inherited sizes at exit. For additional information about window input mode, see Low-Level Console Modes.

+$#A Scrolling the Screen Buffer

The console window displays a portion of the active screen buffer. Each screen buffer maintains its own current window rectangle that specifies the coordinates of the upper left and lower right character cells to be displayed in the console window. To determine the current window rectangle of a screen buffer, use GetConsoleScreenBufferInfo. When a screen buffer is created, the upper left corner of its window is at the upper left corner of the screen buffer at (0,0).

The window rectangle can change to display different parts of the screen buffer. The window rectangle of a screen buffer can change in the following situations:

• When SetConsoleWindowInfo is called to specify a new window rectangle, it scrolls the view of the screen buffer by changing the position of the window rectangle without changing the size of the window. For examples of scrolling the window's contents, see Scrolling a Screen Buffer's Window.

{ewc msdncd, EWGraphic, group10738 0 /a "SDK_01.bmp"}

• When using the WriteFile function to write to a screen buffer with wrap at end-of-line (EOL) output mode enabled, the window rectangle shifts automatically, so the cursor is always displayed.

• When the SetConsoleCursorPosition function specifies a new cursor position that is outside the boundaries of the current window rectangle, the window rectangle shifts automatically to display the cursor.

• When the user changes the size of the console window or uses the window's scroll bars, the window rectangle of the active screen buffer can change. This change is not reported as a window resizing event in the input buffer.

In each of these situations, the window rectangle shifts to display a different part of the screen buffer, but the contents of the screen buffer remain in the same position. The following situations can cause the screen buffer's contents to shift:

• When the ScrollConsoleScreenBuffer function is called, a rectangular block is copied from one part of a screen buffer to another.

• When using WriteFile to write to a screen buffer with wrap at EOL output mode enabled, the screen buffer's contents scroll automatically when the end of the screen buffer is encountered. This scrolling discards the top row of the screen buffer.

ScrollConsoleScreenBuffer specifies the screen buffer rectangle that is moved and the new upper left coordinates to which the rectangle is copied. This function can scroll a portion or the entire contents of the screen buffer.

The illustration shows a ScrollConsoleScreenBuffer operation that scrolls the entire contents of the screen buffer up by several rows. The contents of the top rows are discarded, and the bottom rows are filled with a specified character and color.

{ewc msdncd, EWGraphic, group10738 1 /a "SDK_02.bmp"}

The effects of ScrollConsoleScreenBuffer can be limited by specifying an optional clipping rectangle so that the contents of the screen buffer outside the clipping rectangle are unchanged. The effect of clipping is to create a subwindow (the clipping rectangle) whose contents are scrolled without affecting the rest of the screen buffer. For an example that uses ScrollConsoleScreenBuffer, see Scrolling a Screen Buffer's Contents.

Cursor Appearance and Position

A screen buffer's cursor can be visible or hidden. When it is visible, its appearance can vary, ranging from from completely filling a character cell to appearing as a horizontal line at the bottom of the cell. To retrieve information about the appearance and visibility of the cursor, use the GetConsoleCursorInfo function. This function reports whether the cursor is visible and describes the appearance of the cursor as the percentage of a character cell that it fills. To set the appearance and visibility of the cursor, use the SetConsoleCursorInfo function.

Characters written by the high-level console I/O functions are written at the current cursor location, advancing the cursor to the next location. To determine the current cursor position in the coordinate system of a screen buffer, use GetConsoleScreenBufferInfo. You can use SetConsoleCursorPosition to set the cursor position and, thereby, control the placement of text that is written or echoed by the high-level I/O functions. If you move the cursor, text at the new cursor location is overwritten.

The position, appearance, and visibility of the cursor are set independently for each screen buffer.

Screen Buffer Color Attributes

Each screen buffer character cell stores the color attributes for the colors used in drawing the text and background of that cell. An application can set the color data for each character cell individually, storing the data in the Attributes member of the CHAR_INFO structure for each cell.

The current text attributes of each screen buffer determine the foreground (text) and background colors of characters subsequently written or echoed by the high-level functions. An application can use GetConsoleScreenBufferInfo to determine the current text attributes of a screen buffer and the SetConsoleTextAttribute function to set the text attributes. Changing a screen buffer's text attributes does not affect the display of characters previously written. These text attributes do not affect characters written by the low-level console I/O functions (such as the WriteConsoleOutput or WriteConsoleOutputCharacter function) which either explicitly specify the attributes for each cell that is written or leave the attributes unchanged at the written positions.

The following attribute constants are defined in the WINCON.H header file:

• FOREGROUND_BLUE

• FOREGROUND_GREEN

• FOREGROUND_RED

• FOREGROUND_INTENSITY

• BACKGROUND_BLUE

• BACKGROUND_GREEN

• BACKGROUND_RED

• BACKGROUND_INTENSITY

The foreground attributes specify the text color, and the background attributes specify the color used to fill the cell's background. An application can combine the constants to achieve different colors. For example, the following combination results in bright cyan text on a blue background.

FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_BLUE

If no background constant is specified, the background is black, and if no foreground constant is specified, the text is black. For example, the following combination produces black text on a white background.

BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED

+$#A Input and Output Methods

The Win32 API provides two very different approaches to console I/O, the choice of which depends on how much flexibility and control an application needs. The high-level approach enables simple character stream I/O, but it limits access to a console's input and screen buffers. The low-level approach requires that developers write more code and choose among a greater range of functions, but it also gives an application more flexibility.

An application can use the file I/O functions, ReadFile and WriteFile, and the console functions, ReadConsole and WriteConsole, for high-level I/O that provides indirect access to a console's input and screen buffers. The high-level input functions filter and process the data in a console's input buffer to return input as a stream of characters, discarding mouse and buffer-resizing input. Similarly, the high-level output functions write a stream of characters that are displayed at the current cursor location in a screen buffer. An application controls the way these functions work by setting a console's I/O modes.

The low-level I/O functions provide direct access to a console's input and screen buffers, enabling an application to access mouse and buffer-resizing input events and extended information for keyboard events. Low-level output functions enable an application to read from or write to a specified number of consecutive character cells in a screen buffer, or to read or write to rectangular blocks of character cells at a specified location in a screen buffer. A console's input modes affect low-level input by enabling the application to determine whether mouse and buffer-resizing events are placed in the input buffer. A console's output modes have no effect on low-level output.

The high-level and low-level I/O methods are not mutually exclusive, and an application can use any combination of these functions. Typically, however, an application uses one approach or the other exclusively.

The sections that follow describe in detail the console modes and the high-level and low-level I/O functions.

+$#A Console Modes

Associated with each console input buffer are a set of input modes that affects input operations. Similarly, each console screen buffer has a set of output modes that affects output operations. The input modes can be divided into two groups: those that affect the high-level input functions and those that affect the low-level input functions. The output modes only affect applications that use the high-level output functions.

The GetConsoleMode function reports the current input mode of a console's input buffer or the current output mode of a screen buffer. The SetConsoleMode function sets the current mode of either a console input buffer or a screen buffer. If a console has multiple screen buffers, the output modes of each can be different. An application can change I/O modes at any time. For more information about the console modes that affect high-level and low-level I/O operations, see High-Level Console Modes and Low-Level Console Modes.

+$#A High-Level Console I/O

The high-level I/O functions provide a simple way to read a stream of characters from console input or to write a stream of characters to console output. A high-level read operation gets input characters from a console's input buffer and stores them in a specified buffer. A high-level write operation takes characters from a specified buffer and writes them to a screen buffer at the current cursor location, advancing the cursor as each character is written.

High-level I/O gives you a choice between the ReadFile and WriteFile functions and the ReadConsole and WriteConsole functions. They are identical, except for two important differences. The console functions support the use of either Unicode characters or the ANSI character set; the file I/O functions do not support Unicode. Also, the file I/O functions can be used to access files, pipes, and serial communications devices; the console functions can only be used with console handles. This distinction is important if an application relies on standard handles that may have been redirected.

When using either set of high-level functions, an application can control the text and background colors used to display characters subsequently written to a screen buffer. An application can also use the console modes that affect high-level console I/O to enable or disable the following properties:

• Echoing of keyboard input to the active screen buffer

• Line input, in which a read operation does not return until the ENTER key is pressed

• Automatic processing of keyboard input to handle carriage returns, CTRL+C, and other input details

• Automatic processing of output to handle line wrapping, carriage returns, backspaces, and other output details

+$#A High-Level Console Modes

The behavior of the high-level console functions is affected by the console input and output modes. All of the following console input modes are enabled for a console's input buffer when a console is created:

• Line input mode

• Processed input mode

• Echo input mode

Both of the following console output modes are enabled for a console screen buffer when it is created:

• Processed output mode

• Wrapping at EOL output mode

All three input modes, along with processed output mode, are designed to work together. It is best to either enable or disable all of these modes as a group. When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application. When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.

An application can use the GetConsoleMode function to determine the current mode of a console's input buffer or screen buffer. You can enable or disable any of these modes by using the following values in the SetConsoleMode function. Note that setting the output mode of one screen buffer does not affect the output mode of other screen buffers.

Mode | Description
ENABLE_PROCESSED_INPUT |
 | Used with a console input handle to cause the system to process any system editing or control key input rather than returning it as input in the read operation's buffer. If line input is also enabled, backspaces and carriage returns are handled correctly. A backspace causes the cursor to move back one space without affecting the character at the cursor position. A carriage return is converted to carriage return − linefeed character combination. If echo input mode is enabled and the output should reflect system editing, processed output must be enabled for the active screen buffer. If processed input is enabled, the CTRL+C key combination is passed on to the appropriate handler regardless of whether line input is enabled. For more information about control handlers, see Console Control Handlers.
ENABLE_LINE_INPUT |
 | Used with a console input handle to cause the ReadFile and ReadConsole functions to return when the ENTER key is pressed. If line input mode is disabled, the functions return when one or more characters are available in the input buffer.
ENABLE_ECHO_INPUT |
 | Used with a console input handle to cause keyboard input read by the ReadFile or ReadConsole function to be echoed to the active screen buffer. Characters are echoed only if the process that calls ReadFile or ReadConsole has an open handle of the active screen buffer. Echo mode cannot be enabled unless line input is also enabled. The output mode of the active screen buffer affects the way echoed input is displayed.
ENABLE_PROCESSED_OUTPUT |
 | Used with a console screen buffer handle to cause the system to perform the appropriate action for ANSI control characters that are written to a screen buffer. The backspace, tab, bell, carriage return, and linefeed characters are processed. A tab character moves the cursor to the next tab stop, which occurs every eight characters. A bell character sounds a short tone.
ENABLE_WRAP_AT_EOL_OUTPUT |
 | Used with a console screen buffer handle to cause the current output position (cursor position) to move to the first column in the next row (line) when the end of the current row is reached. If the bottom of the window region is reached, the window origin is moved down one row. This movement has the effect of scrolling the contents of the window up one row. If the bottom of the screen buffer is reached, the contents of the screen buffer are scrolled up one row, and the top row of the screen buffer is discarded. If this mode is disabled, the last character in the row is overwritten with any subsequent characters.

+$#A High-Level Console Input and Output Functions

The ReadFile and WriteFile functions, or the ReadConsole and WriteConsole functions, enable an application to read console input and write console output as a stream of characters. ReadConsole and WriteConsole behave exactly like ReadFile and WriteFile except that they can be used either as wide-character functions (in which text arguments must use Unicode) or as ANSI functions (in which text arguments must use characters from the Windows 3.x character set). Applications that need to maintain a single set of sources to support either Unicode or the ANSI character set should use ReadConsole and WriteConsole.

ReadConsole and WriteConsole can only be used with console handles; ReadFile and WriteFile can be used with other handles (such as files or pipes). ReadConsole and WriteConsole fail if used with a standard handle that has been redirected and is no longer a console handle.

To get keyboard input, a process can use ReadFile or ReadConsole with a handle of the console's input buffer, or it can use ReadFile to read input from a file or a pipe if STDIN has been redirected. These functions only return keyboard events that can be translated into ANSI characters (or Unicode characters in the case of ReadConsole). The input that can be returned includes control key combinations. The functions do not return keyboard events involving the function keys or arrow keys. Input events generated by mouse, window, focus, or menu input are discarded.

If line input mode is enabled (the default mode), ReadFile and ReadConsole do not return to the calling application until the ENTER key is pressed. If line input mode is disabled, the functions do not return until at least one character is available. In either mode, all available characters are read until either no more keys are available or the specified number of characters has been read. Unread characters are buffered until the next read operation. The functions report the total number of characters actually read. If echo input mode is enabled, characters read by these functions are written to the active screen buffer at the current cursor position.

A process can use WriteFile or WriteConsole to write to either an active or inactive screen buffer, or it can use WriteFile to write to a file or a pipe if STDOUT has been redirected. Processed output mode and wrap at EOL output mode control the way characters are written or echoed to a screen buffer.

Characters written by WriteFile or WriteConsole, or echoed by ReadFile or ReadConsole, are inserted in a screen buffer at the current cursor position. As each character is written, the cursor position advances to the next character cell; however, the behavior at the end of a row depends on the screen buffer's wrap at EOL output mode. An application can use the GetConsoleScreenBufferInfo function to determine the current cursor position and the SetConsoleCursorPosition function to set the cursor position.

For an example that uses the high-level console I/O functions, see Using the High-Level Input and Output Functions.

+$#A Low-Level Console I/O

The low-level console I/O functions expand an application's control over console I/O by enabling direct access to a console's input and screen buffers. These functions enable an application to perform the following tasks:

• Receive input about mouse and buffer-resizing events

• Receive extended information about keyboard input events

• Write input records to the input buffer

• Read input records without removing them from the input buffer

• Determine the number of pending events in the input buffer

• Flush the input buffer

• Read and write strings of Unicode or ANSI characters at a specified location in a screen buffer

• Read and write strings of text and background color attributes at a specified screen buffer location

• Read and write rectangular blocks of character and color data at a specified screen buffer location

• Write a single Unicode or ANSI character, or a text and background color attribute combination, to a specified number of consecutive cells beginning at a specified screen buffer location

+$#A Low-Level Console Modes

The types of input events reported in a console's input buffer depend on the console's mouse and window input modes. The console's processed input mode determines how the system handles the CTRL+C key combination. To set or retrieve the state of a console's input modes, an application can specify a console input buffer handle in a call to the SetConsoleMode or GetConsoleMode function. The following modes are used with console input handles.

Mode | Description
ENABLE_MOUSE_INPUT |
 | Controls whether mouse events are reported in the input buffer. By default, mouse input is enabled and window input is disabled. Changing either of these modes affects only input that occurs after the mode is set; pending mouse or window events in the input buffer are not flushed. The mouse pointer is displayed regardless of the mouse mode.
ENABLE_WINDOW_INPUT |
 | Controls whether buffer-resizing events are reported in the input buffer. By default, mouse input is enabled and window input is disabled. Changing either of these modes affects only input that occurs after the mode is set; pending mouse or window events in the input buffer are not flushed. The mouse pointer is displayed regardless of the mouse mode.
ENABLE_PROCESSED_INPUT |
 | Controls the processing of input for applications using the high-level console I/O functions. However, if processed input mode is enabled, the CTRL+C key combination is not reported in the console's input buffer. Instead, it is passed on to the appropriate control handler function. For more information about control handlers, see Console Control Handlers.

The output modes of a screen buffer do not affect the behavior of the low-level output functions.

+$#A Low-Level Console Input Functions

A console's input buffer contains input records that can include information about keyboard, mouse, buffer-resizing, focus, and menu events. The low-level functions provide direct access to the input buffer, unlike the high-level functions that filter and process the input buffer's data, discarding all but keyboard input.

The Win32 API provides five low-level functions for accessing a console's input buffer:

• ReadConsoleInput

• PeekConsoleInput

• GetNumberOfConsoleInputEvents

• WriteConsoleInput

• FlushConsoleInputBuffer

The ReadConsoleInput, PeekConsoleInput, and WriteConsoleInput functions use the INPUT_RECORD structure to read from or write to an input buffer.

Following are descriptions of the low-level console input functions.

Function | Description
ReadConsoleInput | Reads and removes input records from an input buffer. The function does not return until at least one record is available to be read. Then all available records are transferred to the buffer of the calling process until either no more records are available or the specified number of records has been read. Unread records remain in the input buffer for the next read operation. The function reports the total number of records that have been read. For an example that uses ReadConsoleInput, see Reading Input Buffer Events.
PeekConsoleInput | Reads without removing the pending input records in an input buffer. All available records up to the specified number are copied into the buffer of the calling process. If no records are available, the function returns immediately. The function reports the total number of records that have been read.
GetNumberOfConsoleInputEvents | Determines the number of unread input records in an input buffer.
WriteConsoleInput | Places input records into the input buffer behind any pending records in the buffer. The input buffer grows dynamically, if necessary, to hold as many records as are written. To use this function, the specified input buffer handle must have GENERIC_WRITE access.
FlushConsoleInputBuffer | Discards all unread events in the input buffer. To use this function, the specified input buffer handle must have GENERIC_WRITE access.

A thread of an application's process can perform a wait operation to wait for input to be available in an input buffer. To initiate a wait operation, specify a handle of the input buffer in a call to any of the wait functions (WaitForSingleObject, WaitForMultipleObjects, WaitForSingleObjectEx, or WaitForMultipleObjectsEx). These functions can return when the state of one or more objects is signaled. The state of a console input handle becomes signaled when there are unread records in its input buffer. The state is reset to nonsignaled when the input buffer becomes empty. If there is no input available, the calling thread enters an efficient wait state, consuming very little processor time while waiting for the conditions of the wait operation to be satisfied.

+$#A Low-Level Console Output Functions

The low-level console output functions provide direct access to the character cells of a screen buffer. One set of functions reads from or writes to consecutive cells beginning at any location in the screen buffer. Another set of functions reads from or writes to rectangular blocks of cells.

The following functions read from or write to a specified number of consecutive character cells in a screen buffer, beginning with a specified cell.

Function | Description
ReadConsoleOutputCharacter |
 | Copies a string of Unicode or ANSI characters from a screen buffer.
WriteConsoleOutputCharacter |
 | Writes a string of Unicode or ANSI characters to a screen buffer.
ReadConsoleOutputAttribute |
 | Copies a string of text and background color attributes from a screen buffer.
WriteConsoleOutputAttribute |
 | Writes a string of text and background color attributes to a screen buffer.
FillConsoleOutputCharacter |
 | Writes a single Unicode or ANSI character to a specified number of consecutive cells in a screen buffer.
FillConsoleOutputAttribute |
 | Writes a text and background color attribute combination to a specified number of consecutive cells in a screen buffer.

For all of these functions, when the last cell of a row is encountered, reading or writing wraps around to the first cell of the next row. When the end of the last row of the screen buffer is encountered, the write functions discard all unwritten characters or attributes, and the read functions report the number of characters or attributes actually written..

The following functions read from or write to rectangular blocks of character cells at a specified location in a screen buffer.

Function | Description
ReadConsoleOutput |
 | Copies character and color data from a specified block of screen buffer cells into a given block in a destination buffer.
WriteConsoleOutput |
 | Writes character and color data to a specified block of screen buffer cells from a given block in a source buffer.

These functions treat screen buffers and source or destination buffers as two-dimensional arrays of CHAR_INFO structures (containing character and color attribute data for each cell). The functions specify the width and height, in character cells, of the source or destination buffer, and the pointer to the buffer is treated as a pointer to the origin cell (0,0) of the two-dimensional array. The functions use a SMALL_RECT structure to specify which rectangle to access in the screen buffer, and the coordinates of the upper left cell in the source or destination buffer determine the location of the corresponding rectangle in that buffer.

These functions automatically clip the specified screen buffer rectangle to fit within the boundaries of the screen buffer. For example, if the rectangle specifies lower right coordinates that are (column 100, row 50) and the screen buffer is only 80 columns wide, the coordinates are clipped so that they are (column 79, row 50). Similarly, this adjusted rectangle is again clipped to fit within the boundaries of the source or destination buffer. The screen buffer coordinates of the actual rectangle that was read from or written to are specified. For an example that uses these functions, see Reading and Writing Blocks of Characters and Attributes.

The illustration shows a ReadConsoleOutput operation where clipping occurs when the block is read from the screen buffer, and again when the block is copied into the destination buffer. The function reports the actual screen buffer rectangle that it copied from.

{ewc msdncd, EWGraphic, group10738 2 /a "SDK_03.bmp"}

+$#A Console Code Pages

A code page is a mapping of 256 character codes to individual characters. Different code pages include different special characters, typically customized for a language or a group of languages.

Associated with each console are two code pages: one for input and one for output. A console uses its input code page to translate keyboard input into the corresponding character value. It uses its output code page to translate the character values written by the various output functions into the images displayed in the console window. An application can use the SetConsoleCP and GetConsoleCP functions to set and retrieve a console's input code pages and the SetConsoleOutputCP and GetConsoleOutputCP functions to set and retrieve its output code pages.

The identifiers of the code pages available on the local computer are stored in the registry under the following key.

%HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage%

For information about using the registry functions to determine the available code pages, see Registry.

For more information about code pages, see String Manipulation.

+$#A Console Control Handlers

Each console process has its own list of control handler functions that are called by the system when the process receives a CTRL+C, CTRL+BREAK, or CTRL+CLOSE signal. Initially, the list of control handlers for each process contains only a default handler function that calls the ExitProcess function. A console process can add or remove additional handler functions by calling the SetConsoleCtrlHandler function. This function does not affect the lists of control handlers for other processes. When a console process receives any of the control signals, it calls the handler functions on a last-registered, first-called basis until one of the handlers returns TRUE. If none of the handlers returns TRUE, the default handler is called.

The following typedef declaration illustrates the format of a control handler function.

typedef BOOL (*PHANDLER_ROUTINE)(DWORD dwCtrlType);

The function's dwCtrlType parameter identifies which control signal was received, and the return value indicates whether the signal was handled.

For an example of a control handler function, see Registering a Control Handler Function.

+$#A CTRL+C and CTRL+BREAK Signals

The CTRL+C and CTRL+BREAK key combinations receive special handling by console processes. By default, when a console window has the keyboard focus, CTRL+C or CTRL+BREAK is treated as a signal (SIGINT or SIGBREAK) and not as keyboard input. By default, these signals are passed to all console processes that are attached to the console, causing the system to call the control handler function or functions associated with these processes. Detached processes (GUI processes or console processes started with the DETACHED_PROCESS or CREATE_NEW_CONSOLE flag) are not affected.

CTRL+BREAK is always treated as a signal, but an application can change the default CTRL+C behavior in two ways that prevent the handler functions from being called:

• The SetConsoleMode function can disable the ENABLE_PROCESSED_INPUT input mode for a console's input buffer, so CTRL+C is reported as keyboard input rather than as a signal.

• When SetConsoleCtrlHandler is called with NULL and TRUE values for its parameters, the calling process ignores CTRL+C signals. Normal CTRL+C processing is restored by calling SetConsoleCtrlHandler with NULL and FALSE values. This attribute of ignoring or not ignoring CTRL+C signals is inherited by child processes, but it can be enabled or disabled by any process without affecting existing processes.

+$#A CTRL+CLOSE Signal

The system generates a CTRL+CLOSE signal when the user closes a console. All processes attached to the console receive the signal, giving each process an opportunity to clean up before termination. When a process receives this signal, the handler function can take one of the following actions after performing any cleanup operations:

• Call ExitProcess to terminate the process.

• Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.

• Return TRUE. In this case, no other handler functions are called, and a pop-up dialog box asks the user whether to terminate the process. If the user chooses not to terminate the process, the system does not close the console until the process finally terminates.

+$#A Console Process Groups

When a process uses the CreateProcess function to create a new console process, it can specify the CREATE_NEW_PROCESS_GROUP flag to make the new process the root process of a console process group. The process group includes all processes that are descendants of the root process.

A process can use the GenerateConsoleCtrlEvent function to send a CTRL+C or CTRL+BREAK signal to all processes in a console process group. The signal is only received by those processes in the group that are attached to the same console as the process that called GenerateConsoleCtrlEvent.

+$#A Using the Console

• Using the high-level input and output functions

• Writing characters or colors to consecutive cells

• Reading and writing blocks of characters and attributes

• Reading input buffer events

• Scrolling a screen buffer's window

• Scrolling a screen buffer's contents

• Registering a control handler function

• Console application issues

+$#A Using the High-Level Input and Output Functions

The following example uses the high-level console I/O functions for console I/O. For more information about the high-level console I/O functions, see High-Level Console I/O.

The example assumes that the default I/O modes are in effect initially for the first calls to the ReadFile and WriteFile functions. Then the input mode is changed to turn off line input mode and echo input mode for the second calls to ReadFile and WriteFile. The SetConsoleTextAttribute function is used to set the colors in which subsequently written text will be displayed. Before exiting, the program restores the original console input mode and color attributes.

The example's NewLine function is used when line input mode is disabled. It handles carriage returns by moving the cursor position to the first cell of the next row. If the cursor is already in the last row of the screen buffer, the contents of the screen buffer are scrolled up one line. For an example that illustrates the use of the ScrollConsoleScreenBuffer function to scroll a screen buffer, see Scrolling a Screen Buffer's Contents.

#include <windows.h>

VOID NewLine(VOID);

VOID ScrollScreenBuffer(HANDLE, INT);

HANDLE hStdout, hStdin;

CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

VOID main(void) {

LPSTR lpszPrompt1 = "Type something and press Enter:\n";

LPSTR lpszPrompt2 = "Type any key: ";

CHAR chBuffer[256];

DWORD cRead, cWritten, fdwMode, fdwOldMode;

WORD wOldColorAttrs;

/* Get handles to STDIN and STDOUT. */

hStdin = GetStdHandle(STD_INPUT_HANDLE);

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (hStdin == INVALID_HANDLE_VALUE ||

hStdout == INVALID_HANDLE_VALUE)

MyErrorExit("GetStdHandle");

/* Save the current text colors. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))

MyErrorExit("GetConsoleScreenBufferInfo");

wOldColorAttrs = csbiInfo.wAttributes;

/* Set the text attr. to draw red text on black background. */

if (! SetConsoleTextAttribute(hStdout, FOREGROUND_RED))

MyErrorExit("SetConsoleTextAttribute");

/*

* Write to STDOUT and read from STDIN by using the default

* modes. Input is echoed automatically, and ReadFile

* does not return until a carriage return is typed.

*

* The default input modes are line, processed, and echo.

* The default output modes are processed and wrap at EOL.

*/

while (1) {

if (! WriteFile(

hStdout, /* output handle */

lpszPrompt1, /* prompt string */

lstrlen(lpszPrompt1), /* string length */

&cWritten, /* bytes written */

NULL) ) /* not overlapped */

break;

if (! ReadFile(

hStdin, /* input handle */

chBuffer, /* buffer to read into */

255, /* size of buffer */

&cRead, /* actual bytes read */

NULL) ) /* not overlapped */

break;

if (chBuffer[0] == 'q') break;

}

/* Turn off the line input mode, and echo the input mode. */

if (! GetConsoleMode(hStdin, &fdwOldMode))

MyErrorExit("GetConsoleMode");

fdwMode = fdwOldMode &

~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);

if (! SetConsoleMode(hStdin, fdwMode))

MyErrorExit("SetConsoleMode");

/* Prompt for input. */

if (! WriteFile(

hStdout, /* output handle */

lpszPrompt2, /* prompt string */

lstrlen(lpszPrompt2), /* string length */

&cWritten, /* bytes written */

NULL) ) /* not overlapped */

MyErrorExit("WriteFile");

/*

* Without line and echo input modes, ReadFile returns

* when any input is available. Carriage returns must

* be handled, and WriteFile is used to echo input.

*/

while (1) {

if (! ReadFile(hStdin, chBuffer, 1, &cRead, NULL))

break;

if (chBuffer[0] == '\r')

NewLine();

else

if (! WriteFile(hStdout, chBuffer, cRead,

&cWritten, NULL))

break;

if (chBuffer[0] == 'q') break;

}

/* Restore the original console mode. */

if (! SetConsoleMode(hStdin, fdwOldMode))

MyErrorExit("SetConsoleMode");

/* Restore the original text colors. */

if (! SetConsoleTextAttribute(hStdout, wOldColorAttrs))

MyErrorExit("SetConsoleTextAttribute");

}

/*

* The NewLine function handles carriage returns when the processed

* input mode is disabled. It gets the current cursor position

* and resets it to the first cell of the next row.

*/

VOID NewLine(VOID) {

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))

MyErrorExit("GetConsoleScreenBufferInfo");

csbiInfo.dwCursorPosition.X = 0;

/*

* If it is the last line in the screen buffer, scroll

* the buffer up.

*/

if ((csbiInfo.dwSize.Y-1) == csbiInfo.dwCursorPosition.Y) {

ScrollScreenBuffer(hStdout, 1);

}

/* Otherwise, advance the cursor to the next line. */

else

csbiInfo.dwCursorPosition.Y += 1;

if (! SetConsoleCursorPosition(hStdout,

csbiInfo.dwCursorPosition))

MyErrorExit("SetConsoleCursorPosition");

}

+$#A Writing Characters or Colors to Consecutive Cells

Characters or color attributes can be written to specified character cells in a screen buffer. The following example uses the WriteConsoleOutputCharacter function to write a string of characters beginning at the upper left corner of a screen buffer. Then the example uses the WriteConsoleOutputAttribute function to write a string of color attributes to the first 51 cells of the same row. The coord parameter for both functions specifies the character cell in the screen buffer at which writing begins. The location in the console window where these characters or colors appear depends on the current window rectangle of the screen buffer. For additional information about the relationship between a screen buffer and its windows, see Window and Screen Buffer Size and Scrolling the Screen Buffer.

HANDLE hOutput;

LPTSTR lpszString = "Character String";

DWORD cWritten;

BOOL fSuccess;

COORD coord;

WORD wColors[3], wColor;

CHAR chFillChar;

/* Write a string of characters to a screen buffer. */

coord.X = 0; /* start at first cell */

coord.Y = 0; /* of first row */

fSuccess = WriteConsoleOutputCharacter(

hOutput, /* screen buffer handle */

lpszString, /* pointer to source string */

lstrlen(lpszString), /* length of string */

coord, /* first cell to write to */

&cWritten); /* actual number written to */

if (! fSuccess)

MyErrorExit("WriteConsoleOutputCharacter");

/* Write a string of colors to a screen buffer. */

wColors[0] = BACKGROUND_RED;

wColors[1] = BACKGROUND_RED | /* white background */

BACKGROUND_GREEN |

BACKGROUND_BLUE;

wColors[2] = BACKGROUND_BLUE;

for (;fSuccess && coord.X < 50; coord.X += 3)

fSuccess = WriteConsoleOutputAttribute(

hOutput, /* screen buffer handle */

wColors, /* pointer to source string */

3, /* length of string */

coord, /* first cell to write to */

&cWritten); /* actual number written to */

if (! fSuccess)

MyErrorExit("WriteConsoleOutputAttribute");

The same character or color attribute can be written to a specified number of consecutive screen buffer cells beginning at a specified location. The following example uses the FillConsoleOutputCharacter function to clear a 80-by-50-character screen buffer, and then it uses the FillConsoleOutputAttribute function to set the color attributes of the same cells.

/* Fill an 80-by-50-character screen buffer with the space character. */

coord.X = 0; /* start at first cell */

coord.Y = 0; /* of first row */

chFillChar = ' ';

fSuccess = FillConsoleOutputCharacter(

hStdout, /* screen buffer handle */

chFillChar, /* fill with spaces */

80*50, /* number of cells to fill */

coord, /* first cell to write to */

&cWritten); /* actual number written to */

if (! fSuccess)

MyErrorExit("FillConsoleOutputCharacter");

/* Set 80-by-50-character screen buffer colors to white text on red. */

wColor = BACKGROUND_RED |

FOREGROUND_RED |

FOREGROUND_GREEN |

FOREGROUND_BLUE;

fSuccess = FillConsoleOutputAttribute(

hStdout, /* screen buffer handle */

wColor, /* color to fill with */

80*50, /* number of cells to fill */

coord, /* first cell to write to */

&cWritten); /* actual number written to */

if (! fSuccess)

MyErrorExit("FillConsoleOutputAttribute");

+$#A Reading and Writing Blocks of Characters and Attributes

The ReadConsoleOutput function copies a rectangular block of character and color attribute data from a console screen buffer into a destination buffer. The function treats the destination buffer as a two-dimensional array of CHAR_INFO structures. Similarly, the WriteConsoleOutput function copies a rectangular block of character and color attribute data from a source buffer to a console screen buffer. For more information about reading from or writing to rectangular blocks of screen buffer cells, see Input and Output Methods.

The following example uses the CreateConsoleScreenBuffer function to create a new screen buffer. After the SetConsoleActiveScreenBuffer function makes this the active screen buffer, a block of characters and color attributes is copied from the top two rows of the SDTOUT screen buffer into a temporary buffer. The data is then copied from the temporary buffer into the new active screen buffer. When the application is finished using the new screen buffer, it calls SetConsoleActiveScreenBuffer to restore the original STDOUT screen buffer.

#include <windows.h>

VOID main(void) {

HANDLE hStdout, hNewScreenBuffer;

SMALL_RECT srctReadRect;

SMALL_RECT srctWriteRect;

CHAR_INFO chiBuffer[160]; // [2][80];

COORD coordBufSize;

COORD coordBufCoord;

BOOL fSuccess;

/*

* Get a handle of the STDOUT screen buffer to copy from and

* create a new screen buffer to copy to.

*/

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

hNewScreenBuffer = CreateConsoleScreenBuffer(

GENERIC_READ | /* read-write access */

GENERIC_WRITE,

0, /* not shared */

NULL, /* no security attributes */

CONSOLE_TEXTMODE_BUFFER, /* must be TEXTMODE */

NULL); /* reserved; must be NULL */

if (hStdout == INVALID_HANDLE_VALUE ||

hNewScreenBuffer == INVALID_HANDLE_VALUE)

MyErrorExit("CreateConsoleScreenBuffer");

/* Make the new screen buffer the active screen buffer. */

if (! SetConsoleActiveScreenBuffer(hNewScreenBuffer) )

MyErrorExit("SetConsoleActiveScreenBuffer");

/* Set the source rectangle. */

srctReadRect.Top = 0; /* top left: row 0, col 0 */

srctReadRect.Left = 0;

srctReadRect.Bottom = 1; /* bot. right: row 1, col 79 */

srctReadRect.Right = 79;

/* The temporary buffer size is 2 rows x 80 columns. */

coordBufSize.Y = 2;

coordBufSize.X = 80;

/*

* The top left destination cell of the temporary buffer is

* row 0, col 0.

*/

coordBufCoord.X = 0;

coordBufCoord.Y = 0;

/* Copy the block from the screen buffer to the temp. buffer. */

fSuccess = ReadConsoleOutput(

hStdout, /* screen buffer to read from */

chiBuffer, /* buffer to copy into */

coordBufSize, /* col-row size of chiBuffer */

coordBufCoord, /* top left dest. cell in chiBuffer */

&srctReadRect); /* screen buffer source rectangle */

if (! fSuccess)

MyErrorExit("ReadConsoleOutput");

/* Set the destination rectangle. */

srctWriteRect.Top = 10; /* top lt: row 10, col 0 */

srctWriteRect.Left = 0;

srctWriteRect.Bottom = 11; /* bot. rt: row 11, col 79 */

srctWriteRect.Right = 79;

/* Copy from the temporary buffer to the new screen buffer. */

fSuccess = WriteConsoleOutput(

hNewScreenBuffer, /* screen buffer to write to */

chiBuffer, /* buffer to copy from */

coordBufSize, /* col-row size of chiBuffer */

coordBufCoord, /* top left src cell in chiBuffer */

&srctWriteRect); /* dest. screen buffer rectangle */

if (! fSuccess)

MyErrorExit("WriteConsoleOutput");

Sleep(10000);

/* Restore the original active screen buffer. */

if (! SetConsoleActiveScreenBuffer(hStdout))

MyErrorExit("SetConsoleActiveScreenBuffer");

}

+$#A Reading Input Buffer Events

The ReadConsoleInput function can be used to directly access a console's input buffer. When a console is created, mouse input is enabled and window input is disabled. To ensure that the process receives all types of events, this example uses the SetConsoleMode function to enable window and mouse input. Then it goes into a loop that reads and handles console input events.

VOID MouseEventProc(MOUSE_EVENT_RECORD);

VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);

VOID KeyEventProc(KEY_EVENT_RECORD);

VOID GetInputEvents(VOID);

DWORD main(VOID) {

HANDLE hStdin;

DWORD cNumRead, fdwMode, fdwSaveOldMode, i;

INPUT_RECORD irInBuf[128];

/* Get the standard input handle. */

hStdin = GetStdHandle(STD_INPUT_HANDLE);

if (hStdin == INVALID_HANDLE_VALUE)

MyErrorExit("GetStdHandle");

/* Save the current input mode, to be restored on exit. */

if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )

MyErrorExit("GetConsoleMode");

/* Enable the window and mouse input events. */

fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;

if (! SetConsoleMode(hStdin, fdwMode) )

MyErrorExit("SetConsoleMode");

/* Loop to read and handle the input events. */

while (1) {

/* Wait for the events. */

if (! ReadConsoleInput(

hStdin, /* input buffer handle */

irInBuf, /* buffer to read into */

128, /* size of read buffer */

&cNumRead) ) /* number of records read */

MyErrorExit("ReadConsoleInput");

/* Dispatch the events to the appropriate handler. */

for (i = 0; i < cNumRead; i++)

switch(irInBuf[i].EventType) {

case KEY_EVENT: /* keyboard input */

KeyEventProc(irInBuf[i].Event.KeyEvent);

break;

case MOUSE_EVENT: /* mouse input */

MouseEventProc(irInBuf[i].Event.MouseEvent);

break;

case WINDOW_BUFFER_SIZE_EVENT: /* scrn buf. resizing */

ResizeEventProc(

irInBuf[i].Event.WindowBufferSizeEvent);

break;

case FOCUS_EVENT: /* disregard focus events */

case MENU_EVENT: /* disregard menu events */

break;

default:

MyErrorExit("unknown event type");

break;

}

}

return 0;

}

+$#A Scrolling a Screen Buffer's Window

The SetConsoleWindowInfo function can be used to scroll the contents of a screen buffer in the console window. This function can also change the window size. The function can either specify the new upper left and lower right corners of the screen buffer's window as absolute screen buffer coordinates or specify the changes from the current window coordinates. The function fails if the specified window coordinates are outside the boundaries of the screen buffer.

The following example scrolls the view of the screen buffer up one row by modifying the absolute window coordinates returned by the GetConsoleScreenBufferInfo function.

HANDLE hStdout;

CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

SMALL_RECT srctWindow;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Get the current screen buffer size and window position. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))

MyErrorExit("GetConsoleScreenBufferInfo");

/* Set srctWindow to the current window size and location. */

srctWindow = csbiInfo.srWindow;

/* If window is not at the screen buffer top, move it up one line. */

if ( srctWindow.Top > 0 ) {

srctWindow.Top -= 1; /* move top up by one row */

srctWindow.Bottom -= 1; /* move bottom up by one row */

if (! SetConsoleWindowInfo(

hStdout, /* screen buffer handle */

TRUE, /* absolute coordinates */

&srctWindow)) /* specifies new location */

MyErrorExit("SetConsoleWindowInfo");

}

The same scrolling can be done by specifying changes in the window coordinates.

/* Get the current screen buffer window position. */

if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))

MyErrorExit("GetConsoleScreenBufferInfo");

/*If window is not at the screen buffer top, move it up one line. */

if (csbiInfo.srWindow.Top > 0) {

srctWindow.Top = -1; /* move top up by one row */

srctWindow.Bottom = -1; /* move bottom up by one row */

srctWindow.Left = 0; /* no change */

srctWindow.Right = 0; /* no change */

if (! SetConsoleWindowInfo(

hStdout, /* screen buffer handle */

FALSE, /* deltas, not absolute */

&srctWindow)) /* specifies new location */

MyErrorExit("SetConsoleWindowInfo");

}

+$#A Scrolling a Screen Buffer's Contents

The ScrollConsoleScreenBuffer function moves a block of character cells from one part of a screen buffer to another part of the same screen buffer. The function specifies the upper left and lower right cells of the source rectangle to be moved and the destination coordinates of the new location for the upper left cell. The character and color data in the source cells is moved to the new location, and any cells left empty by the move are filled in with a specified character and color. If a clipping rectangle is specified, the cells outside of it are left unchanged.

ScrollConsoleScreenBuffer can be used to delete a line by specifying coordinates of the first cell in the line as the destination coordinates and specifying a scrolling rectangle that includes all the rows below the line.

The following example shows the use of a clipping rectangle to scroll only the bottom 15 rows of the screen buffer. The rows in the specified rectangle are scrolled up one line at a time, and the top row of the block is discarded. The contents of the screen buffer outside the clipping rectangle are left unchanged.

HANDLE hStdout;

BOOL fSuccess;

CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

SMALL_RECT srctScrollRect, srctClipRect;

CHAR_INFO chiFill;

COORD coordDest;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (hStdout == INVALID_HANDLE_VALUE)

MyErrorExit("GetStdHandle");

/* Get the screen buffer size. */

fSuccess = GetConsoleScreenBufferInfo(hStdout, &csbiInfo);

if (! fSuccess)

MyErrorExit("GetConsoleScreenBufferInfo");

/*

* The scrolling rectangle is the bottom 15 rows of the

* screen buffer.

*/

srctScrollRect.Top = csbiInfo.dwSize.Y - 16;

srctScrollRect.Bottom = csbiInfo.dwSize.Y - 1;

srctScrollRect.Left = 0;

srctScrollRect.Right = csbiInfo.dwSize.X - 1;

/* The destination for the scroll rectangle is one row up. */

coordDest.X = 0;

coordDest.Y = csbiInfo.dwSize.Y - 17;

/*

* The clipping rectangle is the same as the scrolling rectangle.

* The destination row is left unchanged.

*/

srctClipRect = srctScrollRect;

/* Fill the bottom row with green blanks. */

chiFill.Attributes = BACKGROUND_GREEN | FOREGROUND_RED;

chiFill.Char.AsciiChar = ' ';

/* Scroll up one line. */

fSuccess = ScrollConsoleScreenBuffer(

hStdout, /* screen buffer handle */

&srctScrollRect, /* scrolling rectangle */

&srctClipRect, /* clipping rectangle */

coordDest, /* top left destination cell*/

&chiFill); /* fill character and color */

+$#A Registering a Control Handler Function

This section shows an example of the SetConsoleCtrlHandler function that is used to install a control handler.

When a CTRL+C signal is received, the control handler returns TRUE, indicating that it has handled the signal. Doing this prevents other control handlers from being called.

When a CTRL_CLOSE_EVENT signal is received, the control handler returns TRUE, causing the system to display a dialog box that gives the user the choice of terminating the process and closing the console or allowing the process to continue execution. If the user chooses not to terminate the process, the system closes the console when the process finally terminates.

When a CTRL+BREAK, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT signal is received, the control handler returns FALSE. Doing this causes the signal to be passed to the next control handler function. If no other control handlers have been registered or none of the registered handlers returns TRUE, the default handler will be used, resulting in the process being terminated.

BOOL CtrlHandler(DWORD fdwCtrlType) {

switch (fdwCtrlType) {

/* Handle the CTRL+C signal. */

case CTRL_C_EVENT:

Beep(1000, 1000);

return TRUE;

/* CTRL+CLOSE: confirm that the user wants to exit. */

case CTRL_CLOSE_EVENT:

return TRUE;

/* Pass other signals to the next handler. */

case CTRL_BREAK_EVENT:

case CTRL_LOGOFF_EVENT:

case CTRL_SHUTDOWN_EVENT:

default:

return FALSE;

}

}

VOID main(void) {

BOOL fSuccess;

fSuccess = SetConsoleCtrlHandler(

(PHANDLER_ROUTINE) CtrlHandler, /* handler function */

TRUE); /* add to list */

if (! fSuccess)

MyErrorExit("Could not set control handler");

+$#A Console Application Issues

The 8-bit console functions use the OEM code page. All other functions use the ANSI code page by default. This means that strings returned by the console functions may not be processed correctly by the other functions and vice versa. For example, if FindFirstFileA returns a string that contains certain extended ANSI characters, WriteConsoleA will not display the string properly.

The best long-term solution for a console application is to use Unicode. Barring that solution, a console application should use the SetFileApisToOEM function. That function changes relevant Win32 file functions so that they produce OEM character set strings rather than ANSI character set strings.

Following are the Win32 file functions:

_lopen | GetDriveType | LoadLibrary
CopyFile | GetFileAttributes | LoadLibraryEx
CreateDirectory | GetFullPathName | MoveFile
CreateFile | GetModuleFileName | MoveFileEx
CreateProcess | GetModuleHandle | OpenFile
DeleteFile | GetSystemDirectory | RemoveDirectory
FindFirstFile | GetTempFileName | SearchPath
FindNextFile | GetTempPath | SetCurrentDirectory
GetCurrentDirectory | GetVolumeInformation | SetFileAttributes
GetDiskFreeSpace | GetWindowsDirectory |

When dealing with command lines, a console application should obtain the command line in Unicode form and convert it to OEM form, using the relevant character-to-OEM functions. Note, also, that argv uses the ANSI character set.

+$#AK Console Reference

The following functions and structures are associated with accessing a console.

+$#A Console Functions

Following are the functions used to access a console.

AllocConsole
CreateConsoleScreenBuffer
FillConsoleOutputAttribute
FillConsoleOutputCharacter
FlushConsoleInputBuffer
FreeConsole
GenerateConsoleCtrlEvent
GetConsoleCP
GetConsoleCursorInfo
GetConsoleMode
GetConsoleOutputCP
GetConsoleScreenBufferInfo
GetConsoleTitle
GetLargestConsoleWindowSize
GetNumberOfConsoleInputEvents
GetNumberOfConsoleMouseButtons
GetStdHandle
HandlerRoutine
PeekConsoleInput
ReadConsole
ReadConsoleInput
ReadConsoleOutput
ReadConsoleOutputAttribute
ReadConsoleOutputCharacter
ScrollConsoleScreenBuffer
SetConsoleActiveScreenBuffer
SetConsoleCP
SetConsoleCtrlHandler
SetConsoleCursorInfo
SetConsoleCursorPosition
SetConsoleMode
SetConsoleOutputCP
SetConsoleScreenBufferSize
SetConsoleTextAttribute
SetConsoleTitle
SetConsoleWindowInfo
SetStdHandle
WriteConsole
WriteConsoleInput
WriteConsoleOutput
WriteConsoleOutputAttribute
WriteConsoleOutputCharacter

+$#A Console Structures

Following are the structures used to access a console.

CHAR_INFO
CONSOLE_CURSOR_INFO
CONSOLE_SCREEN_BUFFER_INFO
FOCUS_EVENT_RECORD
INPUT_RECORD
MENU_EVENT_RECORD
WINDOW_BUFFER_SIZE_RECORD

Créé avec HelpNDoc Personal Edition: Produire des livres Kindle gratuitement


Control Panel Applications.rtf

+$#AK Control Panel Applications

Control Panel applications are special-purpose dynamic-link libraries (DLLs) that let users configure the environment of Microsoft® Windows®.

This overview describes Control Panel applications and explains the functions and messages that these applications use and process to complete their work.

+$#A About Control Panel Applications

Even though Windows provides a number of standard Control Panel applications, you can create additional applications to let users examine and modify the settings and operational modes of specific hardware and software.

+$#A Application Responsibilities and Operation

The primary responsibility of any Control Panel application is to display a dialog box and to carry out any tasks specified by the user. Despite this responsibility, Control Panel applications do not provide menus or other direct means for users to access their dialog boxes. Instead, these applications operate under the control of another application and display their dialog boxes only when requested by the controlling application.

Control Panel applications are usually controlled by a Windows system utility specifically designed to give users access to these applications. However, any application can load and manage Control Panel applications, as long as the controlling application sends messages and processes return values in the way that the Control Panel applications expect.

Most Control Panel applications display and manage a single dialog box, giving the user control of the settings and operational modes of a single system component. However, any given Control Panel application can provide any number of dialog boxes to control any number of system components. (These individual dialog boxes are sometimes called applets.) To distinguish between dialog boxes, a Control Panel application typically supplies the controlling application with a unique icon for each dialog box. The controlling application displays these icons and the user can choose a dialog box by choosing the corresponding icon.

+$#A Application Entry-Point Function

Every Control Panel application must export the standard entry-point function, CPlApplet. This function receives requests, in the form of Control Panel (CPL) messages, and carries out the requested work, such as initializing the application, displaying and managing the dialog box(es), and closing the application.

When the controlling application first loads the Control Panel application, it retrieves the address of the CPlApplet function and subsequently uses the address to call the function and pass it messages. The controlling application may send the following messages:

Message | Description
CPL_DBLCLK | Sent to notify CPlApplet that the user has chosen the icon associated with a given dialog box. CPlApplet should display the corresponding dialog box and carry out any user-specified tasks.
CPL_EXIT | Sent after the last CPL_STOP message and immediately before the controlling application uses the FreeLibrary function to free the DLL containing the Control Panel application. CPlApplet should free any remaining memory and prepare to close.
CPL_GETCOUNT | Sent after the CPL_INIT message to prompt CPlApplet to return a number indicating how many dialog boxes it supports.
CPL_INIT | Sent immediately after the DLL containing the Control Panel application is loaded, to prompt CPlApplet to perform initialization procedures, including memory allocation.
CPL_INQUIRE | Sent after the CPL_GETCOUNT message, to prompt CPlApplet to provide information about a given dialog box. This message has been superseded by the CPL_NEWINQUIRE message.
CPL_NEWINQUIRE | Sent after the CPL_GETCOUNT message, to prompt CPlApplet to provide information about a given dialog box . The second parameter (lParam2) is a pointer to a NEWCPLINFO structure.
CPL_SELECT | Sent to notify CPlApplet that the user has selected the icon. associated with a given dialog box.
CPL_STOP | Sent once for each dialog box before the controlling application closes. CPlApplet should free any memory associated with the given dialog box.

+$#A Message Processing

CPlApplet processes all messages sent to a Control Panel application by a controlling application. The function expects to be sent messages in a given order. The controlling application expects the messages to be processed in a given way.

CPlApplet receives the CPL_INIT message when the controlling application first loads the Control Panel application. The function should carry out any initialization, such as allocating memory, and return nonzero. If CPlApplet cannot complete the initialization, it must return zero, directing the controlling application to terminate communication and release the DLL.

CPlApplet receives the CPL_GETCOUNT message only if the CPL_INIT message succeeded. The function must then return the number of dialog boxes supported by the Control Panel application.

CPlApplet receives one CPL_NEWINQUIRE message for each dialog box supported by the Control Panel application. Each message includes the address of a NEWCPLINFO structure and a unique identifier that the controlling application uses to identify the given dialog box. The function must set the dwSize member in the NEWCPLINFO structure to the size of the structure, set the hIcon member to the handle of the icon that represents the dialog box, and set the szName and szInfo members to the addresses of the short name and description of the dialog box. If the function also sets the dwHelpContext and szHelpFile members, the controlling application will provide context-sensitive Help for the dialog box. The lData member can be used for application-defined data.

Note The CPL_NEWINQUIRE message and NEWCPLINFO structure supersede the CPL_INQUIRE message and CPLINFO structure, respectively.

CPlApplet receives a CPL_SELECT message as a notification that the user has selected the icon representing the dialog box. The function may receive this message any number of times. The message includes the dialog box identifier previously supplied by the controlling application and the lData value previously supplied by CPlApplet. Typically, Control Panel applications do not respond to the CPL_SELECT message.

CPlApplet receives a CPL_DBLCLK message as a notification that the user has chosen the icon representing the dialog box. The function may receive this message any number of times. The message includes the dialog box identifier and the lData value. The function should display the corresponding dialog box and process subsequent user input.

Before the controlling application terminates, CPlApplet receives the CPL_STOP message once for each dialog box supported by the Control Panel application. The message includes the identifier for the dialog box and the lData value. The function should free any memory that it allocated for the given dialog box.

After the last CPL_STOP message, CPlApplet receives a CPL_EXIT message. The function should free all remaining allocated memory and unregister any private window classes that it may have registered. Immediately after the function returns from this message, the controlling application releases the Control Panel application by calling the FreeLibrary function.

+$#A Application Setup

Every Control Panel application is a dynamic-link library. To make sure the DLL containing your Control Panel application can be located and automatically loaded by the Windows system utility, the DLL file must have the .CPL filename extension and must be set up in one of the following ways:

• Copied to the directory that contains the Windows system utility.

• Copied to the Windows SYSTEM directory.

• Registered under the the MMCPL key of the HKEY_CURRENT_USER key in the registry.

You should register the DLL in the registry if the DLL exports functions other than CPlApplet and provides functionality beyond the scope of a Control Panel application. For more information about the registry, seeRegistry.

+$#A Creating Control Panel Applications

Although a Control Panel application may support more than one dialog box, it processes all requests through the single CPlApplet function. In the following example, the Control Panel application supports three dialog boxes that let the user set preferences for a component stereo system attached to the computer. The example uses an application-defined StereoApplets array that contains three structures, each corresponding to one of the dialog boxes. Each structure contains all the information required by the CPL_NEWINQUIRE message, as well as the dialog box template and dialog box procedure required by the CPL_DBLCLK message. The code demonstrates how to fill the structures in the StereoApplets array.

#define NUM_APPLETS 3

typedef struct tagApplets

{

int icon; // icon resource identifier

int namestring; // name-string resource identifier

int descstring; // description-string resource identifier

int dlgtemplate; // dialog box template resource identifier

DLGPROC dlgfn; // dialog box procedure

} APPLETS;

APPLETS StereoApplets[NUM_APPLETS] =

{

AMP_ICON, AMP_NAME, AMP_DESC, AMP_DLG, AmpDlgProc,

TUNER_ICON, TUNER_NAME, TUNER_DESC, TUNER_DLG, TunerDlgProc,

TAPE_ICON, TAPE_NAME, TAPE_DESC, TAPE_DLG, TapeDlgProc,

};

LONG CALLBACK CPlApplet(hwndCPL, uMsg, lParam1, lParam2)

HWND hwndCPL; // handle of Control Panel window

UINT uMsg; // message

LPARAM lParam1; // first message parameter

LPARAM lParam2; // second message parameter

{

int i;

LPNEWCPLINFO lpNewCPlInfo;

i = (int) lParam1;

switch (uMsg) {

case CPL_INIT: // first message, sent once

hinst = GetModuleHandle("ecp.cpl");

return TRUE;

case CPL_GETCOUNT: // second message, sent once

return NUM_APPLETS;

break;

case CPL_NEWINQUIRE: // third message, sent once per app

lpNewCPlInfo = (LPNEWCPLINFO) lParam2;

lpNewCPlInfo->dwSize = (DWORD) sizeof(NEWCPLINFO);

lpNewCPlInfo->dwFlags = 0;

lpNewCPlInfo->dwHelpContext = 0;

lpNewCPlInfo->lData = 0;

lpNewCPlInfo->hIcon = LoadIcon(hinst,

(LPCTSTR) MAKEINTRESOURCE(StereoApplets[i].icon));

lpNewCPlInfo->szHelpFile[0] = '\0';

LoadString(hinst, StereoApplets[i].namestring,

lpNewCPlInfo->szName, 32);

LoadString(hinst, StereoApplets[i].descstring,

lpNewCPlInfo->szInfo, 64);

break;

case CPL_SELECT: // applet icon selected

break;

case CPL_DBLCLK: // applet icon double-clicked

MessageBeep(MB_ICONEXCLAMATION);

DialogBox(hinst,

MAKEINTRESOURCE(StereoApplets[i].dlgtemplate),

hwndCPL, StereoApplets[i].dlgfn);

break;

case CPL_STOP: // sent once per app. before CPL_EXIT

break;

case CPL_EXIT: // sent once before FreeLibrary is called

break;

default:

break;

}

return 0;

}

+$#AK Control Panel Reference

The following functions and messages are associated with the Control Panel.

+$#A Control Panel Functions

Following is the function used with the Control Panel.

CPlApplet

+$#A Control Panel Messages

Following are the messages used with the Control Panel.

CPL_DBLCLK
CPL_EXIT
CPL_GETCOUNT
CPL_INIT
CPL_INQUIRE
CPL_NEWINQUIRE
CPL_SELECT
CPL_STOP
WM_CPL_LAUNCH
WM_CPL_LAUNCHED

Créé avec HelpNDoc Personal Edition: Créer des documentations web iPhone


Controls.rtf

+$#AK Controls

In Microsoft® Windows®, a control is a child window an application uses in conjunction with another window to perform simple input and output (I/O) tasks. Controls are most often used within dialog boxes, but they can also be used in other windows. Controls within dialog boxes provide the user with the means to type text, choose options, and direct a dialog box to complete its action. Controls in other windows provide a variety of services, such as letting the user choose commands, view status, and view and edit text.

+$#A About Controls

Controls, like other windows, each belong to a window class, either predefined or application defined. The window class and the corresponding window procedure define the properties of the control, its appearance, behavior, and purpose. An application can create controls individually by specifying the name of the window class when calling the CreateWindowEx function. An application can also direct Windows to create controls for a dialog box by specifying the controls in the dialog box template.

Controls are child windows. When Windows creates controls for a dialog box, each control is the child of the dialog box. When an application creates a control, the control is the child of a window identified by the application. This relationship is important because a control sends messages, called notification messages, to its parent window when events, such as input from the user, occur in the control. The application relies on these notification messages to determine what action the user wants the application to take.

Because controls are windows, an application can manipulate them by using the window-management functions, such as the ShowWindow and EnableWindow functions. If the window class for a control supports control messages, an application can also manipulate a control of that class by using the SendMessage function to send these messages to the control. The purpose and function of each control message is specific to the window class and is defined by the control's window procedure.

+$#A Predefined Controls

Windows provides several predefined window classes for controls. Controls belonging to these window classes are called predefined controls. An application creates a predefined control of a particular type by specifying the appropriate window class name in either the CreateWindowEx function or the dialog box template. Following are the predefined window classes.

Name | Description
BUTTON | Creates button controls. These controls typically notify the parent window when the user chooses the control.
COMBOBOX | Creates combo boxes. These controls are a combination of list boxes and edit controls, letting the user choose and edit items.
EDIT | Creates edit controls. These controls let the user view and edit text.
LISTBOX | Creates list boxes. These controls display a list from which the user can select one or more items.
SCROLLBAR | Creates scroll bar controls. These controls let the user choose the direction and distance to scroll information in a related window.
STATIC | Creates static controls. These controls often act as labels for other controls.

Each predefined window class has a corresponding set of control styles that enable an application to vary the appearance and behavior of the controls it creates. For example, the BUTTON class supports styles to create push buttons, radio buttons, check boxes, and group boxes. An application specifies the style when creating the control.

Each predefined window class has a corresponding set of notification and control messages. Applications rely on the notification messages to determine when the user has provided input to the controls. For example, a push button sends a BN_CLICKED message to the parent window when the user clicks the button. Applications use the control messages to retrieve information from the controls and to manipulate the appearance and behavior of the controls. For example, an application can send a BM_GETCHECK message to a check box to determine whether it currently contains a check mark.

Most applications make extensive use of predefined controls in dialog boxes and other windows. Because predefined controls offer many capabilities, a full discussion of each is beyond the scope of this topic.

+$#A Custom Controls

Applications can create custom controls to perform tasks not supported by predefined controls. Windows provides the following ways to create custom controls:

• Use owner-drawn buttons, list boxes, and combo boxes.

• Subclass an existing control-window class.

• Register and implement from scratch an application-defined window class.

Buttons, list boxes, and combo boxes have owner-drawn styles available that direct the control to send a message to the parent window whenever the control must be drawn. This feature permits an application to alter the appearance of a control. For buttons, the owner-drawn style affects how the system draws the entire control. For list boxes and combo boxes, the parent window draws the items within the control, and the control draws its own outline. For example, an application can customize a list box so that it displays a small bitmap next to each item in the list.

An application can designate list boxes, combo boxes, and buttons as owner-drawn controls by creating them with the appropriate style. When a control has the owner-drawn style, Windows handles the user's interaction with the control as usual, performing such tasks as detecting when a user has chosen a button and notifying the button's owner of the event. However, because the control is owner drawn, the parent window of the control is responsible for the visual appearance of the control. For more information about owner-drawn controls, see the individual topics for buttons, list boxes, and combo boxes.

Subclassing an existing control is another way to create a custom control. The subclass procedure can alter selected behaviors of the control by processing those messages that affect the selected behaviors. All other messages pass to the original window procedure for the control. For example, an application can display a small bitmap next to the text in a read-only, single-line edit control by subclassing the control and processing the WM_PAINT message. For more information about subclassing, see Window Classes.

Although an application may subclass a predefined control, it relies on the window procedure of the control to provide all other aspects of the control's behavior. For more information about a control's behavior, see the individual topics for the predefined controls.

An application can create custom controls by registering an application-defined window class and specifying the name of the window class in the CreateWindowEx function or in the dialog box template. The process for registering an application-defined window class for a custom control is the same as for registering a class for an ordinary window. Each class must have a unique name, a corresponding window procedure, and other information.

At a minimum, the window procedure draws the control. If an application uses the control to let the user type information, the window procedure also processes input messages from the keyboard and mouse and sends notification messages to the parent window. In addition, if the control supports control messages, the window procedure processes messages sent to it by the parent window or other windows. For example, controls often process the WM_GETDLGCODE message sent by dialog boxes to direct a dialog box to process keyboard input in a given way.

+$#A Control Notification Messages

A control should send a notification message to its parent window to notify the parent about user input or changes to the control. The notification message is a WM_COMMAND message that includes a control identifier and a notification code identifying the nature of the event. A control identifier is a unique number the application uses to identify the control sending the message. The application sets the identifier for a control when it creates the control. The application specifies the identifier either in the hMenu parameter of the CreateWindowEx function or in the id member of the dialog box template (the DLGITEMTEMPATE structure).

Because the control does not set the control identifier, the control must retrieve the identifier before it can send notification messages. A control must use the GetDlgCtrlID function to retrieve its own control identifier. Although the control identifier is specified as the menu handle when the control is created, the GetMenu function cannot be used to retrieve the identifier. Alternatively, a control can retrieve the identifier from the hMenu member in the CREATESTRUCT structure while processing the WM_CREATE message.

+$#A Control Messages

A parent window or other windows send control messages to direct a control to perform specific tasks. The window procedure processes these messages and carries out the requested action.

Control messages can be predefined or application defined. Windows has several predefined messages, such as WM_GETTEXT and WM_GETDLGCODE, that it sends to controls. These messages typically correspond to window-management functions that carry out actions on windows. In general, the window procedure for an application-defined control should process any predefined control message in the following table if the message affects the operation of the control.

Message | Recommendation
WM_GETDLGCODE | Process if the control uses the ENTER, ESC, TAB, or arrow keys. The IsDialogMessage function sends this message to controls in a dialog box to determine whether to process the keys or pass them to the control.
WM_GETFONT | Process if the WM_SETFONT message is also processed.
WM_GETTEXT | Process if the control text is not the same as the title specified by the CreateWindowEx function.
WM_GETTEXTLENGTH | Process if the control text is not the same as the title specified by the CreateWindowEx function.
WM_KILLFOCUS | Process if the control displays a caret, a focus rectangle, or another item to indicate that it has the input focus.
WM_SETFOCUS | Process if the control displays a caret, a focus rectangle, or another item to indicate that it has the input focus.
WM_SETTEXT | Process if the control text is not the same as the title specified by the CreateWindowEx function.
WM_SETFONT | Process if the control displays text. Windows sends this message when creating a dialog box that has the DS_SETFONT style.

Application-defined control messages are specific to the given control and must be explicitly sent to the control by using a SendMessage or SendDlgItemMessage function. The numeric value for each message must be unique and must not conflict with the values of other window messages. To ensure that application-defined message values do not conflict, an application should create each value by adding a unique number to the WM_USER value.

+$#A Control Messages

Following are the messages used with controls.

WM_GETFONT
WM_SETFONT
WM_SETTEXT

Créé avec HelpNDoc Personal Edition: Générateur de documentation complet


Coordinate Spaces and Transformations.rtf

+$#AK Coordinate Spaces and Transformations

Applications written for the Microsoft® Windows® operating system use coordinate spaces and transformations to scale, rotate, translate, shear, and reflect graphics output. A coordinate space is a planar space based on the Cartesian coordinate system. This system requires two axes that are perpendicular and equal in length. There are four coordinate spaces in Windows: world, page, device, and physical device (client area, or desktop, or page of printer paper). A transformation is an algorithm that alters ("transforms") the size, orientation, and shape of objects.

+$#A About Coordinate Spaces and Transformations

Coordinate spaces and transformations are used, for example, by desktop publishing applications to "zoom" parts of a page or to display adjacent pages in a window; computer-aided design (CAD) applications use them to rotate objects, scale drawings, or to create perspective views; and spreadsheet applications use them to move and size graphs.

The following illustrations show successive views of an object created in a drawing application. The first illustration shows the object as it appears in the original drawing; the five remaining illustrations show the effects of applying various transformations.

{ewc msdncd, EWGraphic, group10655 0 /a "SDK_01.bmp"}

Original View

{ewc msdncd, EWGraphic, group10655 1 /a "SDK_02.bmp"}

Scaled View

{ewc msdncd, EWGraphic, group10655 2 /a "SDK_03.bmp"}

Translated View

{ewc msdncd, EWGraphic, group10655 3 /a "SDK_04.bmp"}

Rotated View

{ewc msdncd, EWGraphic, group10655 4 /a "SDK_05.bmp"}

Sheared View

{ewc msdncd, EWGraphic, group10655 5 /a "SDK_06.bmp"}

Reflection

+$#A Transformation of Coordinate Spaces

A coordinate space is a planar space based on the Cartesian coordinate system. This system provides a means of specifying the location of each point on a plane. It requires two axes that are perpendicular and equal in length. The following illustration shows a coordinate space.

{ewc msdncd, EWGraphic, group10655 6 /a "SDK_07.bmp"}

The Microsoft® ( ) Win32® application programming interface (API) uses four coordinate spaces: world, page, device, and physical device. Applications use world space to rotate, shear, or reflect graphics output. World space measures 2^32 units high by 2^32 units wide. Page space (referred to as logical space in earlier versions of Windows) also measures 2^32 units high by 2^32 units wide. Device space measures 2^27 units high by 2^27 units wide. Page space works with device space to provide applications with device-independent units, such as millimeters and inches. The Win32 API refers to both world space and page space as logical space. The final coordinate space, physical device, usually refers to the client area of the application's window; however, it can also include the entire desktop, a complete window (including the frame, title bar, and menu bar), or a page of printer or plotter paper. Physical device dimensions vary according to the dimensions set by the display, printer, or plotter technology.

To depict output on a physical device, Windows copies (or maps) a rectangular region from one coordinate space into the next coordinate space until finally the output appears in its entirety on the physical device. Mapping begins in the application's world space if the application has called the SetWorldTransform function; otherwise, mapping occurs in page space. As Windows copies each point within the rectangular region from one space into another, it applies an algorithm called a transformation. A transformation alters (or transforms) the size, orientation, and shape of objects that are copied from one coordinate space into another. Although a transformation affects an object as a whole, it is applied to each point, or to each line, in the object.

The following illustration shows a typical transformation performed by using the SetWorldTransform function.

{ewc msdncd, EWGraphic, group10655 7 /a "SDK_08.bmp"}

+$#A World-Space to Page-Space Transformations

World-space to page-space transformations are new to Windows. They support translation and scaling that were available in previous versions of Windows. In addition, they support rotation, shear, and reflection capabilities. The following sections describe these transformations, illustrate their effects, and provide the algorithms used to achieve them.

+$#A Translation

Some applications translate (or shift) objects drawn in the client area. If your application features this capability, use the SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM structure containing the appropriate values. The eDx and eDy members of XFORM specify the horizontal and vertical translation components, respectively.

When translation occurs, each point in an object is shifted vertically, horizontally, or both, by a specified amount. The following illustration shows a 20- by 20-unit rectangle that was translated to the right by 10 units when copied from world coordinate space to page coordinate space.

{ewc msdncd, EWGraphic, group10655 8 /a "SDK_09.bmp"}

In the preceding illustration, the x-coordinate of each point in the rectangle is 10 units greater than the original x-coordinate.

Horizontal translation can be represented by the following algorithm.

x' = x + Dx

Where x' is the new x-coordinate, x is the original x-coordinate, and Dx is the horizontal distance moved.

Vertical translation can be represented by the following algorithm.

y' = y + Dy

Where y' is the new y-coordinate, y is the original y-coordinate, and Dy is the vertical distance moved.

The horizontal and vertical translation transformations can be combined into a single operation by using a 3-by-3 matrix.

|1 0 0|

|x' y' 1| = |x y 1| * |0 1 0|

|Dx Dy 1|

(The rules of matrix multiplication state that the number of rows in one matrix must equal the number of columns in the other. The integer 1 in the matrix |x y 1| is a placeholder that was added to meet this requirement.)

The 3-by-3 matrix that produced the illustrated translation transformation contains the following values.

|1 0 0|

|0 1 0|

|10 0 1|

+$#A Scaling

Most CAD and drawing applications provide features that scale output created by the user. If your application features scaling (or zoom) capabilities, use the SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM structure containing the appropriate values. The eM11 and eM22 members of XFORM specify the horizontal and vertical scaling components, respectively.

When scaling occurs, the vertical and horizontal lines (or vectors), that constitute an object, are stretched or compressed with respect to the x- or y-axis. The following illustration shows a 20- by 20-unit rectangle scaled vertically to twice its original height when copied from world coordinate space to page coordinate space.

{ewc msdncd, EWGraphic, group10655 9 /a "SDK_10.bmp"}

In the preceding illustration, the vertical lines that define the original rectangle's side measure 20 units, while the vertical lines that define the scaled rectangle's sides measure 40 units.

Vertical scaling can be represented by the following algorithm.

y' = y * Dy

Where y' is the new length, y is the original length, and Dy is the vertical scaling factor.

Horizontal scaling can be represented by the following algorithm.

x' = x * Dx

Where x' is the new length, x is the original length, and Dx is the horizontal scaling factor.

The vertical and horizontal scaling transformations can be combined into a single operation by using a 2-by-2 matrix.

|x' y'| = |Dx 0| * |x y|

|0 Dy|

The 2-by-2 matrix that produced the scaling transformation contains the following values.

|1 0|

|0 2|

+$#A Rotation

Many CAD applications provide features that rotate objects drawn in the client area. If your application features rotation capabilities, use the SetWorldTransform function to set the appropriate world-space to page-space transformation. This function receives a pointer to an XFORM structure containing the appropriate values. The eM11, eM12, eM21, and eM22 members of XFORM specify, respectively, the cosine, sine, negative sine, and cosine of the angle of rotation.

When rotation occurs, the points that constitute an object are rotated with respect to the coordinate-space origin. The following illustration shows a 20- by 20-unit rectangle rotated 30 degrees when copied from world coordinate space to page coordinate space.

{ewc msdncd, EWGraphic, group10655 10 /a "SDK_11.bmp"}

In the preceding illustration, each point in the rectangle was rotated 30 degrees with respect to the coordinate-space origin.

The following algorithm computes the new x-coordinate (x') for a point (x,y) that is rotated by angle A with respect to the coordinate-space origin.

x' = (x * cos A) - (y * sin A)

The following algorithm computes the y-coordinate (y') for a point (x,y) that is rotated by the angle A with respect to the origin.

y' = (x * sin A) + (y * cos A)

The two rotation transformations can be combined in a 2-by-2 matrix as follows.

|x' y'| == |x y| * | cos A sin A|

|-sin A cos A|

The 2-by-2 matrix that produced the rotation contains the following values.

| .8660 .5000|

|-.5000 .8660|

Rotation Algorithm Derivation

Rotation algorithms are based on trigonometry's addition theorem stating that the trigonometric function of a sum of two angles (A1 and A2) can be expressed in terms of the trigonometric functions of the two angles.

sin(A1 + A2) = (sin A1 * cos A2) + (cos A1 * sin A2)

cos(A1 + A2) = (cos A1 * cos A2) - (sin A1 * sin A2)

The following illustration shows a point p rotated counterclockwise to a new position p'. In addition, it shows two triangles formed by a line drawn from the coordinate-space origin to each point and a line drawn from each point through the x-axis.

{ewc msdncd, EWGraphic, group10655 11 /a "SDK_12.bmp"}

Using trigonometry, the x-coordinate of point p can be obtained by multiplying the length of the hypotenuse h by the cosine of A1.

x = h * cos A1

The y-coordinate of point p can be obtained by multiplying the length of the hypotenuse h by the sine of A1.

y = h * sin A1

Likewise, the x-coordinate of point p' can be obtained by multiplying the length of the hypotenuse h by the cosine of (A1 + A2).

x' = h * cos (A1 + A2)

Finally, the y-coordinate of point p' can be obtained by multiplying the length of the hypotenuse h by the sine of (A1 + A2).

y' = h * sin (A1 + A2)

Using the addition theorem, the preceding algorithms become the following.

x' = (h * cos A1 * cos A2) - (h * sin A1 * sin A2)

y' = (h * cos A1 * sin A2) + (h * sin A1 * cos A2)

The rotation algorithms for a given point rotated by angle A2 can be obtained by substituting x for each occurrence of (h * cos A1) and substituting y for each occurrence of (h * sin A1).

x' = (x * cos A2) - (y * sin A2)

y' = (x * sin A2) + (y * cos A2)

+$#A Shear

Some applications provide features that shear objects drawn in the client area. If your application features shear capabilities, use the SetWorldTransform function to set appropriate values in the world-space to page-space transformation. This function receives a pointer to an XFORM structure containing the appropriate values. The eM12 and eM21 members of XFORM specify the horizontal and vertical proportionality constants, respectively.

There are two components of the shear transformation. The first alters the vertical lines in an object; the second alters the horizontal lines. The following illustration shows a 20- by 20-unit rectangle sheared horizontally when copied from world space to page space.

{ewc msdncd, EWGraphic, group10655 12 /a "SDK_13.bmp"}

A horizontal shear can be represented by the following algorithm:

x' = x + (Sx * y)

where x is the original x-coordinate, Sx is the proportionality constant, and x' is the result of the shear transformation.

A vertical shear can be represented by the following algorithm:

y' = y + (Sy * x)

where y is the original y-coordinate, Sy is the proportionality constant, and y' is the result of the shear transformation.

The horizontal-shear and vertical-shear transformations can be combined into a single operation using a 2-by-2 matrix.

|x' y'| == |x y| * | 1 Sx|

| Sy 1|

The 2-by-2 matrix that produced the shear contains the following values.

|1 1|

|0 1|

+$#A Reflection

Some applications provide features that reflect (or mirror) objects drawn in the client area. If your application features reflection capabilities, use the SetWorldTransform function to set the appropriate values in the world-space to page-space transformation. This function receives a pointer to an XFORM structure containing the appropriate values. The eM11 and eM22 members of XFORM specify the horizontal and vertical reflection components, respectively.

The reflection transformation creates a "mirror" image of an object with respect to either the x- or y-axis. In short, reflection is just negative scaling. To produce a horizontal reflection, x-coordinates are multiplied by −1. To produce a vertical reflection, y-coordinates are multiplied by − 1.

Horizontal reflection can be represented by the following algorithm:

x' = −x

where x is the x-coordinate and x' is the result of the reflection.

The 2-by-2 matrix that produced horizontal reflection contains the following values:

|-1 0|

|0 1|

Vertical reflection can be represented by the following algorithm:

y' = -y

where y is the y-coordinate and y' is the result of the reflection.

The 2-by-2 matrix that produced vertical reflection contains the following values:

|1 0|

|0 -1|

The horizontal-reflection and vertical-reflection operations can be combined into a single operation by using the following 2-by-2 matrix:

|-1 0|

|0 -1|

+$#A Combined World-to-Page Space Transformations

You can combine the five world-to-page transformations into a single 3-by-3 matrix. The CombineTransform function can be used to combine two world-space to page-space transformations. You can use the combined transformations to alter output associated with a particular device context (DC) by calling the SetWorldTransform function and supplying the elements for this matrix. When an application calls SetWorldTransform, it stores the elements of the 3-by-3 matrix in an XFORM structure. The members of this structure correspond to the first two columns of a 3-by-3 matrix; the last column of the matrix is not required because its values are constant.

You can retrieve the elements of the current world transformation matrix by calling the GetWorldTransform function and supplying a pointer to an XFORM structure.

+$#A Page-Space to Device-Space Transformations

The page-space to device-space transformation was part of the original Windows interface. This transformation determines the mapping mode for all graphics output associated with a particular DC. A mapping mode is a scaling transformation that specifies the size of the units used for drawing operations. The mapping mode may also perform translation. In some cases, the mapping mode alters the orientation of the x- and y-axes in device space. The mapping modes are described in the following table.

Mapping mode | Description
MM_ANISOTROPIC | Each unit in page space is mapped to an application-specified unit in device space. The axis may or may not be equally scaled (for example, a circle drawn in world space may appear to be an ellipse when depicted on a given device). The orientation of the axis is also specified by the application.
MM_HIENGLISH | Each unit in page space is mapped to 0.001 inch in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_HIMETRIC | Each unit in page space is mapped to 0.01 millimeter in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_ISOTROPIC | Each unit in page space is mapped to an application-defined unit in device space. The axes are always equally scaled. The orientation of the axes may be specified by the application.
MM_LOENGLISH | Each unit in page space is mapped to 0.01 inch in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_LOMETRIC | Each unit in page space is mapped to 0.1 millimeter in device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move up.
MM_TEXT | Each unit in page space is mapped to one pixel; that is, no scaling is performed at all. When no translation is in effect (this is the default), page space in the MM_TEXT mapping mode is equivalent to physical device space. Increasing values of x occur as you move to the right; increasing values of y occur as you move down.
MM_TWIPS | Each unit in page space is mapped to one twentieth of a printer's point (1/1440 inch). Increasing values of x occur as you move to the right; increasing values of y occur as you move up.

You set a mapping mode by calling the SetMapMode function. You retrieve the current mapping mode for a DC by calling the GetMapMode function.

The page-space to device-space transformations consist of values calculated from the points given by the window and viewport. The window and viewport each consist of a pair of points, with one point specifying an origin and the other a width and height (called the extents). The window points are in logical coordinates; the viewport in device coordinates (pixels). Windows combines the origins and extents from both the window and viewport to create the transformation. This means that the window and viewport each specify half of the factors needed to define the transformation used to map points in page space to device space. The effect of a transformation calculated in this way is that Windows maps the window origin to the viewport origin and the window extents to the viewport extents, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10655 13 /a "SDK_15.bmp"}

The window and viewport extents establish a ratio or scaling factor used in the page-space to device-space transformations. For the six predefined mapping modes (MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, MM_TEXT, and MM_TWIPS), the extents are set by Windows when you call SetMapMode. They cannot be changed. The other two mapping modes (MM_ISOTROPIC and MM_ANISOTROPIC) require that you specify the extents. This is done by calling SetMapMode to set the appropriate mode and then calling the SetWindowExtEx and SetViewportExtEx functions to specify the extents. In the MM_ISOTROPIC mapping mode, it is important to call SetWindowExtEx before calling SetViewportExtEx.

The window and viewport origins establish the translation used in the page-space to device-space transformations. You set the window and viewport origins by using the SetWindowOrgEx and SetViewportOrgEx functions. The origins are independent of the extents, and an application can set them regardless of the current mapping mode. Changing a mapping mode does not affect the currently set origins (although it can affect the extents). Origins are specified in absolute units that the current mapping mode does not affect. You can also alter the origins by using the OffsetWindowOrgEx and OffsetViewportOrgEx functions.

The following formula shows the math involved in converting a point from page space to device space.

Dx = ((Lx - WOx) * VEx / WEx) + VOx

The following variables are involved.

Dx x value in device units

Lx x value in logical units (also known as page space units)

WOx window x origin

VOx viewport x origin

WEx window x-extent

VEx viewport x-extent

The same equation with y replacing x transforms the y component of a point.

The formula first offsets the point from its coordinate origin. This value, no longer biased by the origin, is then scaled into the destination coordinate system by the ratio of the extents. Finally, the scaled value is offset by the destination origin to its final mapping.

The LPtoDP and DPtoLP functions may be used to convert from logical points to device points and from device points to logical points, respectively.

+$#A Predefined Mapping Modes

Of the six predefined mapping modes, one is device dependent (MM_TEXT) while the remaining five (MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS) are device independent.

The default mapping mode is MM_TEXT. One logical unit equals one pixel. Positive x is to the right, and positive y is down. This mode maps directly to the device's coordinate system. The logical-to-physical mapping involves only an offset in x and y that is defined by the application-controlled window and viewport origins. The viewport and window extents are all set to 1, creating a one-to-one mapping.

If your application displays geometric shapes (circles, squares, polygons, and so on), you may want to choose one of the device-independent mapping modes. For example, if you are writing code to provide charting capabilities for a spreadsheet application and want to guarantee that the diameter of each pie chart is 2 inches, you can choose the MM_LOENGLISH mapping mode and call the appropriate functions to draw and fill the chart. By specifying MM_LOENGLISH, you guarantee that the diameter of the chart is consistent on any display or printer. If you were to choose MM_TEXT instead of MM_LOENGLISH, a chart that appears circular on a VGA display would appear elliptical on an EGA display and would appear very small on a 300 dpi (dots per inch) laser printer.

+$#A Application-Defined Mapping Modes

The two application-defined mapping modes (MM_ISOTROPIC and MM_ANISOTROPIC) are provided for application-specific mapping modes. The MM_ISOTROPIC mode guarantees that logical units in the x-direction and in the y-direction are equal, while the MM_ANISOTROPIC mode allows the units to differ. A CAD or drawing application can benefit from the MM_ISOTROPIC mapping mode but may need to specify logical units that correspond to the increments on an engineer's scale (1/64 inch). These units would be difficult to obtain with the predefined mapping modes (MM_HIENGLISH or MM_HIMETRIC); however, they can easily be obtained by selecting the MM_ISOTROPIC (or MM_ANISOTROPIC) mode. The following example shows how to set logical units to 1/64 inch:

SetMapMode(hDC, MM_ISOTROPIC);

SetWindowExtEx(hDC, 64, 64, NULL);

SetViewportExtEx(hDC, GetDeviceCaps(hDC, LOGPIXELSX),

GetDeviceCaps(hDC, LOGPIXELSY), NULL);

+$#A Device-Space to Physical-Device Transformation

The device-space to physical-device transformation is unique in several respects. For example, it is limited to translation and is controlled by the window manager component of Windows. The sole purpose of this transformation is to ensure that the origin of device space is mapped to the proper point on the physical device. There are no functions to set this transformation, nor are there any functions to retrieve related data.

+$#A Default Transformations

Whenever an application creates a DC and immediately begins calling GDI drawing or output functions, it takes advantage of the default page-space to device-space, and device-space to client-area transformations. A world-to-page space transformation cannot happen until the application first calls the SetGraphicsMode function to set the mode to GM_ADVANCED and then calls the SetWorldTransform function.

Use of MM_TEXT (the default page-space to device-space transformation) results in a one-to-one mapping; that is, a given point in page space maps to the same point in device space. As previously mentioned, this transformation is not specified by a matrix. Instead, it is obtained by dividing the width of the viewport by the width of the window and the height of the viewport by the height of the window. In the default case, the viewport dimensions are 1-pixel by 1-pixel and the window dimensions are 1-page unit by 1-page unit.

The device-space to physical-device (client area, desktop, or printer paper) transformation always results in a one-to-one mapping; that is, one unit in device space is always equivalent to one unit in the client area, on the desktop, or on a page. The sole purpose of this transformation is translation; it ensures that output appears correctly in an application's window no matter where that window is moved on the desktop.

The one unique aspect of MM_TEXT is the orientation of the y-axis in page space. In MM_TEXT, the positive y-axis extends downward and the negative y-axis extends upward.

+$#A Using Coordinate Spaces and Transformations

This section contains an example that demonstrates the following tasks:

• Drawing graphics with predefined units.

• Centering graphics in the application's client area.

• Scaling graphics output to half its original size.

• Translating graphics output 3/4 of an inch to the right.

• Rotating graphics 30 degrees.

• Shearing graphics output along the x-axis.

• Reflecting graphics output about an imaginary horizontal axis drawn through its midpoint.

The following example was used to create the illustrations that appear earlier in this topic.

void TransformAndDraw(int iTransform, HWND hWnd)

{

HDC hDC;

XFORM xForm;

RECT rect;

/* Retrieve a DC handle for the application's window. */

hDC = GetDC(hWnd);

/*

* Set the mapping mode to LOENGLISH. This moves the

* client area origin from the upper left corner of the

* window to the lower left corner (this also reorients

* the y-axis so that drawing operations occur in a true

* Cartesian space). It guarantees portability so that

* the object drawn retains its dimensions on any

* display running Windows.

*/

SetMapMode(hDC, MM_LOENGLISH);

/*

* Set the appropriate world transformation (based on the

* user's menu selection).

*/

switch (iTransform) {

case SCALE: /* Scale to 1/2 of the original size. */

xForm.eM11 = (FLOAT) 0.5;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 0.5;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

case TRANSLATE: /* Translate right by 3/4 inch. */

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 75.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

case ROTATE: /* Rotate 30 degrees counterclockwise. */

xForm.eM11 = (FLOAT) 0.8660;

xForm.eM12 = (FLOAT) 0.5000;

xForm.eM21 = (FLOAT) -0.5000;

xForm.eM22 = (FLOAT) 0.8660;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

case SHEAR: /* Shear along the x-axis with a */

/* proportionality constant of 1.0. */

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 1.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

case REFLECT: /* Reflect about a horizontal axis. */

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) -1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

case NORMAL: /* Set the unity transformation. */

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

}

/* Find the midpoint of the client area. */

GetClientRect(hWnd, (LPRECT) &rect);

DPtoLP(hDC, (LPPOINT) &rect, 2);

/* Select a hollow brush. */

SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));

/* Draw the exterior circle. */

Ellipse(hDC, (rect.right / 2 - 100), (rect.bottom / 2 + 100),

(rect.right / 2 + 100), (rect.bottom / 2 - 100));

/* Draw the interior circle. */

Ellipse(hDC, (rect.right / 2 -94), (rect.bottom / 2 + 94),

(rect.right / 2 + 94), (rect.bottom / 2 - 94));

/* Draw the key. */

Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 113),

(rect.right / 2 + 13), (rect.bottom / 2 + 50));

Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 96),

(rect.right / 2 + 13), (rect.bottom / 2 + 50));

/* Draw the horizontal lines. */

MoveToEx(hDC, (rect.right / 2 - 150), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 - 16), (rect.bottom / 2 + 0));

MoveToEx(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 + 13), (rect.bottom / 2 + 0));

MoveToEx(hDC, (rect.right / 2 + 16), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 + 150), (rect.bottom / 2 + 0));

/* Draw the vertical lines. */

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 150), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 16));

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 13), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 13));

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 16), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 150));

ReleaseDC(hWnd, hDC);

}

+$#A Coordinate Space and Transformation Functions

Following are the functions used with coordinate spaces and transformations.

ClientToScreen
CombineTransform
DPtoLP
GetCurrentPositionEx
GetGraphicsMode
GetMapMode
GetViewportExtEx
GetViewportOrgEx
GetWindowExtEx
GetWindowOrgEx
GetWorldTransform
LPtoDP
MapWindowPoints
ModifyWorldTransform
OffsetViewportOrgEx
OffsetWindowOrgEx
ScaleViewportExtEx
ScaleWindowExtEx
ScreenToClient
SetGraphicsMode
SetMapMode
SetViewportExtEx
SetViewportOrgEx
SetWindowExtEx
SetWindowOrgEx
SetWorldTransform

Créé avec HelpNDoc Personal Edition: Éditeur de documentation CHM facile


Cursors.rtf

+$#AK Cursors

A cursor is a small picture whose location on the screen is controlled by a pointing device, such as a mouse, pen, or trackball. (In the remainder of this section, the term mouse refers to any pointing device.) When the user moves the mouse, Microsoft® Windows® moves the cursor accordingly. Microsoft Win32® cursor functions enable applications to create, load, display, move, confine, and destroy cursors.

+$#A About Cursors

Windows provides a set of standard cursors that are available for any application to use at any time. The Windows header files contain identifiers for the standard cursors ⎯ the identifiers begin with the "IDC_" prefix.

Each standard cursor has a corresponding default image associated with it. The user or an application can replace the default image associated with any standard cursor at any time. An application replaces a default image by using the SetSystemCursor function.

An application can use the GetIconInfo function to retrieve the current image for a cursor, and can draw the cursor by using the DrawIconEx function. To draw the default image for a standard cursor, specify the DI_COMPAT flag in the call to DrawIconEx. If you do not specify the DI_COMPAT flag, DrawIconEx draws the standard cursor using the image that the user specified.

Custom cursors are designed for use in a specific application and can be any design the developer defines. The following illustration shows several custom cursors.

{ewc msdncd, EWGraphic, group10799 0 /a "SDK_02.bmp"}

Cursors can be either monochrome or color, and either static or animated. The type of cursor used on a particular computer system depends on the system's display. Old displays such as VGA do not support color or animated cursors, but new displays (whose display drivers use the DIB engine) do support them.

Cursors and icons are similar and can be used interchangeably in many situations. The only difference between them is that an image specified as a cursor must be in the format that the display can support. For example, a cursor must be monochrome for a VGA display.

+$#A The Hot Spot

In the cursor, a pixel called the hot spot marks the exact screen location that is affected by a mouse event, such as clicking a mouse button. Typically, the hot spot is the focal point of the cursor. The system tracks and recognizes this point as the position of the cursor. For example, typical hot spots are the pixel at the tip of an arrow-shaped cursor and the pixel in the middle of a crosshair-shaped cursor.

When a mouse input event occurs, the Windows mouse driver translates the event into an appropriate mouse message that includes the coordinates of the hot spot. Windows sends the mouse message to the window that contains the hot spot or to the window that is capturing mouse input. For more information about mouse input, see Mouse Input.

+$#A The Mouse and the Cursor

If the system includes a mouse, Windows reflects the movement of the mouse by moving the cursor on the screen accordingly. As the cursor moves over different parts of windows or into different windows, Windows (or an application) changes the appearance of the cursor. For example, when the cursor crosses a window border, Windows changes the cursor into a two-headed arrow.

If the system does not have a mouse, Windows displays and moves the cursor only when the user chooses certain system commands, such as those used to size or move a window. To provide the user with a method of displaying and moving the cursor when a mouse isn't available, an application can use the cursor functions to simulate mouse movement. Given this simulation capability, the user can use the arrow keys to move the cursor.

+$#A Cursor Creation

Because standard cursors are predefined, it is not necessary to create them. To use a standard cursor, an application retrieves a cursor handle by using the LoadCursor or LoadImage function. A cursor handle is a unique value of the HCURSOR type that identifies a standard or custom cursor.

To create a custom cursor for an application, developers typically use a graphics application and include the cursor as a resource in the application's resource-definition file. At run time, an application calls LoadCursor to retrieve the cursor handle. Cursor resources contain data for several different display devices. LoadCursor automatically selects the most appropriate data for the current display device.

An application can also create a custom cursor at run time by using the CreateIconIndirect function, which creates a cursor based on the content of an ICONINFO structure. The GetIconInfo function fills this structure with hot spot coordinates and information concerning the associated bitmask and color.

Applications should implement custom cursors as resources and use LoadCursor or LoadImage rather than create the cursor at run time. Using cursor resources avoids device dependence, simplifies localization, and enables applications to share cursor designs.

The CreateIconFromResourceEx function enables an application to create icons and cursors based on resource data. CreateIconFromResourceEx creates a cursor based on binary resource data from other executable (.EXE) files or dynamic-link libraries (DLLs). It must be preceded by calls to the LookupIconIdFromDirectoryEx function, as well as several resource functions. LookupIconIdFromDirectoryEx identifies the most appropriate cursor data for the current display device. For more information about resource functions, see Resources.

+$#A Cursor Location and Appearance

If the system includes a mouse, Windows automatically displays a cursor and updates its position on the screen. Applications can obtain current screen coordinates of the cursor; applications move the cursor to any location on the screen by using the GetCursorPos and SetCursorPos functions, respectively.

Applications can also retrieve the handle of the current cursor by using the GetCursor function and can set the cursor by using the SetCursor function. After an application uses SetCursor, the appearance of the cursor does not change until the mouse moves, until the cursor is explicitly set to a different cursor, or until a system command is executed.

When the user moves the mouse, the system redraws the cursor at the new location as designated by the mouse. The system automatically redraws the cursor design associated with the window to which the cursor is pointing.

Applications can hide and redisplay the cursor, without changing the cursor design, by using the ShowCursor function. This function uses an internal counter to determine when to hide or display the cursor. An attempt to show the cursor increments the counter; an attempt to hide the cursor decrements the counter. The cursor is visible only if this counter is greater than or equal to zero.

+$#A The Window Class Cursor

When registering a window class, an application can assign it a default cursor, known as the class cursor. After the application registers the window class, each window of that class will have the specified class cursor.

To override the class cursor, an application can process the WM_SETCURSOR message. Applications can also replace a class cursor by using the SetClassLong function. This function changes the default window settings for all windows of a given class. For more information about window classes, see Window Classes.

+$#A Cursor Confinement

Applications can confine the cursor to a rectangular area on the screen by using the ClipCursor function. This can be useful when the user must respond to a certain event within the confined area of the rectangle. For example, an application might use ClipCursor to confine the cursor to a modal dialog box, preventing the user from interacting with other windows until the dialog box is closed.

The GetClipCursor function retrieves the screen coordinates of the rectangular area to which the cursor is temporarily confined. When it is necessary to confine the cursor, the application can also use this function to save the coordinates of the original area in which the cursor can move. Then, the application can restore the cursor to the original area when the new confinement is no longer necessary.

+$#A Cursor Destruction

When an application no longer needs a cursor it created by using the CreateIconIndirect function, it should destroy the cursor. The DestroyCursor function destroys the cursor handle and frees any memory that it used. Applications should use this function only on cursors that were created with CreateIconIndirect; it is not necessary to destroy other cursors.

The DestroyCursor function has no effect on a shared cursor; that is, a cursor that was loaded by using the LR_SHARED flag with the LoadImage function. A shared cursor is valid as long as the module from which it was loaded remains in memory.

+$#A Cursor Duplication

The CopyCursor function copies a cursor handle. This enables an application or DLL to retrieve the handle of a cursor owned by another module. Then, if the other module is freed, the application that copied the cursor will still be able to use the cursor design.

For information on how to add, remove, or replace cursor resources in executable files, see Resources.

+$#A Using Cursors

• Creating a cursor

• Displaying a cursor

• Confining a cursor

• Using cursor functions to create a mousetrap

• Using the keyboard to move the cursor

+$#A Creating a Cursor

The following example creates two cursor handles: one for the standard hourglass cursor and one for a custom cursor included as a resource in the application's resource-definition file.

HINSTANCE hinst; /* handle of current instance */

HCURSOR hCurs1, hCurs2; /* cursor handles */

/* Create a standard hourglass cursor. */

hCurs1 = LoadCursor(NULL, IDC_WAIT);

/* Create a custom cursor based on a resource. */

hCurs2 = LoadCursor(hinst, MAKEINTRESOURCE(240));

An application should implement custom cursors as resources. Rather than create the cursors at run time, use the LoadCursor or LoadImage function to avoid device dependence, to simplify localization, and to enable applications to share cursor designs.

The following example uses the CreateCursor function to create a custom cursor at run time. The example is included here to illustrate how the system interprets cursor bitmasks. Even though this example uses the CreateCursor function, you should use the CreateIconIndirect function instead because it is more efficient.

HINSTANCE hinst; /* handle of current instance */

HCURSOR hCurs1, hCurs2; /* cursor handles */

HCURSOR hCurs3; /* cursor handle */

/* Yin cursor AND bitmask */

BYTE ANDmaskCursor[] = {

0xFF, 0xFC, 0x3F, 0xFF, /* line 1 */

0xFF, 0xC0, 0x1F, 0xFF, /* line 2 */

0xFF, 0x00, 0x3F, 0xFF, /* line 3 */

0xFE, 0x00, 0xFF, 0xFF, /* line 4 */

0xF7, 0x01, 0xFF, 0xFF, /* line 5 */

0xF0, 0x03, 0xFF, 0xFF, /* line 6 */

0xF0, 0x03, 0xFF, 0xFF, /* line 7 */

0xE0, 0x07, 0xFF, 0xFF, /* line 8 */

0xC0, 0x07, 0xFF, 0xFF, /* line 9 */

0xC0, 0x0F, 0xFF, 0xFF, /* line 10 */

0x80, 0x0F, 0xFF, 0xFF, /* line 11 */

0x80, 0x0F, 0xFF, 0xFF, /* line 12 */

0x80, 0x07, 0xFF, 0xFF, /* line 13 */

0x00, 0x07, 0xFF, 0xFF, /* line 14 */

0x00, 0x03, 0xFF, 0xFF, /* line 15 */

0x00, 0x00, 0xFF, 0xFF, /* line 16 */

0x00, 0x00, 0x7F, 0xFF, /* line 17 */

0x00, 0x00, 0x1F, 0xFF, /* line 18 */

0x00, 0x00, 0x0F, 0xFF, /* line 19 */

0x80, 0x00, 0x0F, 0xFF, /* line 20 */

0x80, 0x00, 0x07, 0xFF, /* line 21 */

0x80, 0x00, 0x07, 0xFF, /* line 22 */

0xC0, 0x00, 0x07, 0xFF, /* line 23 */

0xC0, 0x00, 0x0F, 0xFF, /* line 24 */

0xE0, 0x00, 0x0F, 0xFF, /* line 25 */

0xF0, 0x00, 0x1F, 0xFF, /* line 26 */

0xF0, 0x00, 0x1F, 0xFF, /* line 27 */

0xF8, 0x00, 0x3F, 0xFF, /* line 28 */

0xFE, 0x00, 0x7F, 0xFF, /* line 29 */

0xFF, 0x00, 0xFF, 0xFF, /* line 30 */

0xFF, 0xC3, 0xFF, 0xFF, /* line 31 */

0xFF, 0xFF, 0xFF, 0xFF}; /* line 32 */

/* Yin cursor XOR bitmask */

BYTE XORmaskCursor[] = {

0x00, 0x00, 0x00, 0x00, /* line 1 */

0x00, 0x03, 0xC0, 0x00, /* line 2 */

0x00, 0x3F, 0x00, 0x00, /* line 3 */

0x00, 0xFE, 0x00, 0x00, /* line 4 */

0x0E, 0xFC, 0x00, 0x00, /* line 5 */

0x07, 0xF8, 0x00, 0x00, /* line 6 */

0x07, 0xF8, 0x00, 0x00, /* line 7 */

0x0F, 0xF0, 0x00, 0x00, /* line 8 */

0x1F, 0xF0, 0x00, 0x00, /* line 9 */

0x1F, 0xE0, 0x00, 0x00, /* line 10 */

0x3F, 0xE0, 0x00, 0x00, /* line 11 */

0x3F, 0xE0, 0x00, 0x00, /* line 12 */

0x3F, 0xF0, 0x00, 0x00, /* line 13 */

0x7F, 0xF0, 0x00, 0x00, /* line 14 */

0x7F, 0xF8, 0x00, 0x00, /* line 15 */

0x7F, 0xFC, 0x00, 0x00, /* line 16 */

0x7F, 0xFF, 0x00, 0x00, /* line 17 */

0x7F, 0xFF, 0x80, 0x00, /* line 18 */

0x7F, 0xFF, 0xE0, 0x00, /* line 19 */

0x3F, 0xFF, 0xE0, 0x00, /* line 20 */

0x3F, 0xC7, 0xF0, 0x00, /* line 21 */

0x3F, 0x83, 0xF0, 0x00, /* line 22 */

0x1F, 0x83, 0xF0, 0x00, /* line 23 */

0x1F, 0x83, 0xE0, 0x00, /* line 24 */

0x0F, 0xC7, 0xE0, 0x00, /* line 25 */

0x07, 0xFF, 0xC0, 0x00, /* line 26 */

0x07, 0xFF, 0xC0, 0x00, /* line 27 */

0x01, 0xFF, 0x80, 0x00, /* line 28 */

0x00, 0xFF, 0x00, 0x00, /* line 29 */

0x00, 0x3C, 0x00, 0x00, /* line 30 */

0x00, 0x00, 0x00, 0x00, /* line 31 */

0x00, 0x00, 0x00, 0x00}; /* line 32 */

/* Create a custom cursor at run time. */

hCurs3 = CreateCursor(hinst, /* app instance */

19, /* horiz pos of hot spot */

2, /* vert pos of hot spot */

32, /* cursor width */

32, /* cursor height */

ANDmaskCursor, /* AND bitmask */

XORmaskCursor); /* XOR bitmask */

To create the cursor, CreateCursor applies the following truth table to the AND and XOR bitmasks.

AND bitmask | XOR bitmask | Display
0 | 0 | Black
0 | 1 | White
1 | 0 | Screen
1 | 1 | Reverse screen

For more information about bitmaps, see Bitmaps.

Before closing, your application must use the DestroyCursor function to destroy any cursors it created with CreateCursor. It is not necessary to destroy cursors created by other functions.

+$#A Displaying a Cursor

If the system includes a mouse, Windows automatically displays the class cursor (the cursor associated with the window to which the cursor is pointing). Your application can assign a class cursor while registering a window class. The following example illustrates this by assigning a cursor handle to the hCursor member of the WNDCLASS structure identified by the wc parameter.

WNDCLASS wc;

/*

* Fill the window class structure with parameters that describe the main window.

*/

wc.style = NULL; /* class style(s) */

wc.lpfnWndProc = (WNDPROC) MainWndProc; /* window proc. */

wc.cbClsExtra = 0; /* no per-class extra data */

wc.cbWndExtra = 0; /* no per-window extra data */

wc.hInstance = hinstance; /* app that owns the class */

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* class icon */

wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(230));

/* class cursor */

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

/* class background */

wc.lpszMenuName = "GenericMenu"; /* class menu */

wc.lpszClassName = "GenericWClass" /* class name */

/* Register the window class. */

return RegisterClass(&wc);

When the window class is registered, the cursor identified by 230 in the application's resource-definition file will be the default cursor for all windows based on the class.

Your application can change the design of the cursor by using the SetCursor function and specifying a different cursor handle. However, when the cursor moves, Windows redraws the class cursor at the new location. To prevent the class cursor from being redrawn, your application must process the WM_SETCURSOR message. Each time the cursor moves and mouse input is not captured, Windows sends this message to the window in which the cursor is moving.

You can specify different cursors for different conditions while processing WM_SETCURSOR. For example, the following example shows how to display the hCurs3 cursor whenever the cursor moves over the icon of a minimized application.

case WM_SETCURSOR:

/*

* If the window is minimized, draw the hCurs3 cursor.

* If the window is not minimized, draw the default

* cursor (class cursor).

*/

if (IsIconic(hwnd)) {

SetCursor(hCurs3);

break;

}

When the window is not minimized, Windows displays the class cursor.

Your application can replace a class cursor by using the SetClassLong function. This function changes the default window settings for all windows of a given class. The following example replaces the existing class cursor with the hCurs2 cursor.

/* Change the cursor for hwnd's window class. */

SetClassLong(hwnd, /* window handle */

GCL_HCURSOR, /* change cursor */

(LONG) hCurs2); /* new cursor */

For more information about window classes and mouse input, see Window Classes and Mouse Input.

+$#A Confining a Cursor

The following example confines the cursor to the application's window and then restores the cursor to its previous window. The example uses the GetClipCursor function to record the area in which the cursor can move and the ClipCursor function to confine and restore the cursor.

RECT rcClip; /* new ClipCursor area */

RECT rcOldClip; /* previous ClipCursor area */

/* Record the area in which the cursor can move. */

GetClipCursor(&rcOldClip);

/* Get the dimensions of the application's window. */

GetWindowRect(hwnd, &rcClip);

/* Confine the cursor to the application's window. */

ClipCursor(&rcClip);

.

. /* Process input from the confined cursor. */

.

/* Restore the cursor to its previous area. */

ClipCursor(&rcOldClip);

Because there is only one cursor at a time available in the system, an application that confines the cursor must restore the cursor before relinquishing control to another window.

+$#A Using Cursor Functions to Create a Mousetrap

The following example uses the SetCursorPos, GetCursorPos, CreateCursor, LoadCursor, and SetCursor functions to create a simple mousetrap. It also uses cursor and timer functions to monitor the cursor's position every 10 seconds. If the cursor position has not changed in the last 10 seconds and the application's main window is minimized, the application changes the cursor and moves it to the mousetrap icon.

An example for a similar mousetrap is included in Icons. It uses the LoadCursor and LoadIcon functions instead of the more device-dependent CreateCursor and CreateIcon functions.

HICON hIcon1; /* icon handles */

POINT ptOld; /* previous cursor location */

HCURSOR hCurs1; /* cursor handle */

/*

* The following cursor bitmasks are defined in a code

* example that appears earlier in this topic.

*/

/* Yin cursor AND and XOR bitmasks */

BYTE ANDmaskCursor[] = ...

BYTE XORmaskCursor[] = ...

/* Yang icon AND bitmask */

BYTE ANDmaskIcon[] = {0xFF, 0xFF, 0xFF, 0xFF, /* line 1 */

0xFF, 0xFF, 0xC3, 0xFF, /* line 2 */

0xFF, 0xFF, 0x00, 0xFF, /* line 3 */

0xFF, 0xFE, 0x00, 0x7F, /* line 4 */

0xFF, 0xFC, 0x00, 0x1F, /* line 5 */

0xFF, 0xF8, 0x00, 0x0F, /* line 6 */

0xFF, 0xF8, 0x00, 0x0F, /* line 7 */

0xFF, 0xF0, 0x00, 0x07, /* line 8 */

0xFF, 0xF0, 0x00, 0x03, /* line 9 */

0xFF, 0xE0, 0x00, 0x03, /* line 10 */

0xFF, 0xE0, 0x00, 0x01, /* line 11 */

0xFF, 0xE0, 0x00, 0x01, /* line 12 */

0xFF, 0xF0, 0x00, 0x01, /* line 13 */

0xFF, 0xF0, 0x00, 0x00, /* line 14 */

0xFF, 0xF8, 0x00, 0x00, /* line 15 */

0xFF, 0xFC, 0x00, 0x00, /* line 16 */

0xFF, 0xFF, 0x00, 0x00, /* line 17 */

0xFF, 0xFF, 0x80, 0x00, /* line 18 */

0xFF, 0xFF, 0xE0, 0x00, /* line 19 */

0xFF, 0xFF, 0xE0, 0x01, /* line 20 */

0xFF, 0xFF, 0xF0, 0x01, /* line 21 */

0xFF, 0xFF, 0xF0, 0x01, /* line 22 */

0xFF, 0xFF, 0xF0, 0x03, /* line 23 */

0xFF, 0xFF, 0xE0, 0x03, /* line 24 */

0xFF, 0xFF, 0xE0, 0x07, /* line 25 */

0xFF, 0xFF, 0xC0, 0x0F, /* line 26 */

0xFF, 0xFF, 0xC0, 0x0F, /* line 27 */

0xFF, 0xFF, 0x80, 0x1F, /* line 28 */

0xFF, 0xFF, 0x00, 0x7F, /* line 29 */

0xFF, 0xFC, 0x00, 0xFF, /* line 30 */

0xFF, 0xF8, 0x03, 0xFF, /* line 31 */

0xFF, 0xFC, 0x3F, 0xFF}; /* line 32 */

/* Yang icon XOR bitmask */

BYTE XORmaskIcon[] = {0x00, 0x00, 0x00, 0x00, /* line 1 */

0x00, 0x00, 0x00, 0x00, /* line 2 */

0x00, 0x00, 0x00, 0x00, /* line 3 */

0x00, 0x00, 0x00, 0x00, /* line 4 */

0x00, 0x00, 0x00, 0x00, /* line 5 */

0x00, 0x00, 0x00, 0x00, /* line 6 */

0x00, 0x00, 0x00, 0x00, /* line 7 */

0x00, 0x00, 0x38, 0x00, /* line 8 */

0x00, 0x00, 0x7C, 0x00, /* line 9 */

0x00, 0x00, 0x7C, 0x00, /* line 10 */

0x00, 0x00, 0x7C, 0x00, /* line 11 */

0x00, 0x00, 0x38, 0x00, /* line 12 */

0x00, 0x00, 0x00, 0x00, /* line 13 */

0x00, 0x00, 0x00, 0x00, /* line 14 */

0x00, 0x00, 0x00, 0x00, /* line 15 */

0x00, 0x00, 0x00, 0x00, /* line 16 */

0x00, 0x00, 0x00, 0x00, /* line 17 */

0x00, 0x00, 0x00, 0x00, /* line 18 */

0x00, 0x00, 0x00, 0x00, /* line 19 */

0x00, 0x00, 0x00, 0x00, /* line 20 */

0x00, 0x00, 0x00, 0x00, /* line 21 */

0x00, 0x00, 0x00, 0x00, /* line 22 */

0x00, 0x00, 0x00, 0x00, /* line 23 */

0x00, 0x00, 0x00, 0x00, /* line 24 */

0x00, 0x00, 0x00, 0x00, /* line 25 */

0x00, 0x00, 0x00, 0x00, /* line 26 */

0x00, 0x00, 0x00, 0x00, /* line 27 */

0x00, 0x00, 0x00, 0x00, /* line 28 */

0x00, 0x00, 0x00, 0x00, /* line 29 */

0x00, 0x00, 0x00, 0x00, /* line 30 */

0x00, 0x00, 0x00, 0x00, /* line 31 */

0x00, 0x00, 0x00, 0x00}; /* line 32 */

hIcon1 = CreateIcon(hinstance, /* handle of app instance */

32, /* icon width */

32, /* icon height */

1, /* number of XOR planes */

1, /* number of bits per pixel */

ANDmaskIcon, /* AND bitmask */

XORmaskIcon); /* XOR bitmask */

hCurs1 = CreateCursor(hinstance, /* handle of app inst. */

19, /* horiz. position of hot spot */

2, /* vert. position of hot spot */

32, /* cursor width */

32, /* cursor height */

ANDmaskCursor, /* AND bitmask */

XORmaskCursor); /* XOR bitmask */

.

. /* Fill in the window class structure. */

.

WNDCLASS wc;

wc.hIcon = hIcon1; /* class icon */

wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* class cursor */

.

. /*

. * Register the window class and perform

. * other application initialization.

. */

.

/* Set a timer for the mousetrap. */

GetCursorPos(&ptOld);

SetTimer(hwnd, IDT_CURSOR, 10000, (TIMERPROC) NULL);

LONG APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

HDC hdc; /* handle of device context */

POINT pt; /* current cursor location */

RECT rc; /* iconized window location */

switch (message) {

.

. /* Process other messages. */

.

case WM_TIMER:

/*

* If the window is minimized, compare the

* current cursor position with the one 10

* seconds before. If the cursor position has

* not changed, move the cursor to the icon.

*/

if (IsIconic(hwnd)) {

GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {

GetWindowRect(hwnd, &rc);

SetCursorPos(rc.left + 20, rc.top + 4);

/*

* Note that the additional constants

* (20 and 4) are application-specific

* values to align the yin-shaped cursor

* and the yang-shaped icon.

*/

}

else {

ptOld.x = pt.x;

ptOld.y = pt.y;

}

}

return 0;

case WM_SETCURSOR:

/*

* If the window is minimized, draw hCurs1.

* If the window is not minimized, draw the

* default * cursor (class cursor).

*/

if (IsIconic(hwnd)) {

SetCursor(hCurs1);

break;

}

case WM_DESTROY:

/* Destroy timer. */

KillTimer(hwnd, IDT_CURSOR);

PostQuitMessage(0);

break;

.

. /* Process other messages. */

.

} /* ends switch (message) */

+$#A Using the Keyboard to Move the Cursor

Because Windows does not require a mouse, an application should be able to simulate mouse actions with the keyboard. The following example shows how to achieve this, by using the GetCursorPos and SetCursorPos functions, and by processing input from the arrow keys.

HCURSOR hCurs1, hCurs2; /* cursor handles */

POINT pt; /* cursor location */

RECT rc; /* client area coordinates */

static int repeat = 1; /* repeat key counter */

.

. /* Other declarations and initialization. */

.

switch (message) {

.

. /* Process other messages. */

.

case WM_KEYDOWN:

if (wParam != VK_LEFT && wParam != VK_RIGHT &&

wParam != VK_UP && wParam != VK_DOWN) {

break;

}

GetCursorPos(&pt);

/*

*Convert screen coordinates to client

* coordinates.

*/

ScreenToClient(hwnd, &pt);

switch (wParam) {

/*

* Move the cursor to reflect which

* arrow keys are pressed.

*/

case VK_LEFT: /* left arrow */

pt.x -= repeat;

break;

case VK_RIGHT: /* right arrow */

pt.x += repeat;

break;

case VK_UP: /* up arrow */

pt.y -= repeat;

break;

case VK_DOWN: /* down arrow */

pt.y += repeat;

break;

default:

return NULL;

}

repeat++; /* increments repeat count */

/* Keep the cursor in the client area. */

GetClientRect(hwnd, &rc);

if (pt.x >= rc.right) {

pt.x = rc.right - 1;

}

else {

if (pt.x < rc.left) {

pt.x = rc.left;

}

}

if (pt.y >= rc.bottom) {

pt.y = rc.bottom - 1;

}

else {

if (pt.y < rc.top) {

pt.y = rc.top;

}

}

/*

* Convert client coordinates to screen

*coordinates.

*/

ClientToScreen(hwnd, &pt);

SetCursorPos(pt.x, pt.y);

break;

case WM_KEYUP:

repeat = 1; /* clears repeat count */

break;

.

.

.

} /* ends switch (message) */

+$#AK Cursor Reference

The following functions and messages are associated with cursors.

+$#A Cursor Functions

Following are the functions used with cursors.

ClipCursor
CopyCursor
CreateCursor
DestroyCursor
GetClipCursor
GetCursor
GetCursorPos
LoadCursor
LoadCursorFromFile
SetCursor
SetCursorPos
SetSystemCursor
ShowCursor

+$#A Cursor Messages

The following message is used with cursors.

WM_SETCURSOR

Créé avec HelpNDoc Personal Edition: Générateur de documentations PDF gratuit


Data Decompression Library.rtf

+$#AK Data Decompression Library

This overview describes important concepts relating to data compression and describes the decompression functions in LZEXPAND.DLL.

+$#A About the Data Decompression Library

The Microsoft® Windows® operating system includes a dynamic-link library (DLL) named LZEXPAND.DLL. Typically, an application calls functions in LZEXPAND.DLL to decompress data previously compressed by using the Microsoft File Compression Utility (COMPRESS.EXE).

+$#A Data Compression

Data compression is an operation that reduces the size of a file by minimizing redundant data. In a file that contains text, redundant data can be frequently occurring characters, such as the space character, or common vowels, such as the letters e and a; it can also be frequently occurring character strings. Data compression creates a compressed version of a file by minimizing this redundant data.

Each type of data-compression algorithm minimizes redundant data in a unique manner. For example, the Huffman encoding algorithm assigns a code to characters in a file based on how frequently those characters occur. Another compression algorithm, called run-length encoding, generates a two-part value for repeated characters: the first part specifies the number of times the character is repeated, and the second part identifies the character. Another compression algorithm, known as the Lempel-Ziv algorithm, converts variable-length strings into fixed-length codes that consume less space than the original strings.

To compress large applications or data files, you can run COMPRESS.EXE from the command line. COMPRESS.EXE uses the Lempel-Ziv compression algorithm.

+$#A Data Decompression

Applications can call the functions in LZEXPAND.DLL to decompress files compressed by using COMPRESS.EXE. The functions can also process uncompressed files without attempting to decompress them. To use these functions, applications must include the LZEXPAND.H header file.

The following table describes each function found in LZEXPAND.DLL.

Function | Purpose
CopyLZFile | This function is obsolete. It is provided to simplify porting of 16-bit Windows-based applications. Win32-based applications should use the LZCopy function.
GetExpandedName | Retrieves the original name of a compressed file, if the /r option was used during compression of the file.
LZClose | Closes a file that was opened when the application called the LZOpenFile function.
LZCopy | Copies a source file to a destination file. If the source file is compressed, this function creates a decompressed destination file. If the source file is not compressed, this function duplicates the original file. This function is intended for single-file copy operations.
LZDone | This function is obsolete and should not be used in Win32-based applications. It is provided to simplify porting of 16-bit Windows-based applications.
LZInit | Creates structures used for decompressing files.
LZOpenFile | Opens a file and returns a handle that identifies it.
LZRead | Reads a specified number of bytes from a file. If the file is compressed, this function decompresses the bytes before copying them to the destination buffer.
LZSeek | Positions the file pointer within the decompressed image of a compressed file. The application calls this function to position the pointer before calling the LZRead function.
LZStart | This function is obsolete and should not be used in Win32-based applications. It is provided to simplify porting of 16-bit Windows-based applications.

+$#A Using the Data Decompression Library

Functions in LZEXPAND.DLL can be used to decompress single or multiple files. They can also be used to decompress compressed files a portion at a time.

• Decompressing a single file

• Decompressing multiple files

• Reading bytes from compressed files

+$#A Decompressing a Single File

An application can decompress a single compressed file by performing the following tasks.

1. Open the source file by calling the LZOpenFile function.

2. Open the destination file by calling LZOpenFile.

3. Copy the source file to the destination file by calling the LZCopy function and passing the handles returned by LZOpenFile.

4. Close the files by calling the LZClose function.

+$#A Decompressing Multiple Files

An application can decompress multiple files by performing the following tasks.

1. Open the source files by calling the LZOpenFile function.

2. Open the destination files by calling LZOpenFile.

3. Copy the source files to the destination files by calling the LZCopy function.

4. Close the files by calling the LZClose function.

+$#A Reading Bytes from Compressed Files

In addition to decompressing a complete file in a single operation, an application can decompress a compressed file a portion at a time by using the LZSeek and LZRead functions. These functions are particularly useful when it is necessary to extract parts of large files. For example, a font manufacturer may have compressed files containing font metrics in addition to character data. To use the information in these files, an application would need to decompress the file; however, most applications would use only part of the file at any particular time. To get information about font metrics, the application would extract data from the header. To get information from the text, the application would reposition the file pointer by calling LZSeek and extract character data by calling LZRead.

+$#A Data Decompression Library Functions

Following are the functions used to decompress files.

GetExpandedName
LZClose
LZCopy
LZInit
LZOpenFile
LZRead
LZSeek

Obsolete or Deleted Functions

CopyLZFile
LZDone
LZStart

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


Debugging.rtf

+$#AK Debugging

A debugger is an application that enables a developer to observe and correct programming errors.

The first section of this overview describes the debugging-specific features of certain standard Windows functions ⎯ specifically, process, thread, and exception-handling functions.

The second section of this overview describes the Windows debugging functions. These functions enable an application to wait for debugging events, cause breakpoint exceptions, transfer execution control to the debugger, and so on.

+$#A About Debugging Support

Microsoft® Windows® supports several functions that can be used to create a basic, event-driven debugger. "Event-driven" means that the debugger is notified every time certain events occur in the process being debugged. Notification enables the debugger to take appropriate action in response to the events.

+$#A Support from Process, Thread, and Exception Functions

Some functions essential to debugging are actually part of the process, thread, or exception-handling architectures. This section describes how to start a process that is going to be debugged and how to examine and manipulate the context and execution of a thread. It also describes how a debugger should handle exceptions.

+$#A Process Functions

The CreateProcess function enables a debugger to start a process and debug it. The fdwCreate parameter of CreateProcess is used to specify the type of debugging operation. If the DEBUG_PROCESS flag is specified for the parameter, a debugger debugs the new process and all of the process's descendants, provided that the descendants are created without the DEBUG_PROCESS flag.

If the DEBUG_PROCESS and DEBUG_ONLY_THIS_PROCESS flags are specified for fdwCreate, a debugger debugs the new process but none of its descendants.

One debugger can debug another by creating a process with the DEBUG_PROCESS flag. The new process (the debugger being debugged) must then create a process with the DEBUG_PROCESS flag.

The OpenProcess function enables a debugger to obtain the identifier of an existing process. (The DebugActiveProcess function uses this identifier to attach the debugger to the process.) Typically, debuggers open a process with the PROCESS_VM_READ and PROCESS_VM_WRITE flags. Using these flags enables the debugger to read from and write to the virtual memory of the process by using the ReadProcessMemory and WriteProcessMemory functions. For more information about processes, see Processes and Threads.

+$#A Thread Functions

The CreateThread function creates a new thread for a process. Debuggers typically need to examine or change the contents of a thread's registers. To accomplish this, a debugger must obtain a handle of the thread by using the DuplicateHandle function and specifying the appropriate access to the thread (THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, or both).

A process with appropriate access to a thread can examine the thread's registers by using the GetThreadContext function and set the contents of the thread's registers by using the SetThreadContext function.

A process can also obtain THREAD_SUSPEND_RESUME access to a thread. This type of access enables a debugger to control a thread's execution with the SuspendThread and ResumeThread functions. For more information about threads, see Processes and Threads.

+$#A Exception Handling for Debugging

When an exception occurs in a process that is being debugged, the kernel notifies the debugger by passing the exception to it. This is known as first-chance notification. The kernel then suspends all threads in the process being debugged.

If the debugger does not handle the exception, the kernel attempts to locate an appropriate exception handler. If the kernel cannot locate an appropriate one, the kernel again notifies the debugger that an exception has occurred. This is known as last-chance notification. If the debugger does not handle the exception after the last-chance notification, the kernel terminates the process being debugged.

For more information about exception handling, see Structured Exception Handling.

+$#A Debugging Functions

Although most of the Windows debugging functions are used to create a debugger, several functions are designed for use in the process being debugged.

To start a process and debug it, a debugger must use CreateProcess, as described in the previous section.

To debug a process that is already executing, the debugger should use DebugActiveProcess with the process identifier retrieved by OpenProcess. DebugActiveProcess attaches the debugger to the active process. In this case, only the active process can be debugged; its descendants cannot. The debugger must have appropriate access to the executing process to use DebugActiveProcess. For more information about access rights, see Security.

After the debugger has either created or attached itself to the process it intends to debug, the kernel notifies the debugger of all debugging events that occur in the process, and, if specified, in the process's descendants. For more information about debugging events, see Debugging Events.

The debugger uses the WaitForDebugEvent function at the beginning of its main loop. This function blocks the debugger until a debugging event occurs. When the debugging event occurs, the kernel suspends all threads in the process being debugged and notifies the debugger of the event. The debugger can interact with the user, or manipulate the state of the process being debugged, by using the FlushInstructionCache, GetThreadContext, GetThreadSelectorEntry, ReadProcessMemory, SetThreadContext, and WriteProcessMemory functions.

GetThreadSelectorEntry returns the descriptor table entry for a specified selector and thread. Debuggers use the descriptor table entry to convert a segment-relative address to a linear virtual address. The ReadProcessMemory and WriteProcessMemory functions require linear virtual addresses.

Debuggers frequently read the memory of the process being debugged and write the memory that contains instructions to the instruction cache. After the instructions are written, the debugger calls FlushInstructionCache to execute the cached instructions.

The debugger uses the ContinueDebugEvent function at the end of its main loop. This function allows the process being debugged to continue executing.

+$#A Communicating with the Debugger

The OutputDebugString function sends a string from the process being debugged to the debugger by generating an OUTPUT_DEBUG_STRING_EVENT debugging event.

The DebugBreak function causes a breakpoint exception in the current process. A breakpoint is a location in a program where execution is stopped to allow the developer to examine the program's code, variables, and register values and, as necessary, to make changes, continue execution, or terminate execution.

The FatalExit function terminates the current process and gives execution control to the debugger, but unlike DebugBreak, it does not generate an exception. This function should only be used as a last resort, because it does not always free the process's memory or close its files.

+$#A Debugging Events

A debugging event is an incident in the process being debugged that causes the kernel to notify the debugger. Debugging events include creating a process, creating a thread, loading a dynamic-link library (DLL), unloading a DLL, sending an output string, and generating an exception.

If a debugging event occurs while a debugger is waiting for one, the kernel fills the DEBUG_EVENT structure specified by WaitForDebugEvent with information describing the event.

When the kernel notifies the debugger of a debugging event, it also suspends all threads in the affected process. The threads do not resume execution until the debugger continues the debugging event by using ContinueDebugEvent. The following debugging events may occur while a process is being debugged.

Debugging event

Description

CREATE_PROCESS_DEBUG_EVENT

Generated whenever a new process is created in a process being debugged or whenever the debugger begins debugging an already active process. The kernel generates this debugging event before the process begins to execute in user mode and before the kernel generates any other debugging events for the new process.

The DEBUG_EVENT structure contains a CREATE_PROCESS_DEBUG_INFO structure. This structure includes a handle of the new process, a handle of the process's image file, a handle of the process's initial thread, and other information that describes the new process.

The handle of the process has PROCESS_VM_READ and PROCESS_VM_WRITE access. If a debugger has these types of access to a thread, it can read and write to the process's memory by using the ReadProcessMemory and WriteProcessMemory functions.

The handle of the process's image file has GENERIC_READ access and is opened for read-sharing.

The handle of the process's initial thread has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. If a debugger has these types of access to a thread, it can read from and write to the thread's registers by using the GetThreadContext and SetThreadContext functions and can suspend and resume the thread by using the SuspendThread and ResumeThread functions.

CREATE_THREAD_DEBUG_EVENT

Generated whenever a new thread is created in a process being debugged or whenever the debugger begins debugging an already active process. This debugging event is generated before the new thread begins to execute in user mode.

The DEBUG_EVENT structure contains a CREATE_THREAD_DEBUG_INFO structure. This structure includes a handle of the new thread and the thread's starting address. The handle has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. If a debugger has these types of access to a thread, it can read from and write to the thread's registers by using the GetThreadContext and SetThreadContext functions and can suspend and resume the thread by using the SuspendThread and ResumeThread functions.

EXCEPTION_DEBUG_EVENT

Generated whenever an exception occurs in the process being debugged. Possible exceptions include attempting to access inaccessible memory, executing breakpoint instructions, attempting to divide by zero, or any other exception noted in Structured Exception Handling.

The DEBUG_EVENT structure contains an EXCEPTION_DEBUG_INFO structure. This structure describes the exception that caused the debugging event.

Besides the standard exception conditions, an additional exception code can occur during console process debugging. The kernel generates a DBG_CONTROL_C exception code when CTRL+C is input to a console process that handles CTRL+C signals and is being debugged. This exception code is not meant to be handled by applications. An application should never use an exception handler to deal with it. It is raised only for the benefit of the debugger and is only used when a debugger is attached to the console process.

If a process is not being debugged or if the debugger passes on the DBG_CONTROL_C exception unhandled (through the gn command), the application's list of handler functions is searched, as documented for the SetConsoleCtrlHandler function.

If the debugger handles the DBG_CONTROL_C exception (through the gh command), an application will not notice the CTRL+C except in code like this.

while ((inputChar = getchar()) != EOF) ...

while (gets(inputString)) ...

Thus, the debugger cannot be used to stop the read wait in such code from terminating.

EXIT_PROCESS_DEBUG_EVENT

Generated whenever the last thread in a process being debugged exits. This debugging event occurs immediately after the kernel unloads the process's DLLs and updates the process's exit code.

The DEBUG_EVENT structure contains an EXIT_PROCESS_DEBUG_INFO structure that specifies the exit code.

The debugger deallocates any internal structures associated with the process on receipt of this debugging event. The kernel closes the debugger's handle of the exiting process and all of the process's threads.

EXIT_THREAD_DEBUG_EVENT

Generated whenever a thread that is part of a process being debugged exits. The kernel generates this debugging event immediately after it updates the thread's exit code.

The DEBUG_EVENT structure contains an EXIT_THREAD_DEBUG_INFO structure that specifies the exit code.

The debugger deallocates any internal structures associated with the thread on receipt of this debugging event. The system closes the debugger's handle of the exiting thread.

This debugging event does not occur if the exiting thread is the last thread of a process. In this case, the EXIT_PROCESS_DEBUG_EVENT debugging event occurs instead.

LOAD_DLL_DEBUG_EVENT

Generated whenever a process being debugged loads a DLL. This debugging event occurs when the system loader resolves links to a DLL or when the debugged process uses the LoadLibrary function. This debugging event only occurs the first time the kernel attaches a DLL to the virtual address space of a process.

The DEBUG_EVENT structure contains a LOAD_DLL_DEBUG_INFO structure. This structure includes a handle of the newly loaded DLL, the base address of the DLL, and other information that describes the DLL.

Typically, a debugger loads a symbol table associated with the DLL on receipt of this debugging event.

OUTPUT_DEBUG_STRING_EVENT

Generated when a process being debugged uses the OutputDebugString function.

The DEBUG_EVENT structure contains an OUTPUT_DEBUG_STRING_INFO structure. This structure specifies the address, length, and format of the debugging string.

UNLOAD_DLL_DEBUG_EVENT

Generated whenever a process being debugged unloads a DLL by using the FreeLibrary function. This debugging event only occurs the last time a DLL is unloaded from a process's address space (that is, when the DLL's usage count is zero).

The DEBUG_EVENT structure contains an UNLOAD_DLL_DEBUG_INFO structure. This structure specifies the base address of the DLL in the address space of the process that unloads the DLL.

Typically, a debugger unloads a symbol table associated with the DLL upon receiving this debugging event.

When a process exits, the kernel automatically unloads the process's DLLs, but does not generate an UNLOAD_DLL_DEBUG_EVENT debugging event.

+$#A Using Debugging Support

The following example uses the WaitForDebugEvent and ContinueDebugEvent functions to illustrate how a simple debugger might be organized.

DEBUG_EVENT DebugEv; /* debugging event info. */

DWORD dwContinueStatus = DBG_CONTINUE; /* exception continuation */

for(;;) {

/*

* Wait for a debugging event to occur. The second parameter

* indicates that the function does not return until

* a debugging event occurs.

*/

WaitForDebugEvent(&DebugEv, INFINITE);

/* Process the debugging event code. */

switch (DebugEv.dwDebugEventCode) {

case EXCEPTION_DEBUG_EVENT:

/*

* Process the exception code. When handling

* exceptions, remember to set the continuation

* status parameter (dwContinueStatus). This value

* is used by the ContinueDebugEvent function.

*/

switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) {

case EXCEPTION_ACCESS_VIOLATION:

/*

* First chance: Pass this on to the kernel.

* Last chance: Display an appropriate error.

*/

case EXCEPTION_BREAKPOINT:

/*

* First chance: Display the current

* instruction and register values.

*/

case EXCEPTION_DATATYPE_MISALIGNMENT:

/*

* First chance: Pass this on to the kernel.

* Last chance: Display an appropriate error.

*/

case EXCEPTION_SINGLE_STEP:

/*

* First chance: Update the display of the

* current instruction and register values.

*/

case DBG_CONTROL_C:

/*

* First chance: Pass this on to the kernel.

* Last chance: Display an appropriate error.

*/

/* Handle other exceptions. */

}

case CREATE_THREAD_DEBUG_EVENT:

/*

* As needed, examine or change the thread's registers

* with the GetThreadContext and SetThreadContext functions;

* and suspend and resume thread execution with the

* SuspendThread and ResumeThread functions.

*/

case CREATE_PROCESS_DEBUG_EVENT:

/*

* As needed, examine or change the registers of the

* process's initial thread with the GetThreadContext and

* SetThreadContext functions; read from and write to the

* process's virtual memory with the ReadProcessMemory and

* WriteProcessMemory functions; and suspend and resume

* thread execution with the SuspendThread and ResumeThread

* functions.

*/

case EXIT_THREAD_DEBUG_EVENT:

/* Display the thread's exit code. */

case EXIT_PROCESS_DEBUG_EVENT:

/* Display the process's exit code. */

case LOAD_DLL_DEBUG_EVENT:

/*

* Read the debugging information included

* in the newly loaded DLL.

*/

case UNLOAD_DLL_DEBUG_EVENT:

/*

* Display a message that the DLL has

* been unloaded.

*/

case OUTPUT_DEBUG_STRING_EVENT:

/* Display the output debugging string. */

}

/* Resume executing the thread that reported the debugging event. */

ContinueDebugEvent(DebugEv.dwProcessId,

DebugEv.dwThreadId, dwContinueStatus);

}

+$#A Debugging Functions

Following are the functions that support debugging:

ContinueDebugEvent
DebugActiveProcess
DebugBreak
FatalAppExit
FatalExit
FlushInstructionCache
GetThreadContext
GetThreadSelectorEntry
OutputDebugString
ReadProcessMemory
SetDebugErrorLevel
SetThreadContext
WaitForDebugEvent
WriteProcessMemory

Créé avec HelpNDoc Personal Edition: Environnement de création d'aide complet


Device Contexts.rtf

+$#AK Device Contexts

A device context is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output. The graphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations.

+$#A About Device Contexts

One of the chief features of the Microsoft® Win32® application programming interface (API) is device independence. Win32-based applications can draw and print output on a variety of devices. The software that supports this device independence is contained in two dynamic-link libraries. The first, GDI.DLL, is referred to as the graphics device interface (GDI); the second is referred to as a device driver. The name of the second depends on the device where the application draws output. For example, if the application draws output in the client area of its window on a VGA display, this library is VGA.DLL; if the application prints output on an Epson® FX-80 printer, this library is EPSON9.DLL.

An application must inform GDI to load a particular device driver and, once the driver is loaded, to prepare the device for drawing operations (such as selecting a line color and width, a brush pattern and color, a font typeface, a clipping region, and so on). These tasks are accomplished by creating and maintaining a device context. A device context is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output. The graphic objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations, and a path for painting and drawing operations. Unlike most of the Win32 structures, an application never has direct access to the device context; instead, it operates on the structure indirectly by calling various functions.

+$#A Graphic Objects

The pen, brush, bitmap, palette, region, and path associated with a device context are referred to as the device context's graphic objects. Following are the attributes associated with each of these objects.

Graphic object | Associated attributes
Bitmap | Size, in bytes; dimensions, in pixels; color-format; compression scheme; and so on
Brush | Style, color, pattern, and origin
Palette | Colors and size (or number of colors)
Font | Typeface name, width, height, weight, character set, and so on
Path | Shape
Pen | Style, width, and color
Region | Location and dimensions

When an application creates a device context, Windows automatically stores a set of default objects in it. (There is no default bitmap or path.) An application can examine the attributes of the default objects by calling the GetCurrentObject and GetObject functions. The application can change these defaults by creating a new object and selecting it into the device context. An object is selected into a device context by calling the SelectObject function.

+$#A Graphic Modes

Windows supports five graphic modes that allow an application to specify how colors are mixed, where output appears, how the output is scaled, and so on. These modes, which are stored in a device context, are described in the following table.

Mode | Description
Background mode | Defines how background colors are mixed with existing window or screen colors for bitmap and text operations.
Drawing mode | Defines how foreground colors are mixed with existing window or screen colors for pen, brush, bitmap, and text operations.
Mapping mode | Defines how graphics output is mapped from logical (or world) space onto the window, screen, or printer paper.
Polygon-fill mode | Defines how the brush pattern is used to fill the interior of complex regions.
Stretching mode | Defines how bitmap colors are mixed with existing window or screen colors when the bitmap is compressed (or scaled down).

As it does with graphic objects, Windows initializes a device context with default graphic modes. An application can retrieve and examine these default modes by calling the following functions.

Graphic mode | Function
Background mode | GetBkMode
Drawing mode | GetROP2
Mapping mode | GetMapMode
Polygon-fill mode | GetPolyFillMode
Stretching mode | GetStretchBltMode

An application can change the default modes by calling one of the following functions.

Graphic mode | Function
Background mode | SetBkMode
Drawing mode | SetROP2
Mapping mode | SetMapMode
Polygon-fill mode | SetPolyFillMode
Stretching mode | SetStretchBltMode

+$#A Device Context Types

The Win32 API provides four types of device contexts: display, printer, memory (or compatible), and information. Each type serves a specific purpose, as described in the following table.

Device context | Purpose
Display | Supports drawing operations on a video display.
Printer | Supports drawing operations on a printer or plotter.
Memory | Supports drawing operations on a bitmap.
Information | Supports the retrieval of device data.

+$#A Display Device Contexts

Windows provides three types of device contexts for video displays: class, common and private. Class and private device contexts are used in applications that perform numerous drawing operations such as computer-aided design (CAD) applications, desktop-publishing applications, drawing and painting applications, and so on. Common device contexts are used in applications that perform infrequent drawing operations.

An application obtains a display device context by calling either the BeginPaint or GetDC function and identifying the window in which the corresponding output will appear. (The type of device context Windows returns is dependent on how the application registered its window class.) Typically, an application obtains a display device context only when it must draw in the client area. When the application is finished drawing, it must release the device context by calling the EndPaint or ReleaseDC function.

Class Device Contexts

Class device contexts are supported strictly for compatibility with previous versions of Windows. When writing a Win32-based application, avoid using the class device context. Use a private device context instead.

Common Device Contexts

Common device contexts are display device contexts maintained in a special cache by the window-management component of the Win32 API. An application obtains a handle identifying one of the available common device contexts by calling the GetDC, GetDCEx, or BeginPaint function. Before returning this device context handle, Windows initializes a common device context with default objects, attributes, and modes. Any drawing operations performed by the application use these defaults unless one of the GDI functions is called to select a new object, change the attributes of an existing object, or select a new mode.

Because only a limited number of common device contexts exist in the window manager's heap, an application should release these device contexts after it has finished drawing. An application releases a common device context by calling the ReleaseDC or EndPaint function. When the application releases a common device context, any changes to the default data are lost.

Private Device Contexts

Private device contexts are display device contexts that, unlike common device contexts, retain any changes to the default data ⎯ even after an application calls the ReleaseDC or EndPaint function. Private device contexts are not part of the window manager's cache and therefore need not be released after use. The window manager automatically removes a private device context after the last window of that class has been destroyed.

An application creates a private device context by first specifying the CS_OWNDC window-class style when it initializes the style member of the WNDCLASS structure and calls the RegisterClass function. (For more information about window classes, see Window Classes.) After creating a window with the CS_OWNDC style, an application can call the GetDC, GetDCEx, or BeginPaint function once to obtain a handle identifying a private device context. The application can continue using this handle (and the associated device context) until it deletes the window created with this class. Any changes to graphic objects and their attributes, or graphic modes are retained by Windows until the window is deleted.

+$#A Printer Device Contexts

Windows provides a single type of printer device context that can be used when printing on a dot-matrix printer, ink-jet printer, laser printer, or plotter. An application creates a printer device context by calling the CreateDC function and supplying the appropriate arguments (the name of the printer driver, the name of the printer, the file or device name for the physical output medium, and other initialization data). When an application has finished printing, it deletes the printer device context by calling the DeleteDC function. An application must delete (rather than release) a printer device context; the ReleaseDC function fails when an application attempts to use it to release a printer device context.

+$#A Memory Device Contexts

Windows supports a memory device context that stores bitmapped images for a particular device. An application creates a memory device context by calling the CreateCompatibleDC function and supplying a handle that identifies a device context for a particular device. When Windows processes this call, it creates a bitmap having a color format compatible with the original device. Because the bitmap is compatible with the given device, a memory device context is also sometimes referred to as a compatible device context.

The original bitmap in a memory device context is simply a placeholder. Its dimensions are one pixel by one pixel. Before an application can begin drawing, it must select a bitmap with the appropriate width and height into the device context by calling the SelectObject function. Once the new bitmap is selected into the memory device context, an application can begin using the device context to store images. For more information about bitmaps and bitmap operations, see Bitmaps.

+$#A Information Device Contexts

Windows supports an information device context used to retrieve default device data. For example, an application can call the CreateIC function to create an information device context for a particular model of printer and then call the GetCurrentObject and GetObject functions to retrieve the default pen or brush attributes. Because Windows can retrieve device information without creating the structures normally associated with the other types of device contexts, an information device context involves far less overhead and is created significantly faster than any of the other types. After an application finishes retrieving data by using an information device context, it must call the DeleteDC function.

+$#A Device Context Operations

An application can perform the following operations on a device context:

• Enumerate existing graphic objects.

• Select new graphic objects.

• Delete existing graphic objects.

• Save the current graphic objects, their attributes, and the graphic modes.

• Restore previously saved graphic objects, their attributes, and the graphic modes.

In addition, an application can use a device context to determine how graphics output is translated, cancel lengthy drawing operations (begun by a thread in a multithreaded application), or reset a printer to a particular state.

+$#A Operations on Graphic Objects

Once an application creates a display or printer device context, it can begin drawing on the associated device or, in the case of the memory device context, it can begin drawing on the bitmap stored in memory. However, before drawing begins and sometimes while drawing is in progress, it is often necessary to replace the default objects with new objects.

An application can examine a default object's attributes by calling the GetCurrentObject and GetObject functions. The GetCurrentObject function returns a handle identifying the current pen, brush, palette, bitmap, or font, and the GetObject function initializes a structure containing that object's attributes.

Some printers provide resident pens, brushes, and fonts that can be used to improve drawing speed in an application. Two functions can be used to enumerate these objects: EnumObjects and EnumFontFamilies. If the application must enumerate resident pens or brushes, it can call the EnumObjects function to examine the corresponding attributes. If the application must enumerate resident fonts, it can call the EnumFontFamilies function (which can also enumerate GDI fonts).

Once an application determines that a default object needs replacing, it creates a new object by calling one of the following creation functions.

Graphic object | Function
Bitmap | CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDiscardableBitmap, CreateDIBitmap
Brush | CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
Color Palette | CreatePalette
Font | CreateFont, CreateFontIndirect
Pen | CreatePen, CreatePenIndirect, ExtCreatePen
Region | CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreatePolyPolygonRgn, CreateRectRgn, CreateRectRgnIndirect, CreateRoundRectRgn

Each of these functions returns a handle identifying a new object. After an application retrieves a handle, it must call the SelectObject function to replace the default object. However, the application should save the handle identifying the default object and use this handle to replace the new object when it is no longer needed. When the application finishes drawing with the new object, it must restore the default object by calling the SelectObject function and delete the new object by calling the DeleteObject function. Failing to delete objects causes serious performance problems.

+$#A Cancellation of Drawing Operations

When complex drawing applications perform lengthy graphics operations, they consume valuable system resources. By taking advantage of Win32 multitasking features, an application can use threads and the CancelDC function to manage these operations. For example, if the graphics operation performed by thread A is consuming needed resources, thread B can call the CancelDC function to halt that operation.

+$#A Retrieving Device Data

Windows supports two functions, GetDeviceCaps and DeviceCapabilities, that applications can use to retrieve device data using a device context. GetDeviceCaps retrieves general device data for the following devices:

• Raster displays

• Dot-matrix printers

• Ink-jet printers

• Laser printers

• Vector plotters

• Raster cameras

The data includes the supported capabilities of the device, including device resolution (for video displays), color format (for video displays and color printers), number of graphic objects, raster capabilities, curve drawing, line drawing, polygon drawing, and text drawing. An application retrieves this data by supplying a handle identifying the appropriate device context as well as an index specifying the type of data the function is to retrieve.

The DeviceCapabilities function retrieves data specific to printers, including the number of available paper bins, the duplex capabilities of the printer, the resolutions supported by the printer, the maximum and minimum supported paper size, and so on. An application retrieves this data by supplying strings specifying a printer device and port, as well as an index specifying the type of data that the function is to retrieve.

+$#A Saving, Restoring, and Resetting a Device Context

The Win32 API supports three functions that an application can use to save, restore, and reset a device context: SaveDC, RestoreDC, and ResetDC. The SaveDC function records on a special GDI stack the current device context's graphic objects and their attributes, and graphic modes. A drawing application can call this function before a user begins drawing and save the application's original state ⎯ providing a "clean slate" for the user. To return to this original state, the application calls the RestoreDC function.

ResetDC is provided to reset printer device context data. An application calls this function to reset the paper orientation, paper size, output scaling factor, number of copies to be printed, paper source (or bin), duplex mode, and so on. Typically, an application calls this function after a user has changed one of the printer options and Windows has issued a WM_DEVMODECHANGE message.

+$#A Using the Device Context Functions

• Obtaining a private display device context

• Creating a printer device context

• Retrieving the capabilities of a printer

• Retrieving graphic-object attributes and selecting new graphic objects

+$#A Obtaining a Private Display Device Context

An application performing numerous drawing operations in the client area of its window must use a private display device context. To create this type of device context, the application must specify the CS_OWNDC constant for the style member of the WNDCLASS structure when registering the window class. After registering the window class, the application obtains a handle identifying a private display device context by calling the GetDC function.

The following example shows how to create a private display device context.

#include <windows.h> /* required for all Windows apps */

#include <stdio.h>

#include <string.h> /* strtok */

#include "dc.h" /* specific to this program */

/* Function prototypes */

BOOL InitApplication(HINSTANCE);

long FAR PASCAL MainWndProc(HWND, UINT, UINT, LONG);

/* Global variables */

HINSTANCE hinst; /* handle of current instance */

HDC hdc; /* display device context handle */

.

.

.

BOOL InitApplication(HINSTANCE hinstance)

{

WNDCLASS wc;

/*

* Fill in the window class structure with parameters

* describing the main window.

*/

wc.style = CS_OWNDC; /* Private-DC constant */

wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hinstance;

wc.hIcon = LoadIcon((HINSTANCE) NULL,

MAKEINTRESOURCE(IDI_APPLICATION));

wc.hCursor = LoadCursor((HINSTANCE) NULL,

MAKEINTRESOURCE(IDC_ARROW));

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "GenericMenu";

wc.lpszClassName = "GenericWClass";

/* Register the window class and return the

* resultant code.

*/

return RegisterClass(&wc);

}

LRESULT APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

WPARAM wParam, /* additional information */

LPARAM lParam) /* additional information */

{

PAINTSTRUCT ps; /* paint structure */

/* Retrieve a handle identifying the private DC. */

hdc = GetDC(hwnd);

switch (message) {

case WM_PAINT:

BeginPaint(hwnd, &ps);

.

. /* Draw and paint using private DC. */

.

+$#A Creating a Printer Device Context

An application can create a printer device context in one of two ways:

• Calling the PrintDlg function to display a common dialog box that allows the user to specify printer options, then creating a printer device context by using those options.

• Creating a printer device context by using the default printer data from the WIN.INI file.

This section contains code demonstrating the latter method.

To use the default printer, retrieve and parse the appropriate string from the WIN.INI file. This string is retrieved by calling the GetProfileString function and specifying the appropriate section and entry names. In the case of the default printer, this data is stored in the [windows] section and identified by the entry named device. The seventh line in the following excerpt from a WIN.INI file shows an entry for a Kodak® Diconix™ printer.

[windows]

load=

run=

Beep=yes

Spooler=yes

NullPort=None

device=Diconix,winspool,LPT1:

CoolSwitch=1

BorderWidth=3

KeyboardSpeed=31

InitialKeyboardIndicators=2

CursorBlinkRate=530

DoubleClickSpeed=686

Programs=com exe bat pif cmd

Documents=

DeviceNotSelectedTimeout=15

TransmissionRetryTimeout=45

swapdisk=

NetWarn=1

fPromptOnVerbose=FALSE

fPromptOnWarning=FALSE

fPromptOnError=TRUE

fPrintVerbose=FALSE

fPrintFileLine=FALSE

shell=

ScreenSaveTimeOut=300

ScreenSaveActive=0

DebugOptions=2048

The following example shows how a printer device context was created by using the data from WIN.INI.

HDC hdcPrint; /* printer DC handle */

char szDevString[120]; /* array for WIN.INI data */

char *szPrinter, *szDriver; /* printer and driver names */

char *szPort; /* port name */

/*

* Retrieve the printer, printer-driver, and

* output-port names from WIN.INI.

*/

GetProfileString("windows", "device", ",,,",

szDevString, 120);

/*

* Parse the string of names, setting ptrs

* as required. If the string contains the

* required names, use them to create a

* device context.

*/

if ((szPrinter = strtok(szDevString,

(const char *) ","))

&& (szDriver = strtok ((char *) NULL,

(const char *) ", "))

&& (szPort = strtok ((char *) NULL,

(const char *) ", ")))

hdcPrint = CreateDC(szDriver, szPrinter,

szPort, NULL);

/*

* Print a test page that contains the string

* "PRINTER TEST" in the upper left corner.

*/

Escape(hdcPrint, STARTDOC, 8, "Test-Doc", NULL);

TextOut(hdcPrint, 50, 50, "PRINTER TEST", 12);

Escape(hdcPrint, NEWFRAME, 0, NULL, NULL);

Escape(hdcPrint, ENDDOC, 0, NULL, NULL);

/* Delete the printer DC. */

DeleteDC(hdcPrint);

+$#A Retrieving the Capabilities of a Printer

Not every output device supports the entire set of Win32 graphics functions. For example, because of hardware limitations, most vector plotters do not support bit-block transfers. An application can determine whether a device supports a particular Win32 graphics function by calling the GetDeviceCaps function, specifying the appropriate index, and examining the return value.

The following example shows how an application tests a printer to determine whether it supports bit-block transfers.

/*

* Examine the raster capabilities of the device

* identified by hdcPrint to verify that it supports

* the BitBlt function.

*/

if ((GetDeviceCaps(hdcPrint, RASTERCAPS)

& RC_BITBLT) == 0) {

DeleteDC(hdcPrint);

break;

}

else

/* Print the bitmap using the printer DC. */

+$# Retrieving Graphic-Object Attributes and Selecting New Graphic Objects

An application can retrieve the attributes for a pen, brush, palette, font, or bitmap by calling the GetCurrentObject and GetObject functions. The GetCurrentObject function returns a handle identifying the object currently selected into the device context; the GetObject function returns a structure that describes the object's attributes.

The following example shows how an application can retrieve the current brush attributes and use the retrieved data to determine whether it is necessary to select a new brush.

HDC hdc; /* display DC handle */

HBRUSH hbrushNew, hbrushOld; /* brush handles */

HBRUSH hbrush; /* brush handle */

LOGBRUSH lb; /* logical-brush structure */

/*

* Retrieve a handle identifying the current brush.

*/

hbrush = GetCurrentObject(hdc, OBJ_BRUSH);

/*

* Retrieve a LOGBRUSH structure that contains the

* current brush attributes.

*/

GetObject(hbrush, sizeof(LOGBRUSH), &lb);

/*

* If the current brush is not a solid-black brush,

* replace it with the solid-black stock brush.

*/

if ((lb.lbStyle != BS_SOLID)

|| (lb.lbColor != 0x000000)) {

hbrushNew = GetStockObject(BLACK_BRUSH);

hbrushOld = SelectObject(hdc, hbrushNew);

}

/* Perform painting operations with the white brush. */

/*

* After completing the last painting operation

* with the new brush, the application should

* select the original brush back into the

* device context and delete the new brush.

* In this example, hbrushNew contains a handle

* to a stock object. It is not necessary (but it

* is not harmful) to call DeleteObject on a stock

* object. If hbrushNew contained a handle to a brush

* created by a function such as CreateBrushIndirect,

* it would be necessary to call DeleteObject.

*/

SelectObject(hdc, hbrushOld);

DeleteObject(hbrushNew);

Note that the application saved the original brush handle when calling the SelectObject function the first time. This handle is saved so that the original brush can be selected back into the device context after the last painting operation has been completed with the new brush. After the original brush is selected back into the device context, the new brush is deleted, freeing memory in the GDI heap.

+$#AK Device Context Reference

The following functions and messages are associated with device contexts.

+$#A Device Context Functions

Following are the functions used with device contexts.

CancelDC
ChangeDisplaySettings
CreateCompatibleDC
CreateDC
CreateIC
DeleteDC
DeleteObject
DeviceCapabilities
DrawEscape
EnumDisplaySettings
EnumObjects
EnumObjectsProc
GetCurrentObject
GetDC
GetDCEx
GetDCOrgEx
GetDeviceCaps
GetGraphicsMode
GetObject
GetObjectType
GetStockObject
ReleaseDC
ResetDC
RestoreDC
SaveDC
SelectObject

+$#A Device Context Messages

Following is the message used with device contexts.

WM_DEVMODECHANGE

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


Device Input and Output.rtf

+$#AK Device Input and Output

Applications use a device input and output control to communicate with a device driver. This overview discusses device handles, which identify the device being accessed, and device control codes, which specify the action the driver is to perform.

+$#A About Device Input and Output

The DeviceIoControl function provides a device input and output control (IOCTL) interface through which an application can communicate directly with a device driver. The DeviceIoControl function is a general-purpose interface that can send control codes to a variety of devices.

+$#A Device Handles

A DeviceIoControl call specifies a handle of a device driver to identify the device being accessed. You can use the CreateFile function to get a device driver handle.

The CreateFile call must specify either the name of a device or the name of the driver associated with a device. To specify a device name, the lpszname parameter of CreateFile specifies a string with the format

\\.\DeviceName

The types of devices that you can open and the method that you must use to specify them in the CreateFile function depend on the Win32 platform on which your application is running.

On Windows NT, DeviceIoControl can open a handle of a specific device. For example, to open a handle to the logical drive A:, specify "\\\\.\\a:". Alternatively, you can use the names "\\\\.\\PhysicalDrive0", "\\\\.\\PhysicalDrive1", and so on, to open handles to the physical drives on a system.

For Win32-based applications running on Windows 95, DeviceIoControl must specify a handle of a virtual device driver. For example, to open a handle to the system VxD, specify "\\\\.\\vwin32".

The CreateFile call should specify the FILE_SHARE_READ and FILE_SHARE_WRITE access flags when opening a handle to a device driver. However, when opening a communications resource, such as a serial port, CreateFile must specify exclusive access. Other CreateFile parameters are used as follows when opening a device handle:

• The fdwCreate parameter must specify the OPEN_EXISTING flag.

• The hTemplateFile parameter must be NULL.

• The fdwAttrsAndFlags parameter can specify FILE_FLAG_OVERLAPPED to indicate that the returned handle can be used in overlapped (asynchronous) I/O operations.

+$#A Device Control Codes

An application can use the DeviceIoControl function to send a control code to a device driver. The control code specifies the action that the driver is to perform. For example, a control code can ask a device driver to return information about the corresponding device, or direct the driver to carry out an action on the device, such as formatting a disk.

A number of standard control codes are defined in the Win32 header files. For a list of standard control codes, see DeviceIoControl . In addition, device drivers can define their own device-specific control codes. The types of control codes you can specify depend on the device being accessed and the Win32 platform on which your application is running.

Applications running on Windows NT can use the standard control codes or device-specific control codes to perform direct input and output operations on a floppy disk drive, hard disk drive, tape drive, or CD-ROM drive.

Win32-based applications running on Windows 95 cannot use the standard control codes in the Win32 header files. These applications are restricted to using the control codes supported by the virtual device driver being accessed. For example, the system VxD, VWIN32.VXD, supports the input and output control (IOCTL) functions originally provided by MS-DOS Interrupt 21h.

The control code specified in a DeviceIoControl call determines the values that must be specified for the other DeviceIoControl parameters. For example, a Windows NT application can specify the IOCTL_DISK_FORMAT_TRACKS control code. In this case, the lpvInBuffer parameter must point to a filled-in FORMAT_PARAMETERS structure, and the cbInBuffer parameter must specify the size of the structure.

+$#A Using Device Input and Output Control

• Calling DeviceIoControl on Windows NT

• Calling DeviceIoControl on Windows 95

+$#A Calling DeviceIoControl on Windows NT

On Windows NT, an application can use the DeviceIoControl function to perform direct input and output operations on, or retrieve information about, a floppy disk drive, hard disk drive, tape drive, or CD-ROM drive. The following example demonstrates how to retrieve information about the first physical drive in the system. It uses the CreateFile function to obtain the device handle of the first physical drive, and then uses the DeviceIoControl function with the IOCTL_DISK_GET_DRIVE_GEOMETRY control code to fill a DISK_GEOMETRY structure with information about the drive.

BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)

{

HANDLE hDevice;

BOOL fResult;

DWORD cb;

hDevice = CreateFile("\\\\.\\PhysicalDrive0",

0, FILE_SHARE_READ | FILE_SHARE_WRITE,

NULL, OPEN_EXISTING, 0, NULL);

if (hDevice == NULL)

return FALSE;

fResult = DeviceIoControl(hDevice,

IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,

pdg, sizeof(*pdg), &cb, (LPOVERLAPPED) NULL);

if (!fResult)

return FALSE;

CloseHandle(hDevice);

}

This example succeeds only when it runs on Windows NT, for two reasons:

• The standard device input/output control codes are available only on Windows NT, and

• On Windows 95, an application must specify a virtual device driver in the CreateFile function ⎯ not a specific device.

+$#A Calling DeviceIoControl on Windows 95

A Win32-based application running on Windows 95 can use the DeviceIoControl function to send control codes directly to a virtual device driver (VxD). Any VxD can support any number of control codes, or none at all.

The system VxD, VWIN32.VXD, supports the input and output control (IOCTL) functions originally provided by MS-DOS Interrupt 21h. The following example shows how to call Get Media ID (Interrupt 21h Function 440Dh Minor Code 66h) from a Win32-based application:

"\\\\.\\VxDName"

In Windows 95,

#define VWIN32_DIOC_DOS_IOCTL 1

typedef struct _DEVIOCTL_REGISTERS {

DWORD reg_EBX;

DWORD reg_EDX;

DWORD reg_ECX;

DWORD reg_EAX;

DWORD reg_EDI;

DWORD reg_ESI;

DWORD reg_Flags;

} DEVIOCTL_REGISTERS, *PDEVIOCTL_REGISTERS;

typedef struct _MID {

WORD midInfoLevel;

DWORD midSerialNum;

BYTE midVolLabel[11];

BYTE midFileSysType[8];

} MID, *PMID;

BOOL GetMediaID(PMID pmid, UINT nDrive)

{

DEVIOCTL_REGISTERS reg;

reg.reg_EAX = 0x440D; /* IOCTL for block devices */

reg.reg_EBX = nDrive; /* zero-based drive ID */

reg.reg_ECX = 0x0866; /* Get Media ID command */

reg.reg_EDX = (DWORD) pmid; /* receives media ID info */

if (!DoIOCTL(&reg))

return FALSE;

if (reg.reg_Flags & 0x8000) /* error if carry flag set */

return FALSE;

return TRUE;

}

BOOL DoIOCTL(PDEVIOCTL_REGISTERS preg)

{

HANDLE hDevice;

BOOL fResult;

DWORD cb;

preg->reg_Flags = 0x8000; /* assume error (carry flag set) */

hDevice = CreateFile("\\\\.\\vwin32",

GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,

(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

if (hDevice == (HANDLE) INVALID_HANDLE_VALUE)

return FALSE;

else {

fResult = DeviceIoControl(hDevice, VWIN32_DIOC_DOS_IOCTL,

preg, sizeof(*preg), preg, sizeof(*preg), &cb, 0);

if (!fResult)

return FALSE;

}

CloseHandle(hDevice);

return TRUE;

}

+$#AK Device Input and Output Reference

The following functions, operations, and structures are associated with device input and output.

+$#A Device Input and Output Functions

Use the following function to implement device input and output:

DeviceIoControl

+$#A Device Input and Output Operations

Use the following operations to implement device input and output:

FSCTL_DISMOUNT_VOLUME
FSCTL_LOCK_VOLUME
FSCTL_UNLOCK_VOLUME
IOCTL_DISK_CHECK_VERIFY
IOCTL_DISK_EJECT_MEDIA
IOCTL_DISK_FORMAT_TRACKS
IOCTL_DISK_GET_DRIVE_GEOMETRY
IOCTL_DISK_GET_DRIVE_LAYOUT
IOCTL_DISK_GET_MEDIA_TYPES
IOCTL_DISK_GET_PARTITION_INFO
IOCTL_DISK_LOAD_MEDIA
IOCTL_DISK_MEDIA_REMOVAL
IOCTL_DISK_PERFORMANCE
IOCTL_DISK_REASSIGN_BLOCKS
IOCTL_DISK_SET_DRIVE_LAYOUT
IOCTL_DISK_SET_PARTITION_INFO
IOCTL_DISK_VERIFY
IOCTL_SERIAL_LSRMST_INSERT

+$#A Device Input and Output Structures

Use these structures in device input and output operations:

FORMAT_PARAMETERS
DISK_GEOMETRY
DRIVE_LAYOUT_INFORMATION
PARTITION_INFORMATION
PREVENT_MEDIA_REMOVAL
DISK_PERFORMANCE
REASSIGN_BLOCKS
SET_PARTITION_INFORMATION
VERIFY_INFORMATION

Créé avec HelpNDoc Personal Edition: Générateur de documentation d'aide HTML gratuit


Dialog Boxes.rtf

+$#AK Dialog Boxes

In Microsoft® Windows®, a dialog box is a temporary window an application creates to retrieve user input. An application typically uses dialog boxes to prompt the user for additional information for commands. A dialog box usually contains one or more controls (child windows) with which the user enters text, chooses options, or directs the action of the command.

+$#A About Dialog Boxes

Windows provides many functions, messages, and predefined controls to help create and manage dialog boxes, thus making it easier to develop the user interface for an application. This overview describes the dialog box functions and messages and explains how to use them to create and use dialog boxes.

Windows also provides many predefined, or "common," dialog boxes that support commands, such as File Open and File Print. Applications that use these commands should use the common dialog boxes to prompt for the same user input, regardless of the type of application carrying out the commands. For more information about using common dialog boxes in your applications, see Common Dialog Box Library.

+$#A When to Use a Dialog Box

Most applications use dialog boxes to prompt for additional information for commands that require user input. Using a dialog box is the only recommended way for an application to retrieve the input. For example, the File Open command requires the name of a file to open, so an application should use a dialog box to prompt the user for the name. In such cases, the application creates the dialog box when the user chooses the command and destroys the dialog box immediately after the user supplies the information.

Many applications also use dialog boxes to display information or options while the user works in another window. For example, word processing applications often use a dialog box with a text-search command. While the application searches for the text, the dialog box remains on the screen. The user can then return to the dialog box and search for the same word again; or the user can change the entry in the dialog box and search for a new word. Applications that use dialog boxes in this way typically create one when the user chooses a command and continue to display it for as long as the application runs or until the user explicitly closes the dialog box.

To support the different ways applications use dialog boxes, Windows provides two types of dialog box: modal and modeless. A modal dialog box requires the user to supply information or cancel the dialog box before allowing the application to continue. Applications use modal dialog boxes in conjunction with commands that require additional information before they can proceed. A modeless dialog box allows the user to supply information and return to the previous task without closing the dialog box. Modal dialog boxes are simpler to manage than modeless dialog boxes because they are created, perform their task, and are destroyed by calling a single function.

To create either a modal or modeless dialog box, an application must supply a dialog box template to describe the dialog box style and content; the application must also supply a dialog box procedure to carry out tasks. The dialog box template is a binary description of the dialog box and the controls it contains. The developer can create this template as a resource to be loaded from the application's executable file, or created in memory while the application runs. The dialog box procedure is an application-defined callback function that Windows calls when it has input for the dialog box or tasks for the dialog box to carry out. Although a dialog box procedure is similar to a window procedure, it does not have the same responsibilities.

An application typically creates a dialog box by using either the DialogBox or CreateDialog function. DialogBox creates a modal dialog box; CreateDialog creates a modeless dialog box. These two functions load a dialog box template from the application's executable file and create a pop-up window that matches the template's specifications. There are other functions that create a dialog box by using templates in memory and pass additional information to the dialog box procedure as the dialog box is created.

Dialog boxes usually belong to a predefined, exclusive window class. Windows uses this window class and its corresponding window procedure for both modal and modeless dialog boxes. When the function is called, it creates the window for the dialog box, as well as the windows for the controls in the dialog box, then sends selected messages to the dialog box procedure. While the dialog box is visible, the predefined window procedure manages all messages, processing some messages and passing others to the dialog box procedure so that the procedure can carry out tasks. Applications do not have direct access to the predefined window class or window procedure, but they can use the dialog box template and dialog box procedure to modify the style and behavior of a dialog box.

+$#A Owner Window

Most dialog boxes have an owner window (or more simply, an owner). When creating the dialog box, the application sets the owner by specifying the owner's window handle. Windows uses the owner to determine the position of the dialog box in the Z order so that the dialog box is always positioned above its owner. Also, Windows can send messages to the window procedure of the owner, notifying it of events in the dialog box.

Windows automatically hides or destroys the dialog box whenever its owner is hidden or destroyed. This means the dialog box procedure requires no special processing to detect changes to the state of the owner window.

Because the typical dialog box is used in conjunction with a command in a menu, the owner window is usually the window containing the menu. Although it is possible to create a dialog box that has no owner, it is not recommended. For example, when a modal dialog box has no owner, Windows does not disable any of the application's other windows and allows the user to continue to carry out work in the other windows, defeating the purpose of the modal dialog box.

When a modeless dialog box has no owner, Windows neither hides nor destroys the dialog box when other windows in the application are hidden or destroyed. Although this does not defeat the purpose of the modeless dialog box, it requires that the application carry out special processing to ensure the dialog box is hidden and destroyed at appropriate times.

+$#A Message Boxes

A message box is a special dialog box that an application can use to display messages and prompt for simple input. A message box typically contains a text message and one or more buttons. An application creates the message box by using the MessageBox or MessageBoxEx function, specifying the text and the number and types of buttons to display. The MessageBoxEx function also allows you to specify the language to use for the text of any predefined push buttons in the message box.

Although the message box is a dialog box, Windows takes complete control of the creation and management of the message box. This means the application does not provide a dialog box template and dialog box procedure. Windows creates its own template based on the text and buttons specified for the message box and supplies its own dialog box procedure.

A message box is a modal dialog box and Windows creates it by using the same internal functions that DialogBox uses. If the application specifies an owner window when calling MessageBox or MessageBoxEx, Windows disables the owner. An application can also direct Windows to disable all top-level windows belonging to the current task by specifying the MB_TASKMODAL value when creating the dialog box.

Windows can send messages to the owner, such as WM_CANCELMODE and WM_ENABLE, just as it does when creating a modal dialog box. The owner window should carry out any actions requested by these messages.

+$#A Modal Dialog Boxes

A modal dialog box should be a pop-up window having a System menu, a title bar, and a thick border; that is, the dialog box template should specify the WS_POPUP, WS_SYSMENU, WS_CAPTION, and DS_MODALFRAME styles. Although an application can designate the WS_VISIBLE style, Windows always displays a modal dialog box regardless of whether the dialog box template specifies the WS_VISIBLE style. An application must not create a modal dialog box having the WS_CHILD style. A modal dialog box with this style disables itself, preventing any subsequent input from reaching the application.

An application creates a modal dialog box by using either the DialogBox or DialogBoxIndirect function. DialogBox requires the name or identifier of a resource containing a dialog box template; DialogBoxIndirect requires the handle of a memory object containing a dialog box template. The DialogBoxParam and DialogBoxIndirectParam functions also create modal dialog boxes; they are identical to the previously mentioned functions but pass a specified parameter to the dialog box procedure when the dialog box is created.

When creating the modal dialog box, Windows makes it the active window. The dialog box remains active until the dialog box procedure calls the EndDialog function or Windows activates a window in another application. Neither the user nor the application can make the owner window active until the modal dialog box is destroyed.

When the owner window is not already disabled, Windows automatically disables the window and any child windows belonging to it when it creates the modal dialog box. The owner window remains disabled until the dialog box is destroyed. Although a dialog box procedure could potentially enable the owner window at any time, enabling the owner defeats the purpose of the modal dialog box and is not recommended. When the dialog box procedure is destroyed, Windows enables the owner window again, but only if the modal dialog box caused the owner to be disabled.

As Windows creates the modal dialog box, it sends the WM_CANCELMODE message to the window (if any) currently capturing mouse input. An application that receives this message should release the mouse capture so that the user can move the mouse in the modal dialog box. Because Windows disables the owner window, all mouse input is lost if the owner fails to release the mouse upon receiving this message.

To process messages for the modal dialog box, Windows starts its own message loop, taking temporary control of the message queue for the entire application. When Windows retrieves a message that is not explicitly for the dialog box, it dispatches the message to the appropriate window. If it retrieves a WM_QUIT message, it posts the message back to the application message queue so that the application's main message loop can eventually retrieve the message.

Windows sends the WM_ENTERIDLE message to the owner window whenever the application message queue is empty. The application can use this message to carry out a background task while the dialog box remains on the screen. When an application uses the message in this way, the application must frequently yield control (for example, by using the PeekMessage function) so that the modal dialog box can receive any user input. To prevent the modal dialog box from sending the WM_ENTERIDLE messages, the application can specify the DS_NOIDLEMSG style when creating the dialog box.

An application destroys a modal dialog box by using the EndDialog function. In most cases, the dialog box procedure calls EndDialog when the user chooses the Close command from the dialog box's System menu or chooses the OK or Cancel button in the dialog box. The dialog box can return a value through the DialogBox function (or other creation functions) by specifying a value when calling the EndDialog function. Windows returns this value after destroying the dialog box. Most applications use this return value to determine whether the dialog box completed its task successfully or was canceled by the user. Windows does not return control from the function that creates the dialog box until the dialog box procedure has called the EndDialog function.

+$#A Modeless Dialog Boxes

A modeless dialog box should be a pop-up window having a System menu, a title bar, and a thin border; that is, the dialog box template should specify the WS_POPUP, WS_CAPTION, WS_BORDER, and WS_SYSMENU styles. Windows does not automatically display the dialog box unless the template specifies the WS_VISIBLE style.

An application creates a modeless dialog box by using the CreateDialog or CreateDialogIndirect function. CreateDialog requires the name or identifier of a resource containing a dialog box template; CreateDialogIndirect requires the handle of a memory object containing a dialog box template. Two other functions, CreateDialogParam and CreateDialogIndirectParam, also create modeless dialog boxes; they pass a specified parameter to the dialog box procedure when the dialog box is created.

CreateDialog and other creation functions return a window handle for the dialog box. The application and the dialog box procedure can use this handle to manage the dialog box. For example, if WS_VISIBLE is not specified in the dialog box template, the application can display the dialog box by passing the window handle to the ShowWindow function.

A modeless dialog box neither disables the owner window nor sends messages to it. When creating the dialog box, Windows makes it the active window, but the user or the application can change the active window at any time. If the dialog box does become inactive, it remains above the owner window in the Z order, even if the owner window is active.

The application is responsible for retrieving and dispatching input messages to the dialog box. Most applications use the main message loop for this. To permit the user to move to and select controls by using the keyboard, however, the application must call the IsDialogMessage function. For more information about this function, see Dialog Box Keyboard Interface.

A modeless dialog box cannot return a value to the application as a modal dialog box does, but the dialog box procedure can send information to the owner window by using the SendMessage function.

An application must destroy all modeless dialog boxes before terminating. It can destroy a modeless dialog box by using the DestroyWindow function. In most cases, the dialog box procedure calls DestroyWindow in response to user input, such as choosing the Cancel button. If the user never closes the dialog box in this way, the application must call DestroyWindow.

DestroyWindow invalidates the window handle for the dialog box, so any subsequent calls to functions that use the handle return error values. To prevent errors, the dialog box procedure should notify the owner that the dialog box has been destroyed. Many applications maintain a global variable containing the handle for the dialog box. When the dialog box procedure destroys the dialog box, it also sets the global variable to NULL, indicating that the dialog box is no longer valid.

The dialog box procedure must not call the EndDialog function to destroy a modeless dialog box.

+$#A Dialog Box Template

A dialog box template is binary data that describes the dialog box, defining its height, width, style, and the controls it contains. To create a dialog box, Windows either loads a dialog box template from the resources in the application's executable file or uses the template passed to it in global memory by the application. In either case, the application must supply a template when creating a dialog box.

A developer creates template resources by using a resource compiler or a dialog box editor. A resource compiler converts a text description into a binary resource, and a dialog box editor saves an interactively constructed dialog box as a binary resource.

Note An explanation of how to create template resources and add them to the application's executable file is beyond the scope of this book. For more information about creating template resources and adding them to an executable file, see the documentation provided with your application development tools.

Applications that create dialog boxes without using template resources must create templates in memory and pass the templates to either the DialogBoxIndirect or CreateDialogIndirect function. An application creates a memory template by allocating a global memory object and filling it with a DLGTEMPLATE structure and one or more DLGITEMTEMPLATE structures. A memory template is identical in form and content to a template resource. Many applications that use memory templates first load a template resource into memory by using the LoadResource function, then modify the loaded resource to create a new memory template.

The following sections describe the styles, measurements, and other values used in a dialog box template.

+$#A Dialog Box Template Styles

Every dialog box template specifies a combination of style values that define the appearance and features of the dialog box. The style values can be window styles, such as WS_POPUP and WS_SYSMENU, and dialog box styles, such as DS_MODALFRAME. The number and type of styles for a template depends on the type and purpose of the dialog box.

Windows passes all window styles given in the template to the CreateWindowEx function when creating the dialog box. Windows may pass one or more extended styles depending on the given dialog box styles. For example, when the template specifies DS_MODALFRAME, Windows uses WS_EX_DLGMODALFRAME when creating the dialog box. When the template specifies DS_SYSMODAL, Windows uses WS_EX_TOPMOST. All other dialog box styles affect how Windows manages the dialog box.

Most dialog boxes are pop-up windows that have a System menu and a title bar. Therefore, the typical template specifies the WS_POPUP, WS_SYSMENU, and WS_CAPTION styles. The template also specifies a border style: WS_BORDER for modeless dialog boxes, and DS_MODALFRAME for modal dialog boxes. A template may specify a window type other than pop-up (such as WS_OVERLAPPED) if it creates a customized window instead of a dialog box.

Windows always displays a modal dialog box regardless of whether the WS_VISIBLE style is given. When the template for a modeless dialog box specifies the WS_VISIBLE style, Windows automatically displays the dialog box when it is created. Otherwise, the application is responsible for displaying the dialog box by using the ShowWindow function.

The template can specify the DS_SETFOREGROUND style to force Windows to bring the dialog box to the foreground. This is especially useful for modal dialog boxes that require immediate attention from the user regardless of whether the owner window is the foreground window.

Using the DS_ABSALIGN style, Windows interprets dialog box measurements as screen coordinates; using the DS_SETFONT style, Windows uses a given font, instead of the system font, to draw text in the dialog box client area and in the controls in the dialog box; the DS_NOIDLEMSG style prevents a modal dialog box from sending WM_ENTERIDLE messages to the owner window. These styles are described in more detail in later topics.

The DS_LOCALEDIT style does not apply to Win32-based applications.

Dialog boxes with the DS_SYSMODAL style receive the WS_EX_TOPMOST window style but no other special properties or styles; this means the user still has access to other windows on the desktop even though a DS_SYSMODAL dialog box may be displayed.

+$#A Dialog Box Measurements

Every dialog box template contains measurements that specify the position, width, and height of the dialog box and the controls it contains. These measurements are device independent, so an application can use a single template to create the same dialog box for all types of display devices. This ensures that a dialog box will have the same proportions and appearance on all screens despite differing resolutions and aspect ratios between screens.

Dialog box measurements are given in dialog base units. One horizontal unit is equal to one-fourth of the average character width for the system font. One vertical unit is equal to one-eighth of the average character height for the system font. An application can retrieve the number of pixels per base unit for the current display by using the GetDialogBaseUnits function. An application can convert measurements from dialog base units to pixels by using the MapDialogRect function.

The template must specify the initial coordinates of the upper left corner of the dialog box. Usually the coordinates are relative to the upper left corner of the owner window's client area. When the template specifies the DS_ABSALIGN style or the dialog box has no owner, the position is relative to the upper left corner of the screen. Windows sets this initial position when creating the dialog box, but permits an application to adjust the position before displaying the dialog box. For example, an application can retrieve the dimensions of the owner window, calculate a new position that centers the dialog box in the owner window, and then set the position by using the SetWindowPos function.

The template should specify a dialog box width and height that does not exceed the width and height of the screen and ensures that all controls and are within the client area of the dialog box. Although Windows permits a dialog box to be any size, creating one that is too small or too large can prevent the user from providing input, defeating the purpose of the dialog box. Many applications use more than one dialog box when there are a large number of controls. In such cases, the initial dialog box usually contains one or more buttons that the user can choose to display the next dialog box.

+$#A Dialog Box Controls

The template specifies the position, width, height, style, identifier, and window class for each control in the dialog box. Windows creates each control by passing this data to the CreateWindowEx function. Controls are created in the order they are given in the template. The template should specify the appropriate number, type, and order of controls to ensure that the user can enter the input needed to complete the command associated with the dialog box.

For each control, the template specifies style values that define the appearance and operation of the control. Every control is a child window and therefore must have the WS_CHILD style. To ensure that the control is visible when the dialog box is displayed, each control must also have the WS_VISIBLE style. Other commonly used window styles are WS_BORDER for controls that have optional borders, WS_DISABLED for controls that should be disabled when the dialog box is initially created, and WS_TABSTOP and WS_GROUP for controls that can be accessed using the keyboard. The WS_TABSTOP and WS_GROUP styles are used in conjunction with the dialog keyboard interface described later in this topic.

The template may also specify control styles specific to the control's window class. For example, a template that specifies a button control must give a button control style such as BS_PUSHBUTTON or BS_CHECKBOX. Windows passes the control styles to the control window procedure through the WM_CREATE message, allowing the procedure to adapt the appearance and operation of the control.

Windows converts the position coordinates and the width and height measurements from dialog base units to pixels, before passing these to CreateWindowEx. When Windows creates a control, it specifies the dialog box as the parent window. This means Windows always interprets the position coordinates of the control as client coordinates, relative to the upper left corner of the dialog box's client area.

The template specifies the window class for each control. A typical dialog box contains controls belonging to the predefined control window classes, such as the button and edit control window classes. In this case, the template specifies window classes by supplying the corresponding predefined atom values for the classes. When a dialog box contains a control belonging a custom control window class, the template gives the name of that registered window class or the atom value currently associated with the name.

Each control in a dialog box must have a unique identifier to distinguish it from other controls. Controls send information to the dialog box procedure through WM_COMMAND messages, so the control identifiers are essential for the procedure to determine which control sent a given message. The only exception to this rule are control identifiers for static controls. Static controls do not require unique identifiers because they send no WM_COMMAND messages.

To permit the user to close the dialog box, the template should specify at least one push button and give it the control identifier IDCANCEL. To permit the user to choose between completing or canceling the command associated with the dialog box, the template should specify two push buttons, labeled OK and Cancel, with control identifiers of IDOK and IDCANCEL, respectively.

A template also specifies optional text and creation data for a control. The text typically provides labels for button controls or specifies the initial content of a static text control. The creation data is one or more bytes of data that Windows passes to the control window procedure when creating the control. Creation data is useful for controls that require more information about their initial content or style than is given by other data. For example, an application can use creation data to set the initial setting and range for a scroll bar control.

+$#A System Menu

Windows gives a dialog box a System menu when the template specifies the WS_SYSMENU style. To prevent inappropriate command input, Windows automatically disables all commands in the menu except the Move and Close commands. The user can use the Move command to move the dialog box. When the user chooses the Close command, Windows sends a WM_COMMAND message to the dialog box procedure with the wParam parameter set to IDCANCEL. This is identical to the message sent by the Cancel button when the user chooses it. The recommended action for this message is to close the dialog box and cancel the requested command or task.

Although other menus in dialog boxes are not recommended, a dialog box template can specify a menu by supplying the identifier or the name of a menu resource. In this case, Windows loads the resource and creates the menu for the dialog box. Applications typically use menu identifiers or names in templates when using the templates to create custom windows rather than dialog boxes.

+$#A Dialog Box Fonts

Windows draws all text in dialog box using the system font by default. An application can direct Windows to use another font by setting the DS_SETFONT style for the dialog box and specifying a point size and typeface name. Although a dialog box template can specify a font, Windows always uses the system font for the title and menus of the dialog box; the DS_SETFONT style does not change this.

When the DS_SETFONT style is specified, the system sends a WM_SETFONT message to the dialog box procedure and to each control as it creates the control. The dialog box procedure is responsible for saving the font handle passed with the WM_SETFONT message and selecting the handle into the display device context whenever it writes text to the window. Predefined controls do this by default.

When the DS_SETFONT style is given, Windows uses the average character width of the font to calculate the position and dimensions of the dialog box. Otherwise, it uses the average character width of the system font.

+$#A Templates in Memory

A template in memory consists of a DLGTEMPLATE structure and related data followed by one or more DLGITEMTEMPLATE structures and related data. There is one DLGITEMTEMPLATE structure for each control in the dialog box.

The DLGTEMPLATE structure specifies the position, width, height, style, and number of controls in the dialog box. Additional data following the structure specifies the optional menu, class, title, and font for the dialog box. The DLGTEMPLATE structure has the following form:

typedef struct { // dltt

DWORD style;

DWORD dwExtendedStyle;

WORD cdit;

WORD x;

WORD y;

WORD cx;

WORD cy;

} DLGTEMPLATE;

The style member specifies the window and dialog box styles for the dialog box. Windows does not use the dwExtendedStyle member when creating a dialog box. An application that uses templates to create other types of windows can use this member to specify extended window styles for the other windows. The x and y members specify the dialog box position; cx and cy specify the width and height. The cdit member specifies the number of controls in the dialog box and is the same as the number of DLGITEMTEMPLATE structures in the template.

The DLGTEMPLATE structure is immediately followed by a 16-bit value that specifies whether the dialog box has a menu. When the value is 0x0000, the dialog box has no menu. When the value is 0xFFFF, the subsequent 16-bit value specifies the identifier of the menu resource that describes the dialog box's menu. When the initial value is neither 0x0000 nor 0xFFFF, Windows assumes it is the start of a null-terminated string specifying the name of the menu resource that describes the dialog box's menu.

The first 16-bit value following the menu identifier or name specifies the window class to which the dialog box belongs. When the value is 0x0000, Windows uses the predefined dialog box class for the dialog box. When the value is 0xFFFF, the subsequent 16-bit value is an atom value identifying a registered window class. When the initial value is neither 0x0000 nor 0xFFFF, Windows assumes it is the start of a null-terminated string identifying a registered window class.

The dialog box title immediately follows the class atom or name. The title is a null-terminated string. When the string is empty, Windows leaves the title bar for the dialog box blank. When the dialog box does not have the WS_CAPTION style, Windows sets the title to the given string but does not display it.

When the dialog box has the DS_SETFONT style, the dialog box title is immediately followed by a 16-bit value specifying the point size and by a null-terminated string specifying the typeface for the font. Windows uses this data to create a font for use when writing text in the dialog box client area and in each control. When the dialog box does not have the DS_SETFONT style, the 16-bit value and string are not part of the template.

Each DLGITEMTEMPLATE structure in the template contains data specifying the position, width, height, style, and identifier of the control. Additional information follows each structure, specifying the window class to which the control belongs, the initial text for the control, and any other creation data needed to create the control. A DLGITEMTEMPLATE structure has the following form:

typedef struct { // dlit

DWORD style;

DWORD dwExtendedStyle;

WORD x;

WORD y;

WORD cx;

WORD cy;

WORD id;

} DLGITEMTEMPLATE;

The style member specifies the control style. This member is a combination of window-style flags, such as BS_PUSHBUTTON and WS_VISIBLE. Windows does not use the dwExtendedStyle member to create controls for dialog boxes. Applications that use templates to create other types of windows can use this member to specify extended window styles for those controls. The x and y members specify the coordinates of the upper left corner of the control; cx and cy specify the width and height of the control. The id member is the control identifier.

The control's window class is specified by an atom or name immediately following the DLGITEMTEMPLATE structure. When the first 16-bit value after the structure is 0xFFFF, the subsequent 16-bit value is an atom value identifying a registered window class. Otherwise, the first value is the start of a null-terminated string specifying the name of a registered window class.

The control text starts on the first 16-bit boundary after the class atom or name. This text is either two 16-bit values specifying a resource identifier or a null- terminated string specifying the initial text for the control. If this member is a resource identifier, the first 16-bit value is 0xFFFF and the second is the identifier. Resource identifiers are usually given for controls, such as static icon controls, that load and display icons or other resources rather than displaying text.

Any additional creation data required by the control follows the control text. Windows passes this data to the control as the creation parameters when it calls the CreateWindowEx function.

+$#A Dialog Box Procedure

A dialog box procedure is similar to a window procedure in that Windows sends messages to the procedure when it has information to give or tasks to carry out. Unlike a window procedure, a dialog box procedure never calls the DefWindowProc function. Instead, it returns the Boolean value TRUE if it processes a message or FALSE if it does not.

Every dialog box procedure has the following form:

BOOL APIENTRY DlgProc(hwndDlg, message, wParam, lParam)

HWND hwndDlg;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

switch (message) {

// Place message cases here.

default:

return FALSE;

}

}

The procedure parameters serve the same purpose as in a window procedure, with the hwndDlg parameter receiving the window handle of the dialog box.

Most dialog box procedures process the WM_INITDIALOG message and the WM_COMMAND messages sent by the controls, but process few if any other messages. If a dialog box procedure does not process a message, it must return FALSE to direct Windows to process the messages internally. The only exception to this rule is the WM_INITDIALOG message. The dialog box procedure must return TRUE to direct Windows to further process the WM_INITDIALOG message. In any case, the procedure must not call DefWindowProc.

+$#A The WM_INITDIALOG Message

Windows does not send a WM_CREATE message to the dialog box procedure. Instead, it sends a WM_INITDIALOG message when it creates the dialog box and all its controls but before it displays the dialog box. The procedure should carry out any initialization required to ensure that the dialog box displays the current settings associated with the command or task. For example, when a dialog box contains a control to show the current drive and directory, the procedure must determine the current drive and directory and set the control to that value.

The procedure can initialize controls by using functions such as SetDlgItemText and CheckDlgButton. Because controls are windows, the procedure can also manipulate them by using window-management functions such as EnableWindow and SetFocus. The procedure can retrieve the window handle for a control by using the GetDlgItem function.

The dialog box procedure can change the contents, state, and position of any control as needed. For example, in a dialog box that contains a list box of filenames and an Open button, the procedure can disable the Open button until the user selects a file from the list. In this example, the dialog box template specifies the WS_DISABLED style for the Open button and Windows automatically disables the button when creating it. When the dialog box procedure receives a notification message from the list box indicating that the user has selected a file, the procedure calls the EnableWindow function to enable the Open button.

If the application creates the dialog box by using one of the functions DialogBoxParam, DialogBoxIndirectParam, CreateDialogParam, or CreateDialogIndirectParam, the lParam parameter for the WM_INITDIALOG message contains the extra parameter passed to the function. Applications typically use this extra parameter to pass the address of additional initialization information to the dialog box procedure, but the dialog box procedure must determine the meaning of the parameter. If the application uses another function to create the dialog box, Windows sets the lParam parameter to NULL.

Before returning from the WM_INITDIALOG message, the procedure should determine whether it should set the input focus to a given control. If the dialog box procedure returns TRUE, Windows automatically sets the input focus to the control whose window handle is in the wParam parameter. If the control receiving the default focus is not appropriate, it can set the focus to the appropriate control by using the SetFocus function. If the procedure sets the input focus, it must return FALSE to prevent Windows from setting the default focus. The control receiving the default input focus is always the first control given in the template that is visible, not disabled, and has the WS_TABSTOP style. If no such control exists, Windows sets the default input focus to the first control in the template.

+$#A The WM_COMMAND Message

A control can send a WM_COMMAND message to the dialog box procedure when the user carries out an action in the control. These messages, called notification messages, inform the procedure of user input and permit it to carry out appropriate responses.

All predefined controls, except static controls, send notification messages for selected user actions. For example, a push button sends the BN_CLICKED notification message whenever the user chooses the button. In all cases, the low-order word of the wParam parameter contains the control identifier, the high-order word of wParam contains the notification code, and the lParam parameter contains the control window handle.

The dialog box procedure should monitor and process notification messages. In particular, the procedure must process messages having the IDOK or IDCANCEL identifiers; these messages represent a request by the user to close the dialog box. The procedure should close the dialog box by using the EndDialog function for modal dialog boxes and the DestroyWindow function for modeless dialog boxes.

Windows also sends WM_COMMAND messages to the dialog box procedure if the dialog box has a menu, such as the System menu, and the user chooses a command. In particular, Windows sends a WM_COMMAND message with the wParam parameter set to IDCANCEL whenever the user chooses the Close command in the dialog box's System menu. The message is nearly identical to the notification message sent by the Cancel button and should be processed in exactly the same way.

+$#A The WM_PARENTNOTIFY Message

A control sends a WM_PARENTNOTIFY message whenever the user presses a mouse button while pointing to the control. Some applications interpret this message as a signal to carry out an action related to the control, such as displaying a line of text describing the purpose of the control.

Windows also sends WM_PARENTNOTIFY messages when it creates and destroys a window, but not for controls created from a dialog box template. Windows prevents these messages by specifying the WS_EX_NOPARENTNOTIFY style when creating the controls. An application cannot override this default behavior unless it creates its own controls for the dialog box.

+$#A Control-Color Messages

Controls and Windows can send control-color messages when they want the dialog box procedure to paint the background of a control or other window by using a specific brush and colors. This can be useful when applications override the default colors used in dialog boxes and their controls. Following are the control-color messages, which have replaced the WM_CTLCOLOR message.

WM_CTLCOLORBTN
WM_CTLCOLORDLG
WM_CTLCOLOREDIT
WM_CTLCOLORLISTBOX
WM_CTLCOLORSCROLLBAR
WM_CTLCOLORSTATIC

A control sends a control-color message to the dialog box procedure just before it paints its own background. The message allows the procedure to specify which brush to use and to set the background and foreground colors. The procedure specifies a brush by returning the brush handle. To set the background and foreground colors, the procedure uses the SetBkColor and SetTextColor functions with the control's display device context. The control-color message passes a handle of the display device context to the procedure in the message's wParam parameter.

Windows sends a WM_CTLCOLORDLG message to the dialog box procedure if the procedure does not process the WM_ERASEBKGND message. The predefined dialog box class does not have a class background brush, so this message lets the procedure define its own background without having to include the code to carry out the work.

In any case, when a dialog box procedure does not process a control-color message, Windows uses a brush with the default window color to paint the background for all controls and windows except scroll bars. An application can retrieve the default window color by passing the COLOR_WINDOW value to the GetSysColor function. While the background is painted, the foreground color for the display device context is set to the default text color (COLOR_WINDOWTEXT). For scroll bars, Windows uses a brush having the default scroll bar color (COLOR_SCROLLBAR). In this case, the background and foreground colors for the display device context are set to white and black, respectively.

+$#A Dialog Box Default Message Processing

The window procedure for the predefined dialog box class carries out default processing for all messages that the dialog box procedure does not process. When the dialog box procedure returns FALSE for any message, the predefined window procedure checks the messages and carries out the following default actions:

Message | Default action
DM_GETDEFID | Returns the control identifier of the default push button if it exists. Otherwise, returns zero.
DM_SETDEFID | Sets the control specified by the control identifier in the wParam parameter to the default push button.
WM_ACTIVATE | Restores the input focus to the control identified by the previously saved handle if the dialog box is activated. Otherwise, the procedure saves the handle of the control having the input focus.
WM_CHARTOITEM | Returns zero.
WM_CLOSE | Posts the BN_CLICKED notification message to the dialog box, specifying IDCANCEL as the control identifier. If the dialog box has an IDCANCEL control identifier and the control is currently disabled, the procedure sounds a warning and does not post the message.
WM_COMPAREITEM | Returns zero.
WM_ERASEBKGND | Fills the dialog box client area by using either the brush returned from the WM_CTLCOLORDLG message or with the default window color.
WM_GETFONT | Returns the handle of the application-defined dialog box font.
WM_INITDIALOG | Returns zero.
WM_LBUTTONDOWN | Sends a CB_SHOWDROPDOWN message to the combo box having the input focus, directing the control to hide its drop-down list box. The procedure calls DefWindowProc to complete the default action.
WM_NCDESTROY | Releases global memory allocated for edit controls in the dialog box (applies to dialog boxes in Windows-based applications that specify the DS_LOCALEDIT style) and frees any application-defined font (applies to dialog boxes that specify the DS_SETFONT style). The procedure calls the DefWindowProc function to complete the default action.
WM_NCLBUTTONDOWN | Sends a CB_SHOWDROPDOWN message to the combo box having the input focus, directing the control to hide its drop-down list box. The procedure calls DefWindowProc to complete the default action.
WM_NEXTDLGCTL | Sets the input focus to the next or previous control in the dialog box, to the control identified by the handle in the wParam parameter, or to the first control in the dialog box that is visible, not disabled, and has the WS_TABSTOP style. The procedure ignores this message if the current window with the input focus is not a control.
WM_SETFOCUS | Sets the input focus to the control identified by a previously saved control window handle. If no such handle exists, the procedure sets the input focus to the first control in the dialog box template that is visible, not disabled, and has the WS_TABSTOP style. If no such control exists, the procedure sets the input focus to the first control in the template.
WM_SHOWWINDOW | Saves the handle of the control having the input focus if the dialog box is being hidden, then calls DefWindowProc to complete the default action.
WM_SYSCOMMAND | Saves the handle of the control having the input focus if the dialog box is being minimized, then calls DefWindowProc to complete the default action.
WM_VKEYTOITEM | Returns zero.

The predefined window procedure passes all other messages to DefWindowProc for default processing.

+$#A Dialog Box Keyboard Interface

Windows provides a special keyboard interface for dialog boxes that carries out special processing for several keys. The interface generates messages that correspond to certain buttons in the dialog box or changes the input focus from one control to another. Following are the keys used in this interface and their respective actions.

Key | Action
ALT+mnemonic | Moves the input focus to the first control (having the WS_TABSTOP style) after the static control containing the given mnemonic.
DOWN | Moves the input focus to the next control in the group.
ENTER | Sends a WM_COMMAND message to the dialog box procedure. The wParam parameter is set to IDOK or control identifier of the default push button.
ESC | Sends a WM_COMMAND message to the dialog box procedure. The wParam parameter is set to IDCANCEL.
LEFT | Moves the input focus to the previous control in the group.
mnemonic | Moves the input focus to the first control (having the WS_TABSTOP style) after the static control containing the given mnemonic.
RIGHT | Moves the input focus to the next control in the group.
SHIFT+TAB | Moves the input focus to the previous control that has the WS_TABSTOP style.
TAB | Moves the input focus to the next control that has the WS_TABSTOP style.
UP | Moves the input focus to the previous control in the group.

The Win32 API automatically provides the keyboard interface for all modal dialog boxes. It does not provide the interface for modeless dialog boxes unless the application calls the IsDialogMessage function to filter messages in its main message loop. This means that the application must pass the message to IsDialogMessage immediately after retrieving the message from the message queue. The function processes the messages if it is for the dialog box and returns a nonzero value to indicate that the message has been processed and must not be passed to the TranslateMessage or DispatchMessage function.

Because the dialog box keyboard interface uses direction keys to move between controls in a dialog box, an application cannot use these keys to scroll the contents of any modal dialog box or any modeless dialog box for which IsDialogMessage is called. When a dialog box has scroll bars, the application must provide an alternate keyboard interface for the scroll bars. Note that the mouse interface for scrolling is available when the system includes a mouse.

+$#A The WS_TABSTOP Style

The TAB key and SHIFT+TAB keys have no effect when the controls in the dialog box do not have the WS_TABSTOP style. Windows looks for this style as it searches for the next control in the dialog box to receive the input focus.

When the user presses TAB or SHIFT+TAB, Windows first determines whether the control having the input focus processes these keys. It sends the control a WM_GETDLGCODE message, and if the control returns DLGC_WANTTAB, Windows passes the keys to the control. Otherwise, Windows uses the GetNextDlgTabItem function to locate the next control that is visible, not disabled, and that has the WS_TABSTOP style. The search starts with the control currently having the input focus and proceeds in the order in which the controls were created ⎯ that is, the order in which they are defined in the dialog box template. When the system locates a control having the required characteristics, Windows moves the input focus to it.

An application can also use GetNextDlgTabItem to locate controls having the WS_TABSTOP style. The function retrieves the window handle of the next or previous control having the WS_TABSTOP style without moving the input focus.

+$#A The WS_GROUP Style

By default, Windows moves the input focus to the next or previous control whenever the user presses a direction key. As long as the current control with the input focus does not process these keys and the next or previous control is not a static control, Windows continues to move the input focus through all controls in the dialog box as the user continues to press the direction keys.

An application can use the WS_GROUP style to modify this default behavior. The style marks the beginning of a group of controls. If a control in the group has the input focus when the user begins pressing direction keys, the focus remains in the group. In general, the first control in a group must have the WS_GROUP style and all other controls in the group must not have this style. All controls in the group must be contiguous ⎯ that is, they must have been created consecutively with no intervening controls.

When the user presses a direction key, Windows first determines whether the current control having the input focus processes the direction keys. Windows sends a WM_GETDLGCODE message to the control and if the control returns the DLGC_WANTARROWS value, passes the key to the control. Otherwise, Windows uses the GetNextDlgGroupItem function to determine the next control in the group.

GetNextDlgGroupItem searches controls in the order (or reverse order) they were created. If the user presses the RIGHT or DOWN key, GetNextDlgGroupItem returns the next control if that control does not have WS_GROUP style. Otherwise, the function reverses the order of the search and returns the first control that has the WS_GROUP style. If the user presses the LEFT or UP key, the function returns the previous control unless the current control already has the WS_GROUP style. If the current control has this style, the function reverses the order of the search, locates the first control having the WS_GROUP style, and returns the control that immediately precedes the located control.

Once Windows has the next or previous control, it sends a WM_GETDLGCODE message to the control to determine the control type. Windows then moves the input focus to control if it is not a static control. If the control is an automatic radio button, Windows sends a BM_CLICK message to it. An application can also use GetNextDlgGroupItem to locate controls in a group.

Generally, the first control in the group combines the WS_GROUP and WS_TABSTOP styles so that the user can move from group to group by using the TAB key. If the group contains radio buttons, the application should apply the WS_TABSTOP style only to the first control in the group. Windows automatically moves the style when the user moves between controls in the group. This ensures that the input focus will always be on the most recently selected control when the user moves to the group using the TAB key.

+$#A Mnemonics

A mnemonic is a selected letter or digit in the label of a button or in the text of a static control. Windows moves the input focus to the control associated with the mnemonic whenever the user either presses the key that corresponds to the mnemonic or presses this key and the ALT key in combination. Mnemonics provide a quick way for the user to move to a given control by using the keyboard.

An application creates a mnemonic for a control by inserting the ampersand (&) immediately before the selected letter or digit in the label or text for the control. In most cases, the null-terminated string provided with the control in the dialog box template contains the ampersand. However, an application can create a mnemonic at any time by replacing a control's existing label or text by using the SetDlgItemText function. Only one mnemonic can be given for each control. Although it is recommended, mnemonics in a dialog box need not be unique.

When the user presses a letter or digit key, Windows first determines whether the current control having the input focus processes the key. Windows sends a WM_GETDLGCODE message to the control, and if the control returns the DLGC_WANTALLKEYS or DLG_WANTMESSAGE value, Windows passes the key to the control. Otherwise, it searches for a control whose mnemonic matches the given letter or digit. It continues to search until it locates a control or has examined all controls. During the search, it skips any static controls that have the SS_NOPREFIX style.

If Windows locates a static control and the control is not disabled, Windows moves the input focus to the first control after the static control that is visible, not disabled, and that has the WS_TABSTOP style. If Windows locates some other control that has a matching mnemonic, it moves the input focus to that control. If the control is a default push button, Windows sends a BN_CLICKED notification message to the dialog box procedure. If the control is another style of button and there is no other control in the dialog box having the same mnemonic, Windows sends the BM_CLICK message to the control.

+$#A Dialog Box Settings

The dialog box settings are the current selections and values for the controls in the dialog box. The dialog box procedure is responsible for initializing the controls to these settings when creating the dialog box. It is also responsible for retrieving the current settings from the controls before destroying the dialog box. The methods used to initialize and retrieve settings depend on the type of control.

+$#A Radio Buttons and Check Boxes

Dialog boxes use radio buttons and check boxes to let the user choose from a list of options. Radio buttons let the user choose from mutually exclusive options; check boxes let the user pick a combination of options.

The dialog box procedure can set the initial state of a check box by using the CheckDlgButton function, which sets or clears the check box. For radio buttons in a group of mutually exclusive radio buttons, the dialog box procedure can use the CheckRadioButton function to set the appropriate radio button and automatically clear any other radio button.

Before a dialog box terminates, the dialog box procedure can check the state of each radio button and check box by using the IsDlgButtonChecked function, which returns the current state of the button. A dialog box typically saves this information to initialize the buttons the next time it creates the dialog box.

+$#A Dialog Box Edit Controls

Many dialog boxes have edit controls that let the user supply text as input. Most dialog box procedures initialize an edit control when the dialog box first starts. For example, the dialog box procedure may place a proposed filename in the control that the user can then select, modify, or replace. The dialog box procedure can set the text in an edit control by using the SetDlgItemText function, which copies text from a given buffer to the edit control. When the edit control receives the input focus, it automatically selects the complete text for editing.

Because edit controls do not automatically return their text to the dialog box, the dialog box procedure must retrieve the text before it terminates. It can retrieve the text by using the GetDlgItemText function, which copies the edit control text to a buffer. The dialog box procedure typically saves this text to initialize the edit control later or passes it on to the parent window for processing.

Some dialog boxes use edit controls that let the user enter numbers. The dialog box procedure can retrieve a number from an edit control by using the GetDlgItemInt function, which retrieves the text from the edit control and converts the text to a decimal value. The user types the number in decimal digits. It can be either signed or unsigned. The dialog box procedure can display an integer by using the SetDlgItemInt function. SetDlgItemInt converts a signed or unsigned integer to a string of decimal digits.

+$#A List Boxes, Combo Boxes, and Directory Listings

Some dialog boxes display lists of names from which the user can select one or more names. To display a list of filenames, for example, a dialog box typically uses a list box and the DlgDirList and DlgDirSelect functions. The DlgDirList function automatically fills a list box with the filenames in the current directory. The DlgDirSelect function retrieves the selected filename from the list box. Together, these two functions provide a convenient way for a dialog box to display a directory listing so the user can select a file without having to type its name and location.

A dialog box can also use a combo box to display a list of filenames. The DlgDirListComboBox function automatically fills a list box portion of the combo box with the filenames in the current directory. The DlgDirSelectComboBox function retrieves a selected filename from the list box portion.

+$#A Dialog Box Control Messages

Many controls recognize predefined messages that, when received by controls, cause them to carry out some action. For example, the BM_SETCHECK message sets the check in a check box and the EM_GETSEL message retrieves the portion of the control's text that is currently selected. The control messages give a dialog procedure greater and more flexible access to the controls than the standard functions, so they are often used when the dialog box requires complex interactions with the user.

A dialog box procedure can send a message to a control by supplying the control identifier and using the SendDlgItemMessage function, which is identical to the SendMessage function, except that it uses a control identifier instead of a window handle to identify the control that is to receive the message. A given message may require that the dialog procedure send parameters with the message, and the message may have corresponding return values. The operation and requirements of each control message depends on the purpose of the message and the control that processes it.

For more information about the control messages, see Controls.

+$#A Custom Dialog Boxes

An application can create custom dialog boxes by using an application-defined window class for the dialog boxes instead of using the predefined dialog box class. Applications typically use this method when a dialog box is their main window, but it is also useful for creating modal and modeless dialog boxes for applications that have standard overlapping windows.

The application-defined window class allows the application to define a window procedure for the dialog box and process messages before sending them to the dialog box procedure. It also lets the application define a class icon, a class background brush, and a class menu for the dialog box. The application must register the window class before attempting to create a dialog box and must provide the dialog box template with the atom value or name of the window class.

Many applications create a new dialog box class by first retrieving the class information for the predefined dialog box class, and passing it to the GetClassInfo function, which fills a WNDCLASS structure with the information. The application modifies individual members of the structure, such as the class name, brush, and icon, then registers the new class by using the RegisterClass function. If an application fills the WNDCLASS structure on its own, it must set the cbWndExtra member to the DLGWINDOWEXTRA, which is the number of extra bytes Windows requires for each dialog box. If an application also uses extra bytes for each dialog box, they must be beyond the extra bytes required by Windows.

The window procedure for the custom dialog box has the same parameters and requirements as any other window procedure. Unlike other window procedures, however, the window procedure for this dialog box should call the DefDlgProc function instead of the DefWindowProc function for any messages it does not process. DefDlgProc carries out the same default message processing as the window procedure for the predefined dialog box, which includes calling the dialog box procedure.

An application can also create custom dialog boxes by subclassing the window procedure of the predefined dialog box. The SetWindowLong function lets an application set the address of the window procedure for a given window. The application may also attempt to subclass by using the SetClassLong function, but doing so affects all dialog boxes in the system, not just those belonging to the application.

Applications that create custom dialog boxes sometimes provide an alternate keyboard interface for the dialog boxes. For modeless dialog boxes, this may mean the application does not call the IsDialogMessage function and instead processes all keyboard input in the custom window procedure. In such cases, the application can use the WM_NEXTDLGCTL message to minimize the code needed to move the input focus from one control to another. This message, when passed to DefDlgProc, moves the input focus to a specified control and updates the appearance of the controls, such as moving the default push button border or setting an automatic radio button.

+$#A Using Dialog Boxes

You use dialog boxes to display information and prompt for input from the user. Your application loads and initializes the dialog box, processes user input, and destroys the dialog box when the user finishes the task. The process for handling dialog boxes varies, depending on whether the dialog box is modal or modeless. A modal dialog box requires the user to close the dialog box before activating another window in the application. However, the user can activate windows in different applications. A modeless dialog box does not require an immediate response from the user. It is similar to a main window containing controls. The following sections discuss how to use both types of dialog boxes.

• Displaying a message box

• Creating a modal dialog box

• Creating a modeless dialog box

• Initializing a dialog box

• Creating a template in memory

+$#A Displaying a Message Box

The simplest form of modal dialog box is the message box. Most applications use message boxes to warn the user of errors and to prompt for directions on how to proceed after an error has occurred. You create a message box by using the MessageBox or MessageBoxEx function, specifying the message and the number and type of buttons to display. Windows creates a modal dialog box, providing its own dialog box template and procedure. After the user closes the message box, MessageBox or MessageBoxEx returns a value identifying the button chosen by the user to close the message box.

In the following example, the application displays a message box if the fError variable is TRUE. The message box displays the message describing the error. The MB_OKCANCEL style directs MessageBox to provide two buttons with which the user can choose how to proceed:

if (fError) {

if (MessageBox(hwndDlg, SZNOTFOUND, SZDELETEITEM,

MB_OKCANCEL)==IDOK)

.

. // Prompt for a new item name and repeat the command.

.

else

.

. // Cancel the command.

.

}

In this example, SZNOTFOUND and SZDELETEITEM are application-defined, null-terminated strings that represent the message text and the title for the message box.

+$#A Creating a Modal Dialog Box

You create a modal dialog box by using the DialogBox function. You must specify the identifier or name of a dialog box template resource and the address of the dialog box procedure. The DialogBox function loads the template, displays the dialog box, and processes all user input until the user closes the dialog box.

In the following example, the application displays a modal dialog box when the user chooses a Delete Item command from an application menu. The dialog box contains an edit control (in which the user enters the name of an item) and OK and Cancel buttons. The control identifiers for these controls are ID_ITEMNAME, IDOK, and IDCANCEL, respectively.

The first part of the example consists of the statements that create the modal dialog box. These statements, in the window procedure for the application's main window, create the dialog box when the system receives a WM_COMMAND message having the IDM_DELETEITEM command identifier. The second part of the example is the dialog box procedure, which retrieves the contents of the edit control and closes the dialog box upon receiving a WM_COMMAND message.

The following statements create the modal dialog box. The dialog box template is a resource in the application's executable file and has the resource identifier DLG_DELETEITEM:

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDM_DELETEITEM:

if (DialogBox(hinst,

MAKEINTRESOURCE(DLG_DELETEITEM),

hwnd, (DLGPROC)DeleteItemProc)==IDOK)

.

. // Complete the command; szItemName

. // contains the name of the item to

. // delete.

.

else

.

. // Cancel the command.

.

break;

}

return 0L;

In this example, the application identifies its main window as the owner window for the dialog box. When Windows initially displays the dialog box, its position is relative to the upper left corner of the owner window's client area. The application uses the return value from DialogBox to determine whether to proceed with the command or cancel it. The following statements define the dialog box procedure.

char szItemName[80]; // receives name of item to delete.

BOOL CALLBACK DeleteItemProc(hwndDlg, message, wParam, lParam)

HWND hwndDlg;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

switch (message) {

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

if (!GetDlgItemText(hwndDlg, ID_ITEMNAME,

szItemName, 80))

*szItemName=0;

// Fall through.

case IDCANCEL:

EndDialog(hwndDlg, wParam);

return TRUE;

}

}

return FALSE;

}

In this example, the procedure uses GetDlgItemText to retrieve the current text from the edit control identified by ID_ITEMNAME. The procedure then calls the EndDialog function to set the dialog box's return value to either IDOK or IDCANCEL, depending on the message received, and to begin the process of closing the dialog box. The IDOK and IDCANCEL identifiers correspond to the OK and Cancel buttons. After the procedure calls EndDialog, Windows sends additional messages to the procedure to destroy the dialog box and returns the dialog box's return value back to the function that created the dialog box.

+$#A Creating a Modeless Dialog Box

You create a modeless dialog box by using the CreateDialog function, specifying the identifier or name of a dialog box template resource and the address of the dialog box procedure. CreateDialog loads the template, creates the dialog box, and optionally displays it. Your application is responsible for retrieving and dispatching user input messages to the dialog box procedure.

In the following example, the application displays a modeless dialog box ⎯ if it is not already displayed ⎯ when the user chooses a Go To command from an application menu. The dialog box contains an edit control, a check box, and OK and Cancel buttons. The dialog box template is a resource in the application's executable file and has the resource identifier DLG_GOTO. The user enters a line number in the edit control and checks the check box to specify that the line number is relative to the current line. The control identifiers are ID_LINE, ID_ABSREL, IDOK, and IDCANCEL.

The statements in the first part of the example create the modeless dialog box. These statements, in the window procedure for the application's main window, create the dialog box when the window procedure receives a WM_COMMAND message having the IDM_GOTO command identifier, but only if the global variable hwndGoto does not already contain a valid handle. The second part of the example is the application's main message loop. The loop includes the IsDialogMessage function to ensure that the user can use the dialog box keyboard interface in this modeless dialog box. The third part of the example is the dialog box procedure. The procedure retrieves the contents of the edit control and check box when the user chooses the OK button. The procedure destroys the dialog box when the user chooses the Cancel button.

HWND hwndGoto = NULL; // window handle of dialog box

.

.

.

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDM_GOTO:

if (!IsWindow(hwndGoto)) {

hwndGoto = CreateDialog(hinst,

MAKEINTRESOURCE(DLG_GOTO),

hwnd, (DLGPROC) GoToProc);

ShowWindow(hwndGoto, SW_SHOW);

}

break;

}

return 0L;

In the preceding statements, CreateDialog is called only if hwndGoto does not contain a valid window handle. This ensures that the application does not display two dialog boxes at the same time. To support this method of checking, the dialog procedure must set hwndGoto to NULL when it destroys the dialog box.

The message loop for an application consists of the following statements:

while (GetMessage(&msg, NULL, NULL, NULL)) {

if (!IsWindow(hwndGoto) || !IsDialogMessage(hwndGoto, &msg)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

The loop checks the validity of the window handle for the dialog box and only calls the IsDialogMessage function if the handle is valid. IsDialogMessage only processes the message if it belongs to the dialog box. Otherwise, it returns FALSE and the loop dispatches the message to the appropriate window.

The following statements define the dialog box procedure:

int iLine; // receives line number

BOOL fRelative; // receives check box status

.

.

.

BOOL CALLBACK GoToProc(hwndDlg, message, wParam, lParam)

HWND hwndDlg;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

BOOL fError;

switch (message) {

case WM_INITDIALOG:

CheckDlgButton(hwndDlg, ID_ABSREL, fRelative);

return TRUE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

fRelative = IsDlgButtonChecked(hwndDlg,

ID_ABSREL);

iLine = GetDlgItemInt(hwndDlg, ID_LINE,

&fError, fRelative);

if (fError) {

MessageBox(hwndDlg, SZINVALIDNUMBER,

SZGOTOERR, MB_OK);

SendDlgItemMessage(hwndDlg, ID_LINE,

EM_SETSEL, 0, -1L);

} else

.

. // Notify the owner window to carry

. // out the command.

.

return TRUE;

case IDCANCEL:

DestroyWindow(hwndDlg);

hwndGoto = NULL;

return TRUE;

}

}

return FALSE;

}

In the preceding statements, the procedure processes the WM_INITDIALOG and WM_COMMAND messages. During WM_INITDIALOG processing, the procedure initializes the check box by passing the current value of the global variable fRelative to CheckDlgButton. The procedure then returns TRUE to direct Windows to set the default input focus.

During WM_COMMAND processing, the procedure closes the dialog box only if the user chooses the Cancel button ⎯ that is, the button having the IDCANCEL identifier. The procedure must call DestroyWindow to close a modeless dialog box. Notice that the procedure also sets the hwndGoto variable to NULL to ensure that other statements that depend on this variable operate correctly.

If the user chooses the OK button, the procedure retrieves the current state of the check box and assigns it to the fRelative variable. It then uses the variable to retrieve the line number from the edit control. GetDlgItemInt translates the text in the edit control into an integer. The value of fRelative determines whether the function interprets the number as a signed or unsigned value. If the edit control text is not a valid number, GetDlgItemInt sets the value of the fError variable to nonzero. The procedure checks this value to determine whether to display an error message or carry out the command. In the event of an error, the dialog box procedure sends a message to the edit control, directing it to select the text in the control so that the user can easily replace it. If GetDlgItemInt does not return an error, the procedure can either carry out the requested command itself or send a message to the owner window, directing it to carry out the command.

+$#A Initializing a Dialog Box

You initialize the dialog box and its contents while processing the WM_INITDIALOG message. The most common task is to initialize the controls to reflect the current dialog box settings. Another common task is to center a dialog box on the screen or within its owner window. A useful task for some dialog boxes is to set the input focus to a given control rather than accept the default input focus.

In the following example, the dialog box procedure centers the dialog box and sets the input focus while processing the WM_INITDIALOG message. To center the dialog box, the procedure retrieves the window rectangles for the dialog box and the owner window and calculates a new position for the dialog box. To set the input focus, the procedure checks the wParam parameter to determine the identifier of the default input focus:

HWND hwndOwner;

RECT rc, rcDlg, rcOwner;

case WM_INITDIALOG:

// Get the owner window and dialog box rectangles.

if ((hwndOwner = GetParent(hwndDlg)) == NULL)

hwndOwner = GetDesktopWindow();

GetWindowRect(hwndOwner, &rcOwner);

GetWindowRect(hwndDlg, &rcDlg);

CopyRect(&rc, &rcOwner);

// Offset the owner and dialog box rectangles so that

// right and bottom values represent the width and

// height, and then offset the owner again to discard

// space taken up by the dialog box.

OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);

OffsetRect(&rc, -rc.left, -rc.top);

OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);

// The new position is the sum of half the remaining

// space and the owner's original position.

SetWindowPos(hwndDlg,

HWND_TOP,

rcOwner.left + (rc.right / 2),

rcOwner.top + (rc.bottom / 2),

0, 0, // ignores size arguments

SWP_NOSIZE);

if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME) {

SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME));

return FALSE;

}

return TRUE;

In the preceding statements, the procedure uses the GetParent function to retrieve the owner window handle for a dialog box. The function returns the owner window handle for dialog boxes, and the parent window handle for child windows. Because an application can create a dialog box that has no owner, the procedure checks the returned handle and uses the GetDesktopWindow function to retrieve the desktop window handle, if necessary. After calculating the new position, the procedure uses the SetWindowPos function to move the dialog box, specifying the HWND_TOP value to ensure that the dialog box remains on top of the owner window.

Before setting the input focus, the procedure checks the control identifier of the default input focus. Windows passes the window handle of the default input focus in the wParam parameter. The GetDlgCtrlID function returns the identifier for the control identified by the window handle. If the identifier does not match the correct identifier, the procedure uses the SetFocus function to set the input focus. The GetDlgItem function is required to retrieve the window handle of the desired control.

+$#A Creating a Template in Memory

Applications sometimes adapt or modify the content of dialog boxes depending on the current state of the data being processed. In such cases, it is not practical to provide all possible dialog box templates as resources in the application's executable file. But creating templates in memory gives the application more flexibility to adapt to any circumstances.

In the following example, the application creates a template in memory for a modal dialog box that contains a message and OK and Help buttons:

LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner,

LPSTR lpszMessage)

{

HGLOBAL hgbl;

LPDLGTEMPLATE lpdt;

LPDLGITEMTEMPLATE lpdit;

LPWORD lpw;

LPWSTR lpwsz;

LRESULT ret;

hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);

if (!hgbl)

return -1;

lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);

// Define a dialog box.

lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU

| DS_MODALFRAME | WS_CAPTION;

lpdt->cdit = 3; // number of controls

lpdt->cdit = 1;

lpdt->x = 10; lpdt->y = 10;

lpdt->cx = 100; lpdt->cy = 100;

lpw = (LPWORD) (lpdt + 1);

*lpw++ = 0; // no menu

*lpw++ = 0; // predefined dialog box class (by default)

lpwsz = (LPWSTR) lpw;

lstrcpyW(lpwsz, L"My Message"); // dialog title (Unicode)

lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);

// Define an OK button.

lpdit = (LPDLGITEMTEMPLATE) lpw;

lpdit->x = 10; lpdit->y = 10;

lpdit->cx = 40; lpdit->cy = 20;

lpdit->id = IDOK; // OK button identifier

lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;

lpw = (LPWORD) (lpdit + 1);

*lpw++ = 0xFFFF;

*lpw++ = 0x0080; // button class

lpwsz = (LPWSTR) lpw;

lstrcpyW(lpwsz, L"OK"); // button label (Unicode)

lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);

*lpw++ = 0; // no creation data

// Define a Help button.

lpdit = (LPDLGITEMTEMPLATE) lpw;

lpdit->x = 55; lpdit->y = 10;

lpdit->cx = 40; lpdit->cy = 20;

lpdit->id = ID_HELP; // Help button identifier

lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;

lpw = (LPWORD) (lpdit + 1);

*lpw++ = 0xFFFF;

*lpw++ = 0x0080; // button class atom

lpwsz = (LPWSTR) lpw;

lstrcpyW(lpwsz, L"Help"); // button label (Unicode)

lpw = (LPWORD) (lpwsz + lstrlenW(lpwsz) + 1);

*lpw++ = 0; // no creation data

// Define a static text control.

lpdit = (LPDLGITEMTEMPLATE) lpw;

lpdit->x = 55; lpdit->y = 10;

lpdit->cx = 40; lpdit->cy = 20;

lpdit->id = ID_TEXT; // text identifier

lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;

lpw = (LPWORD) (lpdit + 1);

*lpw++ = 0xFFFF;

*lpw++ = 0x0082; // static class

for (lpwsz = (LPWSTR) lpw;

*lpwsz++ = (WCHAR) *lpszMessage++; // message

);

lpw = (LPWORD) lpwsz;

*lpw++ = 0; // no creation data

GlobalUnlock(hgbl);

ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,

hwndOwner, (DLGPROC) DialogProc);

GlobalFree(hgbl);

return ret;

}

+$#AK Dialog Box Reference

The following functions, structures, and messages are associated with dialog boxes.

+$#A Dialog Box Functions

Following are the functions used to create and manage dialog boxes and controls within dialog boxes.

CreateDialog
CreateDialogIndirect
CreateDialogIndirectParam
CreateDialogParam
DefDlgProc
DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
DialogProc
EndDialog
GetDialogBaseUnits
GetDlgCtrlID
GetDlgItem
GetDlgItemInt
GetDlgItemText
GetNextDlgGroupItem
GetNextDlgTabItem
IsDialogMessage
MapDialogRect
MessageBox
MessageBoxEx
SendDlgItemMessage
SetDlgItemInt
SetDlgItemText
MessageBoxIndirect

+$#A Dialog Box Structures

Following are the structures used to create and manage dialog boxes and controls within dialog boxes.

DLGITEMTEMPLATE
DLGTEMPLATE
MSGBOXPARAMS

+$#A Dialog Box Messages

Following are the messages used to create and manage dialog boxes and controls within dialog boxes.

WM_CTLCOLORMSGBOX
WM_ENTERIDLE
WM_GETDLGCODE
WM_INITDIALOG
WM_NEXTDLGCTL

Créé avec HelpNDoc Personal Edition: Produire des livres Kindle gratuitement


Drag List Boxes.rtf

+$#AK Drag List Boxes

A drag list box is a special type of list box that enables the user to drag items from one position to another. An application can use a drag list box to display strings in a particular sequence and allow the user to change the sequence.

+$#A About Drag List Boxes

To create a drag list box, you first create a standard list box and then call the MakeDragList function. To convert a list box in a dialog box to a drag list box, you can call MakeDragList when the WM_INITDIALOG message is processed.

Drag list boxes have the same window styles and process the same messages as standard list boxes.

+$#A About Drag List Box Messages

A drag list box notifies its parent window of drag events by sending it a drag list message. The parent window must process the drag list message.

The drag list box registers the drag list message when the MakeDragList function is called. To get the message identifer (numeric value) of the drag list message, you must call the RegisterWindowMessage function, specifying the DRAGLISTMSGSTRING value.

The wParam parameter of the drag list message is the control identifier for the drag list box. The lParam parameter is the pointer to a DRAGLISTINFO structure, which contains the notification code for the drag event and other information. The return value of the message depends on the notification.

+$#A About Drag List Box Notification Messages

A drag list box sends its parent window notification messages in the form of drag list messages. The specific notification, which is identified by the uNotification member of the specified DRAGLISTINFO structure, can be DL_BEGINDRAG, DL_DRAGGING, DL_CANCELDRAG, or DL_DROPPED.

The DL_BEGINDRAG notification message is sent when the user clicks the left mouse button with the cursor on a list item. The parent window can return TRUE to begin the drag operation or FALSE to disallow dragging. In this way, the parent window can enable dragging for some list items and disable it for others. You can determine which list item is at the specified location by using the LBItemFromPt function.

If dragging is in effect, the DL_DRAGGING notification message is sent whenever the mouse is moved, or at regular intervals if the mouse is not being moved. The parent window should determine the list item under the cursor by using LBItemFromPt and then draw the insert icon by using the DrawInsert function. By specifying TRUE for the bAutoScroll parameter of LBItemFromPt, you can cause the list box to scroll by one line if the cursor is above or below its client area. The value you return for this notification specifies the type of mouse cursor that the drag list box should set.

The DL_CANCELDRAG notification message is sent if the user cancels a drag operation by clicking the right mouse button or pressing the ESC key. The DL_DROPPED notification message is sent if the user completes a drag operation by releasing the left mouse button, even if the cursor is not over a list item. The drag list box releases the mouse capture before sending either notification. The return value of these two notifications is ignored.

+$#A Drag List Box Reference

The following functions, notification messages, and structures are associated with drag list boxes.

+$#A Drag List Box Functions

The following functions are used with drag list boxes.

DrawInsert
LBItemFromPt
MakeDragList

+$#A Drag List Box Notification Messages

A drag list box sends notification messages to its parent window by using a registered window message, which is called a drag list message. There are the following drag list notification messages.

DL_BEGINDRAG
DL_CANCELDRAG
DL_DRAGGING
DL_DROPPED

+$#A Drag List Box Structure

The following structure is used with drag list boxes.

DRAGLISTINFO

Créé avec HelpNDoc Personal Edition: Générer des livres électroniques EPub facilement


Dynamic Data Exchange Management Library.rtf

+$#AK Dynamic Data Exchange Management Library

In Microsoft® Windows®, dynamic data exchange (DDE) is a form of interprocess communications that uses shared memory to exchange data between applications. Applications can use DDE for one-time data transfers and for ongoing exchanges and updating of data.

+$#A About the DDEML

DDE differs from the clipboard data-transfer mechanism that is also part of the Windows operating system. One difference is that the clipboard is almost always used as a one-time response to a specific action by the user ⎯ such as choosing the Paste command from a menu. Although DDE may also be initiated by a user, it typically continues without the user's further involvement.

The dynamic data exchange management library (DDEML) provides an application programming interface (API) that simplifies the task of adding DDE capability to a Windows application. Instead of sending, posting, and processing DDE messages directly, an application uses the functions provided by the DDEML to manage DDE conversations. A DDE conversation is the interaction between client and server applications. The DDEML also provides a means for managing the strings and data shared among DDE applications. Instead of using atoms and pointers to shared memory objects, DDE applications create and exchange string handles, which identify strings, and data handles, which identify DDE objects. The DDEML provides a function (DdeNameService) that enables a server application to register the service names it supports. The service names are then broadcast to other applications in the system, which use the names to connect to the server. The DDEML also ensures compatibility among DDE applications by requiring them to implement the DDE protocol in a consistent manner.

Existing applications using the message-based DDE protocol are fully compatible with those that use the DDEML; that is, an application using message-based DDE can establish conversations and perform transactions with applications using the DDEML. Instead of using DDE messages in your new application, take advantage of the DDEML and the many improvements it offers.

To use the API elements of the DDEML, you must include the DDEML.H header file in your source files, link with the USER32.LIB file, and ensure that the DDEML.DLL file resides in the system's path.

+$#A Basic Concepts

The concepts in this section are key to understanding DDE and the DDEML.

+$#A Client and Server Interaction

DDE always occurs between a client application and a server application. The DDE client application initiates the exchange by establishing a conversation with the server to send transactions to the server. A transaction is a request for data or services. The DDE server application responds to transactions by providing data or services to the client. For example, a graphics application might contain a bar graph representing a corporation's quarterly profits, but the data for the bar graph might be contained in a spreadsheet application. To obtain the latest profit figures, the graphics application (the client) could establish a conversation with the spreadsheet application (the server). The graphics application could then send a transaction to the spreadsheet application, requesting the latest profit figures.

A server can have many clients at the same time, and a client can request data from multiple servers. An application can also be both a client and a server. Either the client or the server can terminate the conversation at any time.

+$#A Transactions and the DDE Callback Function

The DDEML notifies an application about DDE activity that affects the application by sending transactions to the application's DDE callback function. A DDE transaction is similar to a message ⎯ it is a named constant accompanied by other parameters that contain additional information about the transaction.

The DDEML passes a transaction to an application-defined DDE callback function that carries out an action appropriate to the type of transaction. For example, when a client application attempts to establish a conversation with a server application, the client calls the DdeConnect function. This function causes the DDEML to send an XTYP_CONNECT transaction to the server's DDE callback function. The callback function can allow the conversation by returning TRUE to the DDEML, or it can deny the conversation by returning FALSE. For a detailed discussion of transactions, see Transaction Management.

+$#A Service Names, Topic Names, and Item Names

A DDE server uses a three-level hierarchy ⎯ service name (called "application name" in previous DDE documentation), topic name, and item name ⎯ to uniquely identify a unit of data the server can exchange during a conversation.

A service name is a string a server application responds to when a client attempts to establish a conversation with the server. A client must specify this service name to establish a conversation with the server. Although a server can respond to many service names, most servers respond to only one name.

A topic name is a string that identifies a logical data context. For servers that operate on file-based documents, topic names are typically filenames; for other servers, they are other application-specific strings. A client must specify a topic name along with a server's service name when it attempts to establish a conversation with a server.

An item name is a string that identifies a unit of data a server can pass to a client during a transaction. For example, an item name might identify an integer, a string, several paragraphs of text, or a bitmap.

The service, topic, and item names enable the client to establish a conversation with a server and to receive data from the server.

+$#A System Topic

The System topic provides a context for information of general interest to any DDE client. It is recommended that server applications support the System topic at all times. The System topic is defined in the DDEML.H header file as SZDDESYS_TOPIC.

To determine which servers are present and the kinds of information they can provide, a client application can request a conversation on the System topic upon starting, setting the device name to NULL. Such wildcard conversations are costly in terms of system performance, so they should be kept to a minimum. For more information about initiating DDE conversations, see Conversation Management.

A server must support the following item names within the System topic and any other item names that are useful to a client.

Item | Description
SZDDE_ITEM_ITEMLIST | A list of the items supported under a non-System topic. (This list may vary from moment to moment and from topic to topic.)
SZDDESYS_ITEM_FORMATS | A tab-delimited list of strings representing all clipboard formats potentially supported by the service application. Strings that represent predefined clipboard formats are equivalent to the CF_ values with the "CF_" prefix removed. For example, the CF_TEXT format is represented by the string "TEXT". These strings must be uppercase to further identify them as predefined formats. The list of formats must appear in the order of most rich in content to least rich in content.
 | For more information about clipboard formats and rendering data, see Clipboard.
SZDDESYS_ITEM_HELP | User-readable information of general interest. This item must contain, at a minimum, information on how to use the server application's DDE features. This information may include, but is not limited to, how to specify items within topics, what execute strings the server can perform, what poke transactions are allowed, and how to find help on other System topic items.
SZDDESYS_ITEM_RTNMSG | Supporting detail for the most recently used WM_DDE_ACK message. This item is useful when more than 8 bits of application-specific return data are required.
SZDDESYS_ITEM_STATUS | An indication of the current status of the server. Typically, this item supports only the CF_TEXT format and contains the Ready or Busy string.
SZDDESYS_ITEM_SYSITEMS | A list of the items supported under the System topic by this server.
SZDDESYS_ITEM_TOPICS | A list of the topics supported by the server at the current time. (This list may vary from moment to moment.)

These item names are values defined in the DDEML.H header file. To obtain string handles for these strings, an application must use the DDEML string-management functions, just as it would for any other string in a DDEML application. For more information about managing strings, see String Management.

+$#A DDEML and Threads

The DDEInitialize function registers an application with the DDEML, creating a DDEML instance. A DDEML instance is thread-based, associated with the thread that called DDEInitialize.

All DDEML function calls for objects belonging to a DDEML instance must be made from the same thread that called DDEInitialize to create the instance. If you call a DDEML function from a different thread, the function will fail. You cannot access a DDEML conversation from a thread other than the one that allocated the conversation.

+$#A Initialization

Before calling any other DDEML function, an application must call the DdeInitialize function. DdeInitialize obtains an instance identifier for the application, registers the application's DDE callback function with the DDEML, and specifies the transaction filter flags for the callback function.

Each instance of an application or a dynamic-link library (DLL) must pass its instance identifier as the idInst parameter to any other DDEML function that requires it. The purpose of multiple DDEML instances is to support DLLs that must use the DDEML at the same time an application is using it. An application must not use more than one instance of the DDEML.

Transaction filters optimize system performance by preventing the DDEML from passing unwanted transactions to the application's DDE callback function. An application sets the transaction filters when it calls DdeInitialize. An application must specify a transaction filter flag for each type of transaction that it does not process in its callback function. An application can change its transaction filters with a subsequent call to DdeInitialize. For a complete list of transaction filter flags, see the description of DdeInitialize in the Microsoft Win32 Programmer's Reference, Volume 3. For more information about transactions, see Transaction Management.

The following example shows how to initialize an application to use the DDEML.

DWORD idInst = 0;

HINSTANCE hinst;

.

.

.

DdeInitialize(&idInst, /* receives instance identifier */

(PFNCALLBACK) DdeCallback, /* address of callback function */

CBF_FAIL_EXECUTES | /* filter XTYPE_EXECUTE */

CBF_SKIP_ALLNOTIFICATIONS, /* filter notifications */

0);

.

.

.

An application must call the DdeUninitialize function when it is no longer going to use the DDEML. This function terminates any conversations currently open for the application and frees the DDEML resources the system allocated for the application.

+$#A Callback Function

An application that uses the DDEML must provide a callback function that processes the DDE events affecting the application. The DDEML notifies an application of such events by sending transactions to the application's DDE callback function. The transactions a callback function receives depend on which callback filter flags the application specified in DdeInitialize and whether the application is a client, a server, or both. The following example shows the general structure of a callback function for a typical client application.

HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1,

hsz2, hdata, dwData1, dwData2)

UINT uType; /* transaction type */

UINT uFmt; /* clipboard data format */

HCONV hconv; /* handle of conversation */

HSZ hsz1; /* handle of string */

HSZ hsz2; /* handle of string */

HDDEDATA hdata; /* handle of global memory object */

DWORD dwData1; /* transaction-specific data */

DWORD dwData2; /* transaction-specific data */

{

switch (uType) {

case XTYP_REGISTER:

case XTYP_UNREGISTER:

.

.

.

return (HDDEDATA) NULL;

case XTYP_ADVDATA:

.

.

.

return (HDDEDATA) DDE_FACK;

case XTYP_XACT_COMPLETE:

.

.

.

return (HDDEDATA) NULL;

case XTYP_DISCONNECT:

.

.

.

return (HDDEDATA) NULL;

default:

return (HDDEDATA) NULL;

}

}

The uType parameter specifies the transaction type sent to the callback function by the DDEML. The values of the remaining parameters depend on the transaction type. The transaction types and the events that generate them are described in the following topics. For detailed information about each transaction type, see Transaction Management.

+$#A String Management

To carry out a DDE task, many DDEML functions require access to strings. For example, a client must specify a service name and a topic name when it calls the DdeConnect function to request a conversation with a server. An application specifies a string by passing a string handle (HSZ) rather than a pointer in a DDEML function. A string handle is a doubleword value, assigned by the system, that identifies a string.

An application can obtain a string handle for a particular string by calling the DdeCreateStringHandle function. This function registers the string with the system and returns a string handle to the application. The application can pass the handle to DDEML functions that must access the string. The following example obtains string handles for the System topic string and the service name string.

HSZ hszServName;

HSZ hszSysTopic;

.

.

.

hszServName = DdeCreateStringHandle(

idInst, /* instance identifier */

"MyServer", /* string to register */

CP_WINANSI); /* Windows ANSI code page */

hszSysTopic = DdeCreateStringHandle(

idInst, /* instance identifier */

SZDDESYS_TOPIC, /* System topic */

CP_WINANSI); /* Windows ANSI code page */

.

.

.

The idInst parameter in the preceding example specifies the instance identifier obtained by the DdeInitialize function.

An application's DDE callback function receives one or more string handles during most DDE transactions. For example, a server receives two string handles during the XTYP_REQUEST transaction: one identifies a string specifying a topic name, and the other identifies a string specifying an item name. An application can obtain the length of the string that corresponds to a string handle and copy the string to an application-defined buffer by calling the DdeQueryString function, as shown in the following example.

DWORD idInst;

DWORD cb;

HSZ hszServ;

PSTR pszServName;

.

.

.

cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0,

CP_WINANSI) + 1;

pszServName = (PSTR) LocalAlloc(LPTR, (UINT) cb);

DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI);

.

.

.

An instance-specific string handle cannot be mapped from string handle to string and back to string handle. For instance, although DdeQueryString creates a string from a string handle and then DdeCreateStringHandle creates a string handle from that string, the two handles are not the same, as shown in the following example.

DWORD idInst;

DWORD cb;

HSZ hszInst, hszNew;

PSZ pszInst;

.

.

.

DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI);

hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI);

/* hszNew != hszInst ! */

.

.

.

A string handle passed to an application's DDE callback function becomes invalid when the callback function returns. An application can save a string handle for use after the callback function returns by using the DdeKeepStringHandle function.

When an application calls DdeCreateStringHandle, the system enters the specified string into a string table and generates a handle that it uses to access the string. The system also maintains a usage count for each string in the string table.

When an application calls DdeCreateStringHandle and specifies a string that already exists in the table, the system increments the usage count rather than adding another occurrence of the string. (An application can also increment the usage count by using DdeKeepStringHandle.) When an application calls the DdeFreeStringHandle function, the system decrements the usage count.

A string is removed from the table when its usage count equals zero. Because more than one application can obtain the handle of a particular string, an application must not free a string handle more times than it has created or retained the handle. Otherwise, the application can cause the string to be removed from the table, denying other applications access to the string.

The DDEML string-management functions are based on the Windows atom manager and are subject to the same size restrictions as are atoms.

+$#A Name Service

The DDEML makes it possible for a server application to register the service names that it supports and to prevent the DDEML from sending XTYP_CONNECT transactions for unsupported service names to the server's DDE callback function. The remaining topics in this section describe the name service.

+$#A Service Name Registration

By registering its service names with the DDEML, a server informs other DDE applications in the system that a new server is available. A server registers a service name by calling the DdeNameService function and specifying a string handle that identifies the name. In response, the DDEML sends an XTYP_REGISTER transaction to the callback function of each DDEML application in the system (except those that specified the CBF_SKIP_REGISTRATIONS filter flag in the DdeInitialize function). The XTYP_REGISTER transaction passes two string handles to a callback function: the first identifies the string specifying the base service name, and the second identifies the string specifying the instance-specific service. A client typically uses the base service name in a list of available servers, so the user can select a server from the list. The client uses the instance-specific service name to establish a conversation with a specific instance of a server application, if more than one instance is running.

A server can use DdeNameService to unregister a service name. This function causes the DDEML to send XTYP_UNREGISTER transactions to the other DDE applications in the system, informing them that they can no longer use the name to establish conversations.

A server must call DdeNameService to register its service names soon after calling DdeInitialize. A server must unregister its service names by using DdeNameService just before calling the DdeUninitialize function.

+$#A Service Name Filter

In addition to registering service names, DdeNameService enables a server to turn its service name filter on or off. When a server turns off its service name filter, the DDEML sends the XTYP_CONNECT transaction to the server's DDE callback function whenever any client calls the DdeConnect function, regardless of the service name specified in the function. When a server turns on its service name filter, the DDEML sends the XTYP_CONNECT transaction to the server only when DdeConnect specifies a service name the server has specified in a call to DdeNameService.

By default, the service name filter is on when an application calls DdeInitialize. This default prevents the DDEML from sending the XTYP_CONNECT transaction to a server before the server has created the string handles it needs. A server can turn off its service name filter by specifying the DNS_FILTEROFF flag in a call to DdeNameService. The DNS_FILTERON flag turns on the filter.

+$#A Conversation Management

A conversation between a client and a server is always established at the request of the client. When a conversation is established, each partner receives a handle that identifies the conversation. The partners use this handle in other DDEML functions to send transactions and manage the conversation. A client can request a conversation with a single server, or it can request multiple conversations with one or more servers.

The remaining topics in this section describe how an application establishes new conversations and gets information about existing conversations.

+$#A Single Conversations

A client application requests a single conversation with a server by calling the DdeConnect function and specifying string handles that identify the strings containing the service name of the server application and the topic name for the conversation. The DDEML responds by sending the XTYP_CONNECT transaction to the DDE callback function of each server application that either has registered a service name that matches the one specified in DdeConnect or has turned service name filtering off by calling DdeNameService. A server can also filter XTYP_CONNECT transactions by specifying the CBF_FAIL_CONNECTIONS filter flag in the DdeInitialize function. During the XTYP_CONNECT transaction, the DDEML passes the service name and the topic name to the server. The server must examine the names and return TRUE if it supports the service name and topic name pair or FALSE if it does not.

If no server responds positively to the client's request to connect, the client receives NULL from DdeConnect and no conversation is established. If a server returns TRUE, a conversation is established and the client receives a conversation handle ⎯ a doubleword value that identifies the conversation. The client uses the handle in subsequent DDEML calls to obtain data from the server. The server receives the XTYP_CONNECT_CONFIRM transaction (unless the server specified the CBF_SKIP_CONNECT_CONFIRMS filter flag). This transaction passes the conversation handle to the server.

The following example requests a conversation on the System topic with a server that recognizes the service name MyServer. The hszServName and hszSysTopic parameters are previously created string handles.

HCONV hConv; /* conversation handle */

HWND hwndParent; /* parent window handle */

HSZ hszServName; /* service name string handle */

HSZ hszSysTopic; /* System topic string handle */

.

.

hConv = DdeConnect(

idInst, /* instance identifier */

hszServName, /* service name string handle */

hszSysTopic, /* System topic string handle */

(PCONVCONTEXT) NULL); /* use default context */

if (hConv == NULL) {

MessageBox(hwndParent, "MyServer is unavailable.",

(LPSTR) NULL, MB_OK);

return FALSE;

}

.

.

.

In the preceding example, DdeConnect causes the DDE callback function of the MyServer application to receive an XTYP_CONNECT transaction.

In the following example, the server responds to the XTYP_CONNECT transaction by comparing the topic name string handle the DDEML passed to the server with each element in the array of topic name string handles the server supports. If the server finds a match, it establishes the conversation.

#define CTOPICS 5

HSZ hsz1; /* string handle passed by DDEML */

HSZ ahszTopics[CTOPICS]; /* array of supported topics */

int i; /* loop counter */

.

. /* Use a switch statement to examine transaction types. */

.

case XTYP_CONNECT:

for (i = 0; i < CTOPICS; i++) {

if (hsz1 == ahszTopics[i])

return TRUE; /* establish a conversation */

}

return FALSE; /* topic not supported; deny conversation */

.

. /* Process other transaction types. */

.

If the server returns TRUE in response to the XTYP_CONNECT transaction, the DDEML sends an XTYP_CONNECT_CONFIRM transaction to the server's DDE callback function. The server can obtain the handle for the conversation by processing this transaction.

A client can establish a wildcard conversation by specifying NULL for the service name string handle, the topic name string handle, or both in a call to DdeConnect. If at least one of the string handles is NULL, the DDEML sends the XTYP_WILDCONNECT transaction to the callback functions of all DDE applications (except those that filter the XTYP_WILDCONNECT transaction). Each server application should respond by returning a data handle that identifies a null-terminated array of HSZPAIR structures. If the server application has not called DdeNameService to register its service names and if filtering is on, the server does not receive XTYP_WILDCONNECT transactions. For more information about data handles, see Data Management.

The array must contain one structure for each service name and topic name pair that matches the pair specified by the client. The DDEML selects one of the pairs to establish a conversation and returns to the client a handle that identifies the conversation. The DDEML sends the XTYP_CONNECT_CONFIRM transaction to the server (unless the server filters this transaction). The following example shows a typical server response to the XTYP_WILDCONNECT transaction.

#define CTOPICS 2

UINT uType;

HSZPAIR ahszp[(CTOPICS + 1)];

HSZ ahszTopicList[CTOPICS];

HSZ hszServ, hszTopic;

WORD i, j;

if (uType == XTYP_WILDCONNECT) {

/*

* Scan the topic list and create an array of HSZPAIR

* structures.

*/

j = 0;

for (i = 0; i < CTOPICS; i++) {

if (hszTopic == (HSZ) NULL ||

hszTopic == ahszTopicList[i]) {

ahszp[j].hszSvc = hszServ;

ahszp[j++].hszTopic = ahszTopicList[i];

}

}

/*

* End the list with an HSZPAIR structure that contains NULL

* string handles as its members.

*/

ahszp[j].hszSvc = NULL;

ahszp[j++].hszTopic = NULL;

/*

* Return a handle to a global memory object containing the

* HSZPAIR structures.

*/

return DdeCreateDataHandle(

idInst, /* instance identifier */

(LPBYTE) &ahszp, /* points to HSZPAIR array */

sizeof(HSZ) * j, /* length of the array */

0, /* start at the beginning */

(HSZ) NULL, /* no item name string */

0, /* return the same format */

0); /* let the system own it */

}

Either the client or the server can terminate a conversation at any time by calling the DdeDisconnect function. This function causes the callback function of the partner in the conversation to receive the XTYP_DISCONNECT transaction (unless the partner specified the CBF_SKIP_DISCONNECTS filter flag). Typically, an application responds to the XTYP_DISCONNECT transaction by using the DdeQueryConvInfo function to obtain information about the conversation that terminated. After the callback function returns from processing the XTYP_DISCONNECT transaction, the conversation handle is no longer valid.

A client application that receives an XTYP_DISCONNECT transaction in its DDE callback function can attempt to reestablish the conversation by calling the DdeReconnect function. The client must call DdeReconnect from within its DDE callback function.

+$#A Multiple Conversations

A client application can use the DdeConnectList function to determine whether any servers of interest are available in the system. A client specifies a service name and topic name when it calls DdeConnectList, causing the DDEML to broadcast the XTYP_WILDCONNECT transaction to the DDE callback functions of all servers that match the service name (except those that filter the transaction). A server's callback function should return a data handle that identifies a null-terminated array of HSZPAIR structures. The array should contain one structure for each service name and topic name pair that matches the pair specified by the client. The DDEML establishes a conversation for each HSZPAIR structure filled by the server and returns a conversation list handle to the client. The server receives the conversation handle by way of the XTYP_CONNECT_CONFIRM transaction (unless the server filters this transaction).

A client can specify NULL for the service name, topic name, or both when it calls DdeConnectList. If the service name is NULL, all servers in the system that support the specified topic name respond. A conversation is established with each responding server, including multiple instances of the same server. If the topic name is NULL, a conversation is established on each topic recognized by each server that matches the service name.

A client can use the DdeQueryNextServer and DdeQueryConvInfo functions to identify the servers that respond to DdeConnectList. DdeQueryNextServer returns the next conversation handle in a conversation list, and DdeQueryConvInfo fills a CONVINFO structure with information about the conversation. The client can keep the conversation handles that it needs and discard the rest from the conversation list.

The following example uses DdeConnectList to establish conversations with all servers that support the System topic and then uses the DdeQueryNextServer and DdeQueryConvInfo functions to obtain the servers' service name string handles and store them in a buffer.

HCONVLIST hconvList; /* conversation list */

DWORD idInst; /* instance identifier */

HSZ hszSystem; /* System topic */

HCONV hconv = NULL; /* conversation handle */

CONVINFO ci; /* holds conversation data */

UINT cConv = 0; /* count of conv. handles */

HSZ *pHsz, *aHsz; /* point to string handles */

/* Connect to all servers that support the System topic. */

hconvList = DdeConnectList(idInst, NULL, hszSystem, NULL, NULL);

/* Count the number of handles in the conversation list. */

while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL)

cConv++;

/* Allocate a buffer for the string handles. */

hconv = NULL;

aHsz = (HSZ *) LocalAlloc(LMEM_FIXED, cConv * sizeof(HSZ));

/* Copy the string handles to the buffer. */

pHsz = aHsz;

while ((hconv = DdeQueryNextServer(hconvList, hconv)) != NULL) {

DdeQueryConvInfo(hconv, QID_SYNC, (PCONVINFO) &ci);

DdeKeepStringHandle(idInst, ci.hszSvcPartner);

*pHsz++ = ci.hszSvcPartner;

}

.

. /* Use the handles; converse with the servers. */

.

/* Free the memory and terminate the conversations. */

LocalFree((HANDLE) aHsz);

DdeDisconnectList(hconvList);

An application can terminate an individual conversation in a conversation list by calling the DdeDisconnect function. An application can terminate all conversations in a conversation list by calling the DdeDisconnectList function. Both functions cause the DDEML to send XTYP_DISCONNECT transactions to each partner's DDE callback function. DdeDisconnectList sends an XTYP_DISCONNECT transaction for each conversation handle in the list.

A client can retrieve a list of the conversation handles in a conversation list by passing an existing conversation list handle to DdeConnectList. The enumeration process removes the handles of terminated conversations from the list, and nonduplicate conversations that fit the specified service name and topic name are added.

If DdeConnectList specifies an existing conversation list handle, the function creates a new conversation list that contains the handles of any new conversations and the handles from the existing list.

If duplicate conversations exist, DdeConnectList attempts to prevent duplicate conversation handles in the conversation list. A duplicate conversation is a second conversation with the same server on the same service name and topic name. Two such conversations would have different handles, yet they would identify the same conversation.

+$#A Data Management

Because DDE uses memory objects to pass data from one application to another, the DDEML provides a set of functions that DDE applications can use to create and manage DDE objects.

All transactions that involve the exchange of data require the application supplying the data to create a local buffer containing the data and then to call the DdeCreateDataHandle function. This function allocates a DDE object, copies the data from the buffer to the object, and returns a data handle. A data handle is a doubleword value that the DDEML uses to provide access to data in the DDE object. To share the data in a DDE object, an application passes the data handle to the DDEML, and the DDEML passes the handle to the DDE callback function of the application that is receiving the data transaction.

The following example shows how to create a DDE object and obtain a handle of the object. During the XTYP_ADVREQ transaction, the callback function converts the current time to an ASCII string, copies the string to a local buffer, and then creates a DDE object that contains the string. The callback function returns the handle of the DDE object (HDDEDATA) to the DDEML, which passes the handle to the client application.

typedef struct tagTIME

{

INT hour; /* 0 - 11 hours for analog clock */

INT hour12; /* 12-hour format */

INT hour24; /* 24-hour format */

INT minute;

INT second;

INT ampm; /* 0 - AM , 1 - PM */

} TIME;

HDDEDATA EXPENTRY DdeCallback(uType, uFmt, hconv, hsz1, hsz2,

hdata, dwData1, dwData2)

UINT uType;

UINT uFmt;

HCONV hconv;

HSZ hsz1;

HSZ hsz2;

HDDEDATA hdata;

DWORD dwData1;

DWORD dwData2;

{

CHAR szBuf[32];

switch (uType) {

case XTYP_ADVREQ:

if ((hsz1 == hszTime && hsz2 == hszNow) &&

(uFmt == CF_TEXT)) {

/* Copy the formatted string to a buffer. */

itoa(tmTime.hour, szBuf, 10);

lstrcat(szBuf, ":");

if (tmTime.minute < 10)

lstrcat(szBuf, "0");

itoa(tmTime.minute, &szBuf[lstrlen(szBuf)], 10);

lstrcat(szBuf, ":");

if (tmTime.second < 10)

strcat(szBuf, "0");

itoa(tmTime.second, &szBuf[lstrlen(szBuf)], 10);

szBuf[lstrlen(szBuf)] = '\0';

/* Create a global object and return its data handle. */

return (DdeCreateDataHandle(

idInst,

(LPBYTE) szBuf, /* instance identifier */

lstrlen(szBuf) + 1, /* source buffer length */

0, /* offset from beginning */

hszNow, /* item name string */

CF_TEXT, /* clipboard format */

0)); /* no creation flags */

} else

return (HDDEDATA) NULL;

.

. /* Process other transactions. */

.

}

}

The receiving application obtains a pointer to the DDE object by passing the data handle to the DdeAccessData function. The pointer returned by DdeAccessData provides read-only access. The application should use the pointer to review the data and then call the DdeUnaccessData function to invalidate the pointer. The application can copy the data to a local buffer by using the DdeGetData function.

The following example obtains a pointer to the DDE object identified by the hData parameter, copies the contents to a local buffer, and then invalidates the pointer.

HDDEDATA hdata;

LPBYTE lpszAdviseData;

DWORD cbDataLen;

DWORD i;

char szData[32];

.

.

.

case XTYP_ADVDATA:

lpszAdviseData = DdeAccessData(hdata, &cbDataLen);

for (i = 0; i < cbDataLen; i++)

szData[i] = *lpszAdviseData++;

DdeUnaccessData(hdata);

return (HDDEDATA) TRUE;

.

.

.

Usually, when an application that created a data handle passes that handle to the DDEML, the handle becomes invalid in the creating application. This situation is not a problem if the application must share data with only a single application. If an application must share the same data with multiple applications, however, the creating application should specify the HDATA_APPOWNED flag in DdeCreateDataHandle. Doing so gives ownership of the DDE object to the creating application and prevents the DDEML from invalidating the data handle. The application can then pass the data handle any number of times after calling DdeCreateDataHandle only once.

If an application specifies the HDATA_APPOWNED flag in the afCmd parameter of DdeCreateDataHandle, it must call the DdeFreeDataHandle function to free the memory handle, regardless of whether it passed the handle to the DDEML. Before it terminates, an application must call DdeFreeDataHandle to free any data handle that it created but did not pass to the DDEML.

An application that has not yet passed the handle of a DDE object to the DDEML can add data to the object or overwrite data in the object by using the DdeAddData function. Typically, an application uses DdeAddData to fill an uninitialized DDE object. After an application passes a data handle to the DDEML, the DDE object identified by the handle cannot be changed; it can only be freed.

+$#A Transaction Management

After establishing a conversation with a server, a client can send transactions to obtain data and services from the server. The remaining topics in this section describe the types of transactions that clients can use to interact with a server.

+$#A Request Transaction

A client application can use the XTYP_REQUEST transaction to request a data item from a server application. The client calls the DdeClientTransaction function, specifying XTYP_REQUEST as the transaction type and specifying the data item the application needs.

The DDEML passes the XTYP_REQUEST transaction to the server, specifying the topic name, item name, and data format requested by the client. If the server supports the requested topic, item, and format, the server should return a data handle that identifies the current value of the item. The DDEML passes this handle to the client as the return value from DdeClientTransaction. The server should return NULL if it does not support the topic, item, or format requested.

DdeClientTransaction uses the lpdwResult parameter to return a transaction-status flag to the client. If the server does not process the XTYP_REQUEST transaction, DdeClientTransaction returns NULL, and lpdwResult points to the DDE_FNOTPROCESSED or DDE_FBUSY flag. If the DDE_FNOTPROCESSED flag is returned, the client cannot determine why the server did not process the transaction.

If a server does not support the XTYP_REQUEST transaction, it should specify the CBF_FAIL_REQUESTS filter flag in the DdeInitialize function. This flag prevents the DDEML from sending the transaction to the server.

+$#A Poke Transaction

A client can send unsolicited data to a server by using DdeClientTransaction to send an XTYP_POKE transaction to a server's callback function.

The client application first creates a buffer that contains the data to send to the server and then passes a pointer to the buffer as a parameter to DdeClientTransaction. Alternatively, the client can use the DdeCreateDataHandle function to obtain a data handle that identifies the data and then pass the handle to DdeClientTransaction. In either case, the client also specifies the topic name, item name, and data format when it calls DdeClientTransaction.

The DDEML passes the XTYP_POKE transaction to the server, specifying the topic name, item name, and data format that the client requested. To accept the data item and format, the server should return DDE_FACK. To reject the data, the server should return DDE_FNOTPROCESSED. If the server is too busy to accept the data, the server should return DDE_FBUSY.

When DdeClientTransaction returns, the client can use the lpdwResult parameter to access the transaction-status flag. If the flag is DDE_FBUSY, the client should send the transaction again later.

If a server does not support the XTYP_POKE transaction, it should specify the CBF_FAIL_POKES filter flag in DdeInitialize. This flag prevents the DDEML from sending this transaction to the server.

+$#A Advise Transaction

A client application can use the DDEML to establish one or more links to items in a server application. When such a link has been established, the server sends periodic updates about the linked item to the client (typically, whenever the value of the item associated with the server application changes). Linking establishes an advise loop between the two applications that remains in place until the client ends it.

There are two kinds of advise loops: "hot" and "warm." In a hot advise loop, the server immediately sends a data handle that identifies the changed value. In a warm advise loop, the server notifies the client that the value of the item has changed but does not send the data handle until the client requests it.

A client can request a hot advise loop with a server by specifying the XTYP_ADVSTART transaction type in a call to DdeClientTransaction. To request a warm advise loop, the client must combine the XTYPF_NODATA flag with the XTYP_ADVSTART transaction type. In either event, the DDEML passes the XTYP_ADVSTART transaction to the server's DDE callback function. The server's DDE callback function should examine the parameters that accompany the XTYP_ADVSTART transaction (including the requested format, topic name, and item name) and then return TRUE to allow the advise loop or FALSE to deny it.

After an advise loop has been established, the server application should call the DdePostAdvise function whenever the value of the item associated with the requested item name changes. This call results in an XTYP_ADVREQ transaction being sent to the server's own DDE callback function. The server's DDE callback function should return a data handle that identifies the new value of the data item. The DDEML then notifies the client that the specified item has changed by sending the XTYP_ADVDATA transaction to the client's DDE callback function.

If the client requested a hot advise loop, the DDEML passes the data handle for the changed item to the client during the XTYP_ADVDATA transaction. Otherwise, the client can send an XTYP_REQUEST transaction to obtain the data handle.

It is possible for a server to send updates faster than a client can process the new data. The speed of updates can be a problem for a client that must perform lengthy processing operations on the data. In this case, the client should specify the XTYPF_ACKREQ flag when it requests an advise loop. This flag causes the server to wait for the client to acknowledge that it has received and processed a data item before the server sends the next data item. Advise loops that are established with the XTYPF_ACKREQ flag are more robust with fast servers but may occasionally miss updates. Advise loops established without the XTYPF_ACKREQ flag are guaranteed not to miss updates as long as the client keeps up with the server.

A client can end an advise loop by specifying the XTYP_ADVSTOP transaction type in a call to DdeClientTransaction.

If a server does not support advise loops, it should specify the CBF_FAIL_ADVISES filter flag in the DdeInitialize function. This flag prevents the DDEML from sending the XTYP_ADVSTART and XTYP_ADVSTOP transactions to the server.

+$#A Execute Transaction

A client can use the XTYP_EXECUTE transaction to cause a server to execute a command or a series of commands.

To execute a server command, the client first creates a buffer that contains a command string for the server to execute and then passes either a pointer to the buffer or a data handle identifying the buffer when it calls DdeClientTransaction. Other required parameters include the conversation handle, the item name string handle, the format specification, and the XTYP_EXECUTE transaction type. An application that creates a data handle for passing execute data must specify NULL for the hszItem parameter of the DdeCreateDataHandle function and zero for the uFmt parameter.

The DDEML passes the XTYP_EXECUTE transaction to the server's DDE callback function and specifies the format name, conversation handle, topic name, and data handle identifying the command string. If the server supports the command, it should use the DdeAccessData function to obtain a pointer to the command string, execute the command, and then return DDE_FACK. If the server does not support the command or cannot complete the transaction, it should return DDE_FNOTPROCESSED. The server should return DDE_FBUSY if it is too busy to complete the transaction.

In general, a server's callback function should process the XTYP_EXECUTE transaction before returning with the following exceptions:

1. When the command passed with the XTYP_EXECUTE transaction requests the server to terminate, the server should not terminate until its callback function returns from processing XTYP_EXECUTE.

2. If the server must perform an operation, such as processing a dialog box or a DDE transaction that might cause DDEML recursion problems, the server should return the CBR_BLOCK return code to block the execute transaction, perform the operation, and then resume processing the execute transaction.

When DdeClientTransaction returns, the client can use the lpdwResult parameter to access the transaction status flag. If the flag is DDE_FBUSY, the client should send the transaction again later.

If a server does not support the XTYP_EXECUTE transaction, it should specify the CBF_FAIL_EXECUTES filter flag in the DdeInitialize function. Doing so prevents the DDEML from sending the transaction to the server.

+$#A Synchronous and Asynchronous Transactions

A client can send either synchronous or asynchronous transactions. In a synchronous transaction, the client specifies a time-out value that indicates the maximum amount of time it will wait for the server to process the transaction. DdeClientTransaction does not return until the server processes the transaction, the transaction fails, or the time-out value expires. The client specifies the time-out value when it calls DdeClientTransaction.

During a synchronous transaction, the client enters a modal loop while waiting for the transaction to be processed. The client can still process user input but cannot send another synchronous transaction until DdeClientTransaction returns.

A client sends an asynchronous transaction by specifying the TIMEOUT_ASYNC flag in DdeClientTransaction. The function returns after the transaction has begun, passing a transaction identifier to the client. When the server finishes processing the asynchronous transaction, the DDEML sends an XTYP_XACT_COMPLETE transaction to the client. One of the parameters that the DDEML passes to the client during the XTYP_XACT_COMPLETE transaction is the transaction identifier. By comparing this transaction identifier with the identifier returned by DdeClientTransaction, the client identifies which asynchronous transaction the server has finished processing.

A client can use the DdeSetUserHandle function as an aid in processing an asynchronous transaction. This function makes it possible for a client to associate an application-defined doubleword value with a conversation handle and a transaction identifier. The client can use the DdeQueryConvInfo function during the XTYP_XACT_COMPLETE transaction to obtain the application-defined doubleword value. Because of this function, an application need not maintain a list of active transaction identifiers.

When a client successfully completes a request for data using a synchronous transaction, the DDEML has no way to tell when the client has finished using the data received. The client application must pass the data handle received to the DdeFreeDataHandle function, notifying the DDEML that the handle will no longer be used. Data handles returned by synchronous transactions are effectively owned by the client.

If a server does not process an asynchronous transaction in a timely manner, the client can abandon the transaction by calling the DdeAbandonTransaction function. The DDEML releases all resources associated with the transaction and discards the results of the transaction when the server finishes processing it. A time-out during a synchronous transaction effectively cancels the transaction.

The asynchronous transaction method is provided for applications that must send a high volume of DDE transactions while simultaneously performing a substantial amount of processing, such as performing calculations. The asynchronous method is also useful in applications that must stop processing DDE transactions temporarily so they can complete other tasks without interruption. In most other situations, an application should use the synchronous method.

Synchronous transactions are simpler to maintain and are faster than asynchronous transactions. However, only one synchronous transaction can be performed at a time, whereas many asynchronous transactions can be performed simultaneously. With synchronous transactions, a slow server can cause a client to remain idle while it is waiting for a response. Also, synchronous transactions cause the client to enter a modal loop that could bypass message filtering in the application's own message loop.

If the client has installed a hook procedure to filter messages (that is, specified the WH_MSGFILTER hook type in a call to the SetWindowsHookEx function), a synchronous transaction will not cause the system to bypass the hook procedure. When an input event occurs while the client is waiting for a synchronous transaction to end, the hook procedure receives an MSGF_DDEMGR hook code. The main danger of using a synchronous transaction modal loop is that a modal loop created by a dialog box can interfere with its operation. Asynchronous transactions should always be used when the DDEML is being used by a dynamic-link library (DLL).

+$#A Transaction Control

An application can suspend transactions to its DDE callback function ⎯ either those transactions associated with a specific conversation handle or all transactions regardless of the conversation handle. This capability is useful when an application receives a transaction that requires lengthy processing. In such a case, the application can return the CBR_BLOCK return code to suspend future transactions associated with the transaction's conversation handle, so that the application is free to process other conversations.

When processing has been completed, the application calls the DdeEnableCallback function to resume transactions associated with the suspended conversation. Calling DdeEnableCallback causes the DDEML to resend the transaction that resulted in the application suspending the conversation. Therefore, the application should store the result of the transaction in such a way that it can obtain and return the result without reprocessing the transaction.

An application can suspend all transactions associated with a specific conversation handle by specifying the handle and the EC_DISABLE flag in a call to DdeEnableCallback. By specifying a NULL handle, an application can suspend all transactions for all conversations.

When a conversation has been suspended, the DDEML saves transactions for the conversation in a transaction queue. When the application reenables the conversation, the DDEML removes the saved transactions from the queue and passes each transaction to the appropriate callback function. The capacity of the transaction queue is large, but an application should reenable a suspended conversation as soon as possible to avoid losing transactions.

An application can resume usual transaction processing by specifying the EC_ENABLEALL flag in DdeEnableCallback. For a more controlled resumption of transaction processing, the application can specify the EC_ENABLEONE flag. This flag removes one transaction from the transaction queue and passes it to the appropriate callback function; after that transaction has been processed, any conversations are again disabled.

If the EC_ENABLEONE flag and a conversation handle are specified in the call to DdeEnableCallback, only that conversation is blocked after the transaction has been processed. If a NULL conversation handle is specified, all conversations are blocked after a transaction has been processed in any conversation.

+$#A Transaction Classes

The DDEML has four classes of transactions. Each class is identified by a constant that begins with the XCLASS_ prefix. The classes are defined in the DDEML header file. The class value is combined with the transaction-type value and is passed to the DDE callback function of the receiving application.

A transaction's class determines the return value that a callback function is expected to return if it processes the transaction. The following return values and transaction types are associated with each of the four transaction classes.

Class | Return value | Transaction
XCLASS_BOOL | TRUE or FALSE | XTYP_ADVSTART XTYP_CONNECT
XCLASS_DATA | A data handle, the CBR_BLOCK return code, or NULL | XTYP_ADVREQ XTYP_REQUEST XTYP_WILDCONNECT
XCLASS_FLAGS | A transaction flag: DDE_FACK, DDE_FBUSY, or DDE_FNOTPROCESSED | XTYP_ADVDATA XTYP_EXECUTE XTYP_POKE
XCLASS_NOTIFICATION | None | XTYP_ADVSTOP XTYP_CONNECT_ CONFIRM XTYP_DISCONNECT XTYP_ERROR XTYP_REGISTER XTYP_UNREGISTER XTYP_XACT_ COMPLETE

+$#A Transaction Types

Each DDE transaction type has a receiver and an associated activity that causes the DDEML to generate each type.

Transaction type | Receiver | Cause
XTYP_ADVDATA | Client | A server responded to an XTYP_ADVREQ transaction by returning a data handle.
XTYP_ADVREQ | Server | A server called the DdePostAdvise function, indicating that the value of a data item in an advise loop had changed.
XTYP_ADVSTART | Server | A client specified the XTYP_ADVSTART transaction type in a call to the DdeClientTransaction function.
XTYP_ADVSTOP | Server | A client specified the XTYP_ADVSTOP transaction type in a call to DdeClientTransaction.
XTYP_CONNECT | Server | A client called the DdeConnect function and specified a service name and topic name supported by the server.
XTYP_CONNECT_CONFIRM | Server | The server returned TRUE in response to an XTYP_CONNECT or XTYP_WILDCONNECT transaction.
XTYP_DISCONNECT | Client/Server | A partner in a conversation called the DdeDisconnect function, causing both partners to receive this transaction.
XTYP_ERROR | Client/Server | A critical error has occurred. The DDEML may not have sufficient resources to continue.
XTYP_EXECUTE | Server | A client specified the XTYP_EXECUTE transaction type in a call to DdeClientTransaction.
XTYP_MONITOR | DDE monitoring application | A DDE event occurred in the system. For more information about DDE monitoring applications, see Monitoring Applications.
XTYP_POKE | Server | A client specified the XTYP_POKE transaction type in a call to DdeClientTransaction.
XTYP_REGISTER | Client/Server | A server application used the DdeNameService function to register a service name.
XTYP_REQUEST | Server | A client specified the XTYP_REQUEST transaction type in a call to DdeClientTransaction.
XTYP_UNREGISTER | Client/Server | A server application used DdeNameService to unregister a service name.
XTYP_WILDCONNECT | Server | A client called the DdeConnect or DdeConnectList function, specifying NULL for the service name, the topic name, or both.
XTYP_XACT_COMPLETE | Client | An asynchronous transaction, sent when the client specified the TIMEOUT_ASYNC flag in a call to DdeClientTransaction, has concluded.

+$#A Error Detection for DDEML Functions

Whenever a DDEML function fails, an application can call the DdeGetLastError function to determine the cause of the failure. DdeGetLastError returns an error value that specifies the cause of the most recent error.

+$#A Monitoring Applications

The application programming interface (API) elements of the DDEML can be used to create an application that monitors DDE activity in the system. Like any DDEML application, a DDE monitoring application contains a DDE callback function. The DDEML notifies a monitoring application's DDE callback function whenever a DDE event occurs, passing information about the event to the callback function. The application typically displays the information in a window or writes it to a file.

To receive notifications from the DDEML, an application must have registered as a DDE monitor by specifying the APPCLASS_MONITOR flag in a call to the DdeInitialize function. In this same call, the application can specify one or more monitor flags to indicate the types of events for which the DDEML is to notify the application's callback function. The following monitor flags can be specified by an application:

Flag | Description
MF_CALLBACKS | Notifies the callback function whenever a transaction is sent to any DDE callback function in the system.
MF_CONV | Notifies the callback function whenever a conversation is established or terminated.
MF_ERRORS | Notifies the callback function whenever a DDEML error occurs.
MF_HSZ_INFO | Notifies the callback function whenever a DDEML application creates, frees, or increments the usage count of a string handle or whenever a string handle is freed as a result of a call to the DdeUninitialize function.
MF_LINKS | Notifies the callback function whenever an advise loop is started or ended.
MF_POSTMSGS | Notifies the callback function whenever the system or an application posts a DDE message.
MF_SENDMSGS | Notifies the callback function whenever the system or an application sends a DDE message.

The following example shows how to register a DDE monitoring application so that its DDE callback function receives notifications of all DDE events.

DWORD idInst;

PFNCALLBACK lpDdeProc;

hInst = hInstance;

if (DdeInitialize(

(LPDWORD) &idInst, /* instance identifier */

DDECallback, /* points to callback function */

APPCLASS_MONITOR | /* this is a monitoring application */

MF_CALLBACKS | /* monitor callback functions */

MF_CONV | /* monitor conversation data */

MF_ERRORS | /* monitor DDEML errors */

MF_HSZ_INFO | /* monitor data handle activity */

MF_LINKS | /* monitor advise loops */

MF_POSTMSGS | /* monitor posted DDE messages */

MF_SENDMSGS, /* monitor sent DDE messages */

0)) /* reserved */

return FALSE;

The DDEML informs a monitoring application of a DDE event by sending an XTYP_MONITOR transaction to the application's DDE callback function. During this transaction, the DDEML passes a monitor flag that specifies the type of DDE event that has occurred and a handle of a DDE object that contains detailed information about the event. The DDEML provides a set of structures that the application can use to extract the information from the DDE object. There is a corresponding structure for each type of DDE event.

Structure | Description
MONCBSTRUCT | Contains information about a transaction.
MONCONVSTRUCT | Contains information about a conversation.
MONERRSTRUCT | Contains information about the latest DDE error.
MONLINKSTRUCT | Contains information about an advise loop.
MONHSZSTRUCT | Contains information about a string handle.
MONMSGSTRUCT | Contains information about a DDE message that was sent or posted.

The following example shows the DDE callback function of a DDE monitoring application that formats information about each string handle event and then displays the information in a window. The function uses the MONHSZSTRUCT structure to extract the information from the DDE object.

HDDEDATA CALLBACK DDECallback(uType, uFmt, hconv, hsz1, hsz2,

hdata, dwData1, dwData2)

UINT uType;

UINT uFmt;

HCONV hconv;

HSZ hsz1;

HSZ hsz2;

HDDEDATA hdata;

DWORD dwData1;

DWORD dwData2;

{

LPVOID lpData;

CHAR *szAction;

CHAR szBuf[256];

DWORD cb;

switch (uType) {

case XTYP_MONITOR:

/* Obtain a pointer to the global memory object. */

if (lpData = DdeAccessData(hdata, &cb)) {

/* Examine the monitor flag. */

switch (dwData2) {

case MF_HSZ_INFO:

#define PHSZS ((MONHSZSTRUCT FAR *)lpData)

/*

* The global memory object contains

* string handle data. Use the MONHSZSTRUCT

* structure to access the data.

*/

switch (PHSZS->fsAction) {

/*

* Examine the action flags to

* determine the action performed on

* the handle.

*/

case MH_CREATE:

szAction = "Created";

break;

case MH_KEEP:

szAction = "Incremented";

break;

case MH_DELETE:

szAction = "Deleted";

break;

case MH_CLEANUP:

szAction = "Cleaned up";

break;

default:

DdeUnaccessData(hdata);

return (HDDEDATA) 0;

}

/* Write formatted output to a buffer. */

wsprintf(szBuf,

"Handle %s, Task: %x, Hsz: %lx(%s)",

(LPSTR) szAction, PHSZS->hTask,

PHSZS->hsz, (LPSTR) PHSZS->str);

.

. /* Display text or write to a file. */

.

break;

#undef PHSZS

.

. /* Process other MF_* flags. */

.

default:

break;

}

}

/* Free the global memory object. */

DdeUnaccessData(hdata);

break;

default:

break;

}

return (HDDEDATA) 0;

}

+$#A Dynamic Data Exchange Management Functions

Following are the functions used to manage dynamic data exchange.

DdeAbandonTransaction
DdeAccessData
DdeAddData
DdeCallback
DdeClientTransaction
DdeCmpStringHandles
DdeConnect
DdeConnectList
DdeCreateDataHandle
DdeCreateStringHandle
DdeDisconnect
DdeDisconnectList
DdeEnableCallback
DdeFreeDataHandle
DdeFreeStringHandle
DdeGetData
DdeGetLastError
DdeInitialize
DdeKeepStringHandle
DdeNameService
DdePostAdvise
DdeQueryConvInfo
DdeQueryNextServer
DdeQueryString
DdeReconnect
DdeSetUserHandle
DdeUnaccessData
DdeUninitialize

Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle


Dynamic Data Exchange.rtf

+$#AK Dynamic Data Exchange

This overview provides guidelines for implementing dynamic data exchange for applications that cannot use the Dynamic Data Exchange Management Library (DDEML). For more information about the DDEML, see Dynamic Data Exchange Management Library.

+$#A About Dynamic Data Exchange

The Microsoft® Win32® application programming interface (API) provides several methods for transferring data between applications. One method is to use the Win32 dynamic data exchange (DDE) protocol. The DDE protocol is a set of messages and guidelines. It sends messages between applications that share data and uses shared memory to exchange data between applications. Applications can use the DDE protocol for one-time data transfers and for continuous exchanges in which applications send updates to one another as new data becomes available.

The Win32 API also includes the Dynamic Data Exchange Management Library (DDEML). The DDEML is a dynamic-link library (DLL) that applications running with Windows can use to share data. The DDEML provides an application programming interface (API) that simplifies the task of adding DDE capability to a Win32-based application. Instead of sending, posting, and processing DDE messages directly, an application uses the DDEML functions to manage DDE conversations. (A DDE conversation is the interaction between client and server applications.)

The DDEML also provides a facility for managing the strings and data that DDE applications share. Instead of using atoms and pointers to shared memory objects, DDE applications create and exchange string handles, which identify strings, and data handles, which identify memory objects. The DDEML also makes it possible for a server application to register the service names it supports. The names are broadcast to other applications in the system, which can use the names to connect to the server. Moreover, the DDEML ensures compatibility among DDE applications by forcing them to implement the DDE protocol in a consistent manner.

Existing applications that use the message-based DDE protocol are fully compatible with those that use the DDEML. That is, an application that uses message-based DDE can establish conversations and perform transactions with applications that use the DDEML. Because of the many advantages of the DDEML, new applications should use it rather than the DDE messages. To use the API elements of the DDEML, you must include the DDEML header file in your source files, link with the DDEML library, and ensure that the DDEML dynamic-link library is in the system's search path.

+$#A Dynamic Data Exchange Protocol

Because the Win32 API has a message-based architecture, passing messages is the most appropriate method for automatically transferring information between applications. However, Win32 messages contain only two parameters (wParam and lParam) for passing data. As a result, these parameters must refer indirectly to other pieces of data when more than a few words of information pass between applications. The DDE protocol defines exactly how applications should use the wParam and lParam parameters to pass larger pieces of data by means of global atoms and shared memory handles. The DDE protocol has specific rules for allocating and deleting global atoms and shared memory objects.

A global atom is a reference to a character string. In the DDE protocol, atoms identify the applications exchanging data, the nature of the data being exchanged, and the data items themselves. For more information about atoms, see Atoms. A shared memory handle is a handle to a memory object allocated by GlobalAlloc, using the GMEM_DDESHARE flag. In the DDE protocol, shared memory objects store data items passed between applications, protocol options, and remote command execution strings.

+$#A Uses for Windows Dynamic Data Exchange

DDE is most appropriate for data exchanges that do not require ongoing user interaction. Usually, an application provides a method for the user to establish the link between the applications exchanging data. Once that link is established, however, the applications exchange data without further user involvement.

DDE can be used to implement a broad range of application features ⎯ for example:

• Linking to real-time data, such as to stock market updates, scientific instruments, or process control.

• Creating compound documents, such as a word processing document that includes a chart produced by a graphics application. Using DDE, the chart will change when the source data is changed, while the rest of the document remains the same.

• Performing data queries between applications, such as a spreadsheet querying a database for accounts past due.

+$#A Dynamic Data Exchange from the User's Point of View

The following example illustrates illustrates how two DDE applications can cooperate, as seen from the user's point of view.

A spreadsheet user wants to use Microsoft® Excel to track the price of a particular stock on the New York Stock Exchange. The user has a Win32-based application called Quote that in turn has access to NYSE data. The DDE conversation between Microsoft Excel and Quote takes place as follows:

• The user initiates the conversation by supplying the name of the application (Quote) that will supply the data and the particular topic of interest (NYSE). The resulting DDE conversation is used to request quotes on specific stocks.

• Microsoft Excel broadcasts the application and topic names to all DDE applications currently running in the system. Quote responds, establishing a conversation with Microsoft Excel about the NYSE topic.

• The user can then create a spreadsheet formula in a cell that requests that the spreadsheet be automatically updated whenever a particular stock quotation changes. For example, the user could request an automatic update whenever a change occurs in the selling price of ZAXX stock by specifying the following Microsoft Excel formula:

='Quote'|'NYSE'!ZAXX

• The user can terminate the automatic updating of the ZAXX stock quotation at any time. Other data links that were established separately (such as for quotations for other stocks) still will remain active under the same NYSE conversation.

• The user can also terminate the entire conversation between Microsoft Excel and Quote on the NYSE topic, so that no specific data links on that topic can be established without initiating a new conversation.

+$#A Dynamic Data Exchange Concepts

The following sections explain the important concepts and terminology that are key to understanding dynamic data exchange.

+$#A Client, Server, and Conversation

Two applications participating in dynamic data exchange are said to be engaged in a DDE conversation. The application that initiates the conversation is the DDE client application; the application that responds to the client is the DDE server application. An application can engage in several conversations at the same time, acting as the client in some and as the server in others.

A DDE conversation takes place between two windows, one for each of the participating applications. A window may be the main window of the application; a window associated with a specific document, as in a multiple document interface (MDI) application; or a hidden (invisible) window whose only purpose is to process DDE messages.

Since a DDE conversation is identified by the pair of handles to the windows engaged in the conversation, no window should be engaged in more than one conversation with another window. Either the client application or the server application must provide a different window for each of its conversations with a particular server or client application.

An application can ensure a pair of client and server windows is never involved in more than one conversation by creating a hidden window for each conversation. The sole purpose of this window is to process DDE messages.

+$#A Application, Topic, and Item Names

The DDE protocol identifies the units of data passed between the client and server with a three-level hierarchy of application, topic, and item names.

Each DDE conversation is uniquely defined by the application name and topic. At the beginning of a DDE conversation, the client and server determine the application name and topic. The application name is usually the name of the server application. For example, when Microsoft Excel acts as the server in a conversation, the application name is Excel.

The DDE topic is a general classification of data within which multiple data items may be "discussed" (exchanged) during the conversation. For applications that operate on file-based documents, the topic is usually a filename. For other applications, the topic is an application-specific name.

Because the client and server window handles together identify a DDE conversation, the application name and topic that define a conversation cannot be changed during the course of the conversation.

A DDE data item is information related to the conversation topic exchanged between the applications. Values for the data item can be passed from the server to the client, or from the client to the server. Data can be passed with any of the standard clipboard formats or with a registered clipboard format. A special, registered format named Link identifies an item in a DDE conversation. For more information about clipboard formats, see Clipboard.

+$#A The System Topic

Applications should support the system topic at all times. This topic provides a context for information that may be of general interest to another application.

Data-item values must be rendered in the CF_TEXT clipboard format. Individual elements of item values for a system topic must be delimited by tab characters. The following table suggests some items for the system topic.

Item | Description
Formats | Tab-delimited list of clipboard formats the application can render. Typically, CF_ formats are listed with the "CF_" portion of the names removed (for example, CF_TEXT is listed as "TEXT").
Help | Text that briefly explains how to use the DDE server.
ReturnMessage | Supporting detail for the most recently used WM_DDE_ACK message. This item is useful when more than eight bits of application-specific return data are required.
Status | Indication of the current status of the application. When a server receives a WM_DDE_REQUEST message for this system-topic item, it should respond by posting a WM_DDE_DATA message with a string containing either Busy or Ready, as appropriate.
SysItems | List of system-topic items the application supports.
TopicItemList | Similar to the SysItems item, except that TopicItemList should be supported for each topic other than the system topic. This allows browsing of the items supported under any topic. If the items cannot be enumerated, this item should contain only "TopicItemList".
Topics | List of topics the application supports at the current time; this list can vary from moment to moment.

+$#A Permanent Data Links

Once a DDE conversation has begun, the client can establish one or more permanent data links with the server. A data link is a communications mechanism by which the server notifies the client whenever the value of a given data item changes. The data link is permanent in the sense that this notification process continues until the data link or the DDE conversation itself is terminated.

There are two kinds of permanent DDE data links: warm and hot. In a warm data link, the server notifies the client that the value of the data item has changed, but the server does not send the data value to the client until the client requests it. In a hot data link, the server immediately sends the changed data value to the client.

Applications that support warm or hot data links typically provide a Copy or Paste Link command in their Edit menu to enable the user to establish links between applications. For more information, see Initiating a Data Link.

+$#A Atoms and Shared Memory Objects

Certain arguments of DDE messages are global atoms or shared memory objects. Applications using these arguments must follow explicit rules about when to allocate and delete them. In all cases, the message sender must delete any atom or shared memory object that the intended receiver will not receive because of an error condition, such as failure of the PostMessage function.

DDE uses shared memory objects for three purposes:

• To carry a data-item value to be exchanged. This is an item referenced by the hData parameter in the WM_DDE_DATA and WM_DDE_POKE messages.

• To carry options in a message. This is an item referenced by the hOptions parameter in a WM_DDE_ADVISE message.

• To carry a command execution string. This is an item referenced by the hCommands parameter in the WM_DDE_EXECUTE message and its corresponding WM_DDE_ACK message.

An application that receives a DDE shared memory object must treat it as read only. The application must not use the object as a mutual read-write area for the free exchange of data.

As it does with a DDE atom, an application should free a shared memory object to manage memory effectively. The application should also lock and unlock memory objects.

+$#A Dynamic Data Exchange Messages Overview

Because DDE is a message-based protocol, it employs no special Win32 functions or libraries. All DDE transactions are conducted by passing certain defined DDE messages between the client and server windows.

There are nine DDE messages; the symbolic constants for these messages are defined in the DDE header file. Certain structures for the various DDE messages are also defined in this header file.

The following table summarizes the nine DDE messages.

Message | Description
WM_DDE_ACK | Acknowledges receiving or not receiving a message.
WM_DDE_ADVISE | Requests the server application to supply an update or notification for a data item whenever it changes. This establishes a permanent data link.
WM_DDE_DATA | Sends a data-item value to the client application.
WM_DDE_EXECUTE | Sends a string to the server application, which is expected to process the string as a series of commands.
WM_DDE_INITIATE | Initiates a conversation between the client and server applications.
WM_DDE_POKE | Sends a data-item value to the server application.
WM_DDE_REQUEST | Requests the server application to provide the value of a data item.
WM_DDE_TERMINATE | Terminates a conversation.
WM_DDE_UNADVISE | Terminates a permanent data link.

An application calls SendMessage to issue the WM_DDE_INITIATE message or a WM_DDE_ACK message sent in response to WM_DDE_INITIATE. All other messages are sent by PostMessage. The first parameter of these calls is the handle of the receiving window; the second parameter contains the message to be sent; the third parameter identifies the sending window; and the fourth parameter contains the message-specific arguments.

+$#A Dynamic Data Exchange Message Flow

A typical DDE conversation consists of the following events:

1. The client application initiates the conversation, and the server application responds.

2. The applications exchange data by any or all of the following methods:

• The server application sends data to the client at the client's request.

• The client application sends unsolicited data to the server application.

• The client application requests the server application to notify the client whenever a data item changes (warm data link).

• The client application requests the server application to send data whenever the data changes (hot data link).

• The server application carries out a command at the client's request.

3. Either the client or server application terminates the conversation.

An application window that processes requests from a client or server must process them strictly in the order they are received.

A client can establish conversations with more than one server; a server can have conversations with more than one client. When handling messages from more than one source, a client or server must process the messages of a given conversation synchronously, but need not process all messages synchronously. In other words, it can shift from one conversation to another as needed.

If an application is unable to process an incoming request because it is waiting for a DDE response, it must prevent deadlock by posting a WM_DDE_ACK message with the fBusy member of the DDEACK structure set to 1. An application can also send a busy WM_DDE_ACK message if, for any reason, it cannot process an incoming request within a reasonable amount of time.

An application should be able to handle the failure of a client or server to respond to a message within a certain time. Since the time-out interval may vary depending on the nature of the application and the configuration of the user's system (including whether it is connected to a network), the application should provide a way for the user to specify the interval.

+$#A Parameter-Packing Functions

The lParam parameter of many DDE messages contains two pieces of data. For example, the lParam of the WM_DDE_DATA message contains a data handle and an atom. In previous versions of Windows, an application could use the MAKELONG macro to prepare an lParam parameter, and the LOWORD and HIWORD macros to remove the low-order and high-order values from lParam. Because a Win32 data handle is a 32-bit value, a Win32-based application must use the PackDDElParam function to pack the handle and atom into an lParam parameter, and the UnpackDDElParam function to remove the values. DDE applications must use PackDDElParam and UnpackDDElParam for all messages posted during a DDE conversation.

The Win32 API also includes the ReuseDDElParam and FreeDDElParam functions. ReuseDDElParam allows a DDE application to reuse a packed lParam parameter, helping reduce the number of memory reallocations the application must perform during a conversation. An application can use FreeDDElParam to free the memory associated with a data handle received during a DDE conversation.

+$#A Using Dynamic Data Exchange

• Initiating a DDE conversation

• Transferring a single data item

• Establishing a permanent data link

• Carrying out commands in a server application

• Terminating a DDE conversation

+$#A Initiating a Conversation

To initiate a DDE conversation, the client sends a WM_DDE_INITIATE message. Usually, the client broadcasts this message by calling SendMessage, with −1 as the first parameter. If the application already has the window handle of the server application, it can send the message directly to that window. The client prepares atoms for the application name and topic name by calling GlobalAddAtom. The client can request conversations with any potential server application and for any potential topic by supplying NULL (wildcard) atoms for the application and topic.

The following example illustrates how the client initiates a conversation, where both the application and topic are specified.

/* Global variable */

static BOOL fInInitiate = FALSE;

char *szApplication;

char *szTopic;

atomApplication = *szApplication == 0 ?

NULL : GlobalAddAtom((LPSTR) szApplication);

atomTopic = *szTopic == 0 ?

NULL : GlobalAddAtom((LPSTR) szTopic);

fInInitiate = TRUE;

SendMessage((HWND) -1, /* broadcasts message */

WM_DDE_INITIATE, /* initiates conversation */

(WPARAM) hwndClientDDE, /* handle of client DDE window */

MAKELONG(atomApplication, /* application-name atom */

atomTopic)); /* topic-name atom */

fInInitiate = FALSE;

if (atomApplication != NULL)

GlobalDeleteAtom(atomApplication);

if (atomTopic != NULL)

GlobalDeleteAtom(atomTopic);

Note that if your application uses NULL atoms, you need not use the GlobalAddAtom and GlobalDeleteAtom functions. In this example, the client application creates two global atoms containing the name of the server and the name of the topic, respectively.

The client application sends a WM_DDE_INITIATE message with these two atoms in the lParam parameter of the message. In the call to the SendMessage function, the special window handle −1 directs Windows to send this message to all other active applications. SendMessage does not return to the client application until all applications that receive the message have, in turn, returned control to Windows. This means that all WM_DDE_ACK messages sent in reply by the server applications are guaranteed to have been processed by the client by the time the SendMessage call has returned.

After SendMessage returns, the client application deletes the global atoms.

Server applications respond according to the logic illustrated in the following diagram.

{ewc msdncd, EWGraphic, group10800 0 /a "SDK_01.bmp"}

To acknowledge one or more topics, the server must create atoms for each conversation (requiring duplicate application-name atoms if there are multiple topics) and send a WM_DDE_ACK message for each conversation, as illustrated in the following example.

if ((atomApplication = GlobalAddAtom("Server")) != 0) {

if ((atomTopic = GlobalAddAtom(szTopic)) != 0) {

SendMessage(hwndClientDDE,

WM_DDE_ACK,

(WPARAM) hwndServerDDE,

MAKELONG(atomApplication, atomTopic));

GlobalDeleteAtom(atomApplication);

}

GlobalDeleteAtom(atomTopic);

}

if ((atomApplication == 0) || (atomTopic == 0)) {

.

. /* error handling */

.

}

When a server responds with a WM_DDE_ACK message, the client application should save the handle of the server window. The client receiving the handle as the wParam parameter of the WM_DDE_ACK message then sends all subsequent DDE messages to the server window this handle identifies.

If your client application uses a NULL atom for the application name or topic name, expect the application to receive acknowledgments from more than one server application. Multiple acknowledgements can also come from multiple instances of a DDE server, even if your client application does not use NULL atoms. A server should always use a unique window for each conversation. The window procedure in the client application can use the handle of the server window (provided as the lParam parameter of WM_DDE_INITIATE) to track multiple conversations. This allows a single client window to process several conversations without needing to terminate and reconnect with a new client window for each conversation.

+$#A Transferring a Single Item

Once a DDE conversation has been established, the client can either retrieve the value of a data item from the server by issuing the WM_DDE_REQUEST message, or submit a data-item value to the server by issuing WM_DDE_POKE.

+$#A Retrieving an Item from the Server

To retrieve an item from the server, the client sends the server a WM_DDE_REQUEST message specifying the item and format to retrieve, as shown in the following example.

if ((atomItem = GlobalAddAtom(szItemName)) != 0) {

if (!PostMessage(hwndServerDDE,

WM_DDE_REQUEST,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_REQUEST, CF_TEXT, atomItem)))

GlobalDeleteAtom(atomItem);

}

if (atomItem == 0) {

.

. /* error handling */

.

}

In this example, the client specifies the clipboard format CF_TEXT as the preferred format for the requested data item.

The receiver (server) of the WM_DDE_REQUEST message typically must delete the item atom, but if the PostMessage call fails, the client must delete the atom.

If the server has access to the requested item and can render it in the requested format, the server copies the item value as a shared memory object and sends the client a WM_DDE_DATA message, as illustrated in the following example.

/*

* Allocate the size of the DDE data header, plus the data: a

* string,<CR><LF><NULL>. The byte for the string's terminating

* null character is counted by DDEDATA.Value[1].

*/

if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,

(LONG) sizeof(DDEDATA) + lstrlen(szItemValue) + 2)))

return;

if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) {

GlobalFree(hData);

return;

}

.

.

.

lpData->cfFormat = CF_TEXT;

lstrcpy((LPSTR) lpData->Value, (LPSTR) szItemValue);

/* Each line of CF_TEXT data is terminated by CR/LF. */

lstrcat((LPSTR) lpData->Value, (LPSTR) "\r\n");

GlobalUnlock(hData);

if ((atomItem = GlobalAddAtom((LPSTR) szItemName)) != 0) {

lParam = PackDDElParam(WM_DDE_ACK, (UINT) hData, atomItem);

if (!PostMessage(hwndClientDDE,

WM_DDE_DATA,

(WPARAM) hwndServerDDE,

lParam)) {

GlobalFree(hData);

GlobalDeleteAtom(atomItem);

FreeDDElParam(WM_DDE_ACK, lParam);

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

In this example, the server application allocates a memory object to contain the data item. The memory is allocated with the GMEM_DDESHARE option, so that the server and client applications can share the memory. After allocating the memory object, the server application locks the object so it can obtain the object's address. The data object is initialized as a DDEDATA structure.

The server application then sets the cfFormat member of the structure to CF_TEXT to inform the client application that the data is in text format. The client responds by copying the value of the requested data into the Value member of the DDEDATA structure. After the server has filled the data object, the server unlocks the data and creates a global atom containing the name of the data item.

Finally, the server issues the WM_DDE_DATA message by calling PostMessage. The handle of the data object and the atom containing the item name are packed into the lParam parameter of the message by the PackDDElParam function.

If PostMessage fails, the server must use the FreeDDElParam function to free the packed lParam parameter. The server must also free the packed lParam parameter for the WM_DDE_REQUEST message it received.

If the server cannot satisfy the request, it sends a negative WM_DDE_ACK message to the client, as shown in the following example.

/* negative acknowledgment */

PostMessage(hwndClientDDE,

WM_DDE_ACK,

(WPARAM) hwndServerDDE,

PackDDElParam(WM_DDE_ACK, 0, atomItem));

Upon receiving a WM_DDE_DATA message, the client processes the data-item value as appropriate. Then, if the fAckReq member pointed to in the WM_DDE_DATA message is 1, the client must send the server a positive WM_DDE_ACK message, as shown in the following example.

UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT) &hData,

(PUINT) &atomItem);

if (!(lpDDEData = (DDEDATA FAR*) GlobalLock(hData))

|| (lpDDEData->cfFormat != CF_TEXT)) {

PostMessage(hwndServerDDE,

WM_DDE_ACK,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_ACK, 0, atomItem)); /* negative ACK */

}

/* Copy data from lpDDEData here.*/

if (lpDDEData->fAckReq) {

PostMessage(hwndServerDDE,

WM_DDE_ACK,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_ACK, 0x8000,

atomItem)); /* positive ACK */

}

bRelease = lpDDEData->fRelease;

GlobalUnlock(hData);

if (bRelease)

GlobalFree(hData);

In this example, the client examines the format of the data. If the format is not CF_TEXT (or if the client cannot lock the memory for the data), the client sends a negative WM_DDE_ACK message to indicate that it cannot process the data. If the client cannot lock a data handle because the handle contains the fAckReq member, the client should not send a negative WM_DDE_ACK message. Instead, the client should terminate the conversation.

If a client sends a negative acknowledgement in response to a WM_DDE_DATA message, the server is responsible for freeing the memory (but not the lParam parameter) referenced by the WM_DDE_DATA message associated with the negative acknowledgement.

If it can process the data, the client examines the fAckReq member of the DDEDATA structure to determine whether the server requested that it be informed that the client received and processed the data successfully. If the server did request this information, the client sends the server a positive WM_DDE_ACK message.

Because unlocking data invalidates the pointer to the data, the client saves the value of the fRelease member before unlocking the data object. After saving the value, the client then examines it to determine whether the server application requested the client to free the memory containing the data; the client acts accordingly.

Upon receiving a negative WM_DDE_ACK message, the client can ask for the same item value again, specifying a different clipboard format. Typically, a client will first ask for the most complex format it can support, then step down if necessary through progressively simpler formats until it finds one the server can provide.

If the server supports the Formats item of the system topic, the client can determine once what clipboard formats the server supports, instead of determining them each time the client requests an item. For more information about the system topic, see The System Topic.

+$#A Submitting an Item to the Server

The client may send an item value to the server by using the WM_DDE_POKE message. The client renders the item to be sent and sends the WM_DDE_POKE message, as illustrated in the following example.

if (!(hPokeData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,

(LONG) sizeof(DDEPOKE) + lstrlen(szValue) + 2)))

return;

if (!(lpPokeData = (DDEPOKE FAR*) GlobalLock(hPokeData))) {

GlobalFree(hPokeData);

return;

}

lpPokeData->fRelease = TRUE;

lpPokeData->cfFormat = CF_TEXT;

lstrcpy((LPSTR) lpPokeData->Value, (LPSTR) szValue);

/* Each line of CF_TEXT data is terminated by CR/LF. */

lstrcat((LPSTR) lpPokeData->Value, (LPSTR) "\r\n");

GlobalUnlock(hPokeData);

if ((atomItem = GlobalAddAtom((LPSTR) szItem)) != 0) {

.

.

.

if (!PostMessage(hwndServerDDE,

WM_DDE_POKE,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_POKE, (UINT) hPokeData,

atomItem))) {

GlobalDeleteAtom(atomItem);

GlobalFree(hPokeData);

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

Note that sending data by using a WM_DDE_POKE message is essentially the same as sending it by using WM_DDE_DATA, except that WM_DDE_POKE is sent from the client to the server.

If the server is able to accept the data-item value in the format rendered by the client, the server processes the item value as appropriate and sends the client a positive WM_DDE_ACK message. If it is unable to process the item value, because of its format or for other reasons, the server sends the client a negative WM_DDE_ACK message.

UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT) &hPokeData,

(PUINT) &atomItem);

GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE);

if (!(lpPokeData = (DDEPOKE FAR*) GlobalLock(hPokeData))

|| lpPokeData->cfFormat != CF_TEXT

|| !IsItemSupportedByServer(szItemName)) {

PostMessage(hwndClientDDE,

WM_DDE_ACK,

(WPARAM) hwndServerDDE,

PackDDElParam(WM_DDE_ACK, 0, atomItem)); /* negative ACK */

}

lstrcpy(szItemValue, lpPokeData->Value); /* copies value */

bRelease = lpPokeData->fRelease;

GlobalUnlock(hPokeData);

if (bRelease) {

GlobalFree(hPokeData);

}

PostMessage(hwndClientDDE,

WM_DDE_ACK,

(WPARAM) hwndServerDDE,

PackDDElParam(WM_DDE_ACK,

0x8000, atomItem)); /* positive ACK */

In this example, the server calls GlobalGetAtomName to retrieve the name of the item the client sent. The server then determines whether it supports the item and whether the item is rendered in the correct format (that is, CF_TEXT). If the item is not supported and not rendered in the correct format, or if the server cannot lock the memory for the data, the server sends a negative acknowledgment back to the client application. Note that in this case, sending a negative acknowledgement is correct because WM_DDE_POKE messages are always assumed to have the fAckReq member set. The server should ignore the member.

If a server sends a negative acknowledgement in response to a WM_DDE_POKE message, the client is responsible for freeing the memory (but not the lParam parameter) referenced by the WM_DDE_POKE message associated with the negative acknowledgement.

+$#A Establishing a Permanent Data Link

A client application can use DDE to establish a link to an item in a server application. After such a link is established, the server sends periodic updates of the linked item to the client, typically, whenever the value of the item changes. Thus, a permanent data stream is established between the two applications; this data stream remains in place until it is explicitly disconnected.

+$#A Initiating a Data Link

The client initiates a data link by posting a WM_DDE_ADVISE message, as shown in the following example.

if (!(hOptions = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,

sizeof(DDEADVISE))))

return;

if (!(lpOptions = (DDEADVISE FAR*) GlobalLock(hOptions))) {

GlobalFree(hOptions);

return;

}

lpOptions->cfFormat = CF_TEXT;

lpOptions->fAckReq = TRUE;

lpOptions->fDeferUpd = FALSE;

GlobalUnlock(hOptions);

if ((atomItem = GlobalAddAtom(szItemName)) != 0) {

if (!(PostMessage(hwndServerDDE,

WM_DDE_ADVISE,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_ADVISE, (UINT) hOptions,

atomItem)))) {

GlobalDeleteAtom(atomItem);

GlobalFree(hOptions);

FreeDDElParam(WM_DDE_ADVISE, lParam);

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

In this example, the client application sets the fDeferUpd flag of the WM_DDE_ADVISE message to FALSE. This directs the server application to send the data to the client whenever the data changes.

If the server is unable to service the WM_DDE_ADVISE request, it sends the client a negative WM_DDE_ACK message. But if the server has access to the item and can render it in the requested format, the server notes the new link (recalling the flags specified in the hOptions parameter) and sends the client a positive WM_DDE_ACK message. From then on, until the client issues a matching WM_DDE_UNADVISE message, the server sends the new data to the client every time the value of the item changes in the server.

The WM_DDE_ADVISE message establishes the format of the data to be exchanged during the link. If the client attempts to establish another link with the same item but is using a different data format, the server can choose to reject the second data format or attempt to support it. If a warm link has been established for any data item, the server can support only one data format at a time. This is because the WM_DDE_DATA message for a warm link has a NULL data handle, which otherwise contains the format information. Thus, a server must reject all warm links for an item already linked, and must reject all links for an item that has warm links. Another interpretation may be that the server changes the format and the hot or warm state of a link when a second link is requested for the same data item.

In general, client applications should not attempt to establish more than one link at a time for a data item.

+$#A Initiating a Data Link with the Paste Link Command

Applications that support hot or warm data links typically support a registered clipboard format named Link. When associated with the application's Copy and Paste Link commands, this clipboard format enables the user to establish DDE conversations between applications simply by copying a data item in the server application and pasting it into the client application.

A server application supports the Link clipboard format by placing in the clipboard a string containing the application, topic, and item names when the user chooses the Copy command from the Edit menu. Following is the standard Link format:

application\0topic\0item\0\0

A single null character separates the names, and two null characters terminate the entire string.

Both the client and server applications must register the Link clipboard format, as shown:

cfLink = RegisterClipboardFormat("Link");

A client application supports the Link clipboard format by means of a Paste Link command on its Edit menu. When the user chooses this command, the client application parses the application, topic, and item names from the Link-format clipboard data. Using these names, the client application initiates a conversation for the application and topic, if such a conversation does not already exist. The client application then sends a WM_DDE_ADVISE message to the server application, specifying the item name contained in the Link-format clipboard data.

Following is an example of a client application's response when the user chooses the Paste Link command.

void DoPasteLink(hwndClientDDE)

HWND hwndClientDDE;

{

HANDLE hData;

LPSTR lpData;

HWND hwndServerDDE;

CHAR szApplication[APP_MAX_SIZE + 1];

CHAR szTopic[TOPIC_MAX_SIZE + 1];

CHAR szItem[ITEM_MAX_SIZE + 1];

int nBufLen;

if (OpenClipboard(hwndClientDDE)) {

if (!(hData = GetClipboardData(cfLink)) ||

!(lpData = GlobalLock(hData))) {

CloseClipboard();

return;

}

/* Parse the clipboard data. */

if ((nBufLen = lstrlen(lpData)) >= APP_MAX_SIZE) {

CloseClipboard();

GlobalUnlock(hData);

return;

}

lstrcpy(szApplication, lpData);

lpData += (nBufLen + 1); /* skips over null */

if ((nBufLen = lstrlen(lpData)) >= TOPIC_MAX_SIZE) {

CloseClipboard();

GlobalUnlock(hData);

return;

}

lstrcpy(szTopic, lpData);

lpData += (nBufLen + 1); /* skips over null */

if ((nBufLen = lstrlen(lpData)) >= ITEM_MAX_SIZE) {

CloseClipboard();

GlobalUnlock(hData);

return;

}

lstrcpy(szItem, lpData);

GlobalUnlock(hData);

CloseClipboard();

if (hwndServerDDE =

FindServerGivenAppTopic(szApplication, szTopic)) {

/* App/topic conversation is already started. */

if (DoesAdviseAlreadyExist(hwndServerDDE, szItem))

MessageBox(hwndMain,

"Advisory already established",

"Client", MB_ICONEXCLAMATION | MB_OK);

else

SendAdvise(hwndClientDDE, hwndServerDDE, szItem);

}

else {

/* Client must initiate a new conversation first. */

SendInitiate(szApplication, szTopic);

if (hwndServerDDE =

FindServerGivenAppTopic(szApplication,

szTopic))

SendAdvise(hwndClientDDE, hwndServerDDE, szItem);

}

}

return;

}

In this example, the client application opens the clipboard and determines whether it contains data in the Link format (that is, cfLink) it had previously registered. If not, or if it cannot lock the data in the clipboard, the client returns.

After the client application retrieves a pointer to the clipboard data, it parses the data to extract the application, topic, and item names.

The client application determines whether a conversation on the topic already exists between it and the server application. If a conversation does exist, the client checks whether a link already exists for the data item. If such a link exists, the client displays a message box to the user; otherwise, it calls its own SendAdvise function to send a WM_DDE_ADVISE message to the server for the item.

If a conversation on the topic does not already exist between the client and the server, the client first calls its own SendInitiate function to broadcast the WM_DDE_INITIATE message to request a conversation and, second, calls its own FindServerGivenAppTopic function to establish the conversation with the window that responds on behalf of the server application. After the conversation has begun, the client application calls SendAdvise to request the link.

+$#A Notifying the Client that Data Has Changed

When the client establishes a link by using the WM_DDE_ADVISE message, with the fDeferUpd member not set (that is, equal to zero) in the DDEDATA structure, the client has requested the server send the data item each time the item's value changes. In such cases, the server renders the new value of the data item in the previously specified format and sends the client a WM_DDE_DATA message, as shown in the following example.

/*

* Allocate the size of a DDE data header, plus data (a string),

* plus a <CR><LF><NULL>

*/

if (!(hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,

sizeof(DDEDATA) + lstrlen(szItemValue) + 3)))

return;

if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) {

GlobalFree(hData);

return;

}

lpData->fAckReq = bAckRequest; /* as specified in original */

/* WM_DDE_ADVISE message */

lpData->cfFormat = CF_TEXT;

lstrcpy(lpData->Value, szItemValue); /* copies value to be sent */

lstrcat(lpData->Value, "\r\n"); /* CR/LF for CF_TEXT format */

GlobalUnlock(hData);

if ((atomItem = GlobalAddAtom(szItemName)) != 0) {

if (!PostMessage(hwndClientDDE,

WM_DDE_DATA,

(WPARAM) hwndServerDDE,

PackDDElParam(WM_DDE_DATA, (UINT) hData, atomItem))) {

GlobalFree(hData);

GlobalDeleteAtom(atomItem);

FreeDDElParam(WM_DDE_DATA, lParam);

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

In this example, the client processes the item value as appropriate. If the fAckReq flag for the item is set, the client sends the server a positive WM_DDE_ACK message.

When the client establishes the link, with the fDeferUpd member set (that is, equal to 1), the client has requested that only a notification, not the data itself, be sent each time the data changes. In such cases, when the item value changes, the server does not render the value but simply sends the client a WM_DDE_DATA message with a null data handle, as illustrated in the following example.

if (bDeferUpd) { /* checking whether the flag was originally */

/* set in the WM_DDE_ADVISE message */

if ((atomItem = GlobalAddAtom(szItemName)) != 0) {

if (!PostMessage(hwndClientDDE,

WM_DDE_DATA,

(WPARAM) hwndServerDDE,

PackDDElParam(WM_DDE_DATA, 0,

atomItem))) { /* NULL data */

GlobalDeleteAtom(atomItem);

FreeDDElParam(WM_DDE_DATA, lParam);

}

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

As necessary, the client can request the latest value of the data item by issuing a normal WM_DDE_REQUEST message, or it can simply ignore the notice from the server that the data has changed. In either case, if fAckReq is equal to 1, the client is expected to send a positive WM_DDE_ACK message to the server.

+$#A Terminating a Data Link

If the client requests that a specific data link be terminated, the client sends the server a WM_DDE_UNADVISE message, as shown in the following example.

if ((atomItem = GlobalAddAtom(szItemName)) != 0) {

if (!PostMessage(hwndServerDDE,

WM_DDE_UNADVISE,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_UNADVISE, 0, atomItem))) {

GlobalDeleteAtom(atomItem);

FreeDDElParam(WM_DDE_UNADVISE, lParam);

}

}

if (atomItem == 0) {

.

. /* error handling */

.

}

The server checks whether the client currently has a link to the specific item in this conversation. If a link exists, the server sends the client a positive WM_DDE_ACK message; the server is then no longer required to send updates about the item. If no link exists, the server sends the client a negative WM_DDE_ACK message.

The WM_DDE_UNADVISE message specifies a data format. A format of zero informs the server to stop all links for the specified item, even if several hot links are established and each uses a different format.

To terminate all links for a conversation, the client application sends the server a WM_DDE_UNADVISE message with a null item atom. The server determines whether the conversation has at least one link currently established. If a link exists, the server sends the client a positive WM_DDE_ACK message; the server then no longer has to send any updates in the conversation. If no link exists, the server sends the client a negative WM_DDE_ACK message.

+$#A Carrying Out Commands in a Server Application

A Win32-based application can use the WM_DDE_EXECUTE message to cause a certain command or series of commands to be carried out in another application. To do this, the client sends the server a WM_DDE_EXECUTE message containing a handle to a command string, as shown in the following example.

if (!(hCommand = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,

sizeof(szCommandString) + 1)))

return;

if (!(lpCommand = GlobalLock(hCommand))) {

GlobalFree(hCommand);

return;

}

lstrcpy(lpCommand, szCommandString);

GlobalUnlock(hCommand);

if (!PostMessage(hwndServerDDE,

WM_DDE_EXECUTE,

(WPARAM) hwndClientDDE,

PackDDElParam(WM_DDE_EXECUTE, 0, (UINT) hCommand))) {

GlobalFree(hCommand);

FreeDDElParam(WM_DDE_EXECUTE, lParam);

}

In this example, the server attempts to carry out the specified command string. If it succeeds, the server sends the client a positive WM_DDE_ACK message; otherwise, it sends a negative WM_DDE_ACK message. This WM_DDE_ACK message reuses the hCommand handle passed in the original WM_DDE_EXECUTE message.

If the client's command execution string requests that the server terminate, the server should respond by sending a positive WM_DDE_ACK message and then post a WM_DDE_TERMINATE message before terminating. All other commands sent with a WM_DDE_EXECUTE message should be executed synchronously; that is, the server should send a WM_DDE_ACK message only after successfully completing the command.

+$#A Terminating a Conversation

Either the client or the server can issue a WM_DDE_TERMINATE message to terminate a conversation at any time. Similarly, both the client and server applications should be prepared to receive this message at any time. An application must terminate all of its conversations before shutting down.

In the following example, the application terminating the conversation posts a WM_DDE_TERMINATE message.

PostMessage(hwndServerDDE, WM_DDE_TERMINATE,

PackDDElParam(WM_DDE_TERMINATE, (UINT) hwndClientDDE, 0), 0);

This informs the other application that the sending application will send no further messages and the recipient can close its window. The recipient is expected in all cases to respond promptly by sending a WM_DDE_TERMINATE message. The recipient must not send a negative, busy, or positive WM_DDE_ACK message.

After an application has sent the WM_DDE_TERMINATE message to the partner in a DDE conversation, it must not respond to messages from that partner, since the partner might have destroyed the window to which the response would be sent.

If an application receives a DDE message other than WM_DDE_TERMINATE after it has posted WM_DDE_TERMINATE, it should free all objects associated with the received messages except the data handles for WM_DDE_DATA or WM_DDE_POKE messages that do not have the fRelease member set.

When an application is about to terminate, it should end all active DDE conversations before completing processing of the WM_DESTROY message. However, if an application does not end its active DDE conversations, the system will terminate any DDE conversations associated with a window when the window is destroyed. The following example shows how a server application terminates all DDE conversations.

void TerminateConversations(hwndServerDDE)

HWND hwndServerDDE;

{

HWND hwndClientDDE;

/* Terminate each active conversation. */

while (hwndClientDDE = GetNextLink(hwndClientDDE)) {

SendTerminate(hwndServerDDE, hwndClientDDE);

}

return;

}

BOOL AtLeastOneLinkActive(VOID)

{

return TRUE;

}

HWND GetNextLink(hwndDummy)

HWND hwndDummy;

{

return (HWND) 1;

}

VOID SendTerminate(HWND hwndServerDDE, HWND hwndClientDDE)

{

return;

}

+$#AK Dynamic Data Exchange Reference

The following functions and messages are associated with dynamic data exchange.

+$#A Dynamic Data Exchange Functions

Following are the functions used with DDE.

DdeSetQualityOfService
FreeDDElParam
ImpersonateDdeClientWindow
PackDDElParam
ReuseDDElParam
UnpackDDElParam

+$#A Dynamic Data Exchange Messages

Following are the messages used with DDE.

WM_DDE_ACK
WM_DDE_ADVISE
WM_DDE_DATA
WM_DDE_EXECUTE
WM_DDE_INITIATE
WM_DDE_POKE
WM_DDE_REQUEST
WM_DDE_TERMINATE
WM_DDE_UNADVISE

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques EPub facilement


Dynamic-Link Libraries.rtf

+$#AK Dynamic-Link Libraries

In Microsoft® Windows®, dynamic linking provides a way for a process to call a function that is not part of its executable code. The executable code for the function is located in a dynamic-link library (DLL), containing one or more functions that are compiled, linked, and stored separately from the processes using them. The Microsoft® Win32® application programming interface (API) is implemented as a set of dynamic-link libraries, so any process using the Win32 API uses dynamic linking.

+$#A About Dynamic-Link Libraries

Dynamic linking allows an executable module (either a .DLL or .EXE file) to include only the information needed at run time to locate the executable code for a DLL function. This type of linking differs from the more familiar static linking, which requires a copy of a library function's executable code in the executable module of each application using it.

There are two methods for calling a function in a DLL:

• Load-time dynamic linking occurs when an application's code makes an explicit call to a DLL function. This type of linking requires that the executable module of the application be built by linking with the DLL's import library, which supplies the information needed to locate the DLL function when the application starts. For more information, see Load-Time Dynamic Linking.

• Run-time dynamic linking occurs when a program uses the LoadLibrary and GetProcAddress functions to get the starting address of a DLL function. This type of linking eliminates the need to link with an import library. For more information, see Using Run-Time Dynamic Linking.

A DLL attaches to a process when the process starts or when one of its threads calls the LoadLibrary function. When a DLL attaches to a process, the operating system maps the DLL module into the address space of the process, making the DLL's executable code available to the process. When a DLL detaches from a process at process termination or as a result of a call to the FreeLibrary function, the DLL module is unmapped from the address space of the process.

Like any other function, a DLL function is executed in the context of the thread (and the thread's process) that calls it. Therefore, the following conditions apply:

• The threads of the process that called the DLL can use handles opened by a DLL function. Similarly, handles opened by any thread of the calling process can be used in the DLL function.

• The DLL uses the user stack of the calling thread and the address space of the calling process.

• Memory allocated by the DLL is in the address space of the calling process.

• The threads of the process that called the DLL can read from or write to the globally declared variables in the DLL.

+$#A Advantages of Dynamic Linking

Dynamic linking has the following advantages:

• Many processes can use a single DLL simultaneously, sharing a single copy of the DLL in memory. Doing this saves memory space and reduces swapping.

• When the functions in a DLL change, the applications that use them do not need to be recompiled or relinked as long as the functions' arguments and return values do not change. In contrast, statically linked object code requires that the application be relinked when the functions change.

• A DLL can provide after-market support. For example, a display driver DLL can be modified to support a display that was not available when the application was shipped.

• Programs written in different programming languages can call the same DLL function as long as the programs follow the function's calling convention. The programs and the DLL function must be compatible in the following ways: the order the function expects its arguments to be pushed onto the stack, whether the function or the application is responsible for cleaning up the stack, and whether any arguments are passed in registers.

A potential disadvantage to using DLLs is that the application is not self-contained; it depends on the existence of a separate DLL module. A process using load-time dynamic linking is terminated by the operating system if the DLL is not found at process startup. A process using run-time dynamic linking is not terminated in this situation, but capabilities provided by the DLL are not available to the program.

+$#A Dynamic-Link Library Creation

To create a DLL, first create one or more source code (.C) files and a module-definition (.DEF) file. The .C file contains the exported functions for the DLL. The .DEF file lists these functions and defines other attributes of the DLL.

The compiler uses the .C file to create an object code (.OBJ) file. The library manager creates an export (.EXP) file and an import library (.LIB) file from the .DEF file.

Finally, the linker uses the .OBJ, .EXP, and .LIB files to build the executable files for the DLL. The .OBJ and .EXP files provide information the linker needs to build the .DLL file. The .LIB file contains information the linker needs to resolve external references to DLL functions, so that a program can locate the specified DLL at run time. The .LIB file enables the linker to build an executable module (.EXE or .DLL) that calls a function in the DLL. For more information about .DEF files, see Module-Definition File.

Note that the information in this section is based on using Win32 software development tools. For more detailed information about creating DLLs, see the documentation included with your development tools.

+$#A Dynamic-Link Library Entry-Point Function

A DllEntryPoint function is an optional DLL function that the operating system calls to perform user-defined operations. DllEntryPoint is not a typical function; it is a reference to a function specified in the linker command line. A DLL is not required to have an entry-point function, but it can be useful for initializing and cleaning up processes and threads. The name of a DLL's entry-point function is specified by the -entry switch of the linker. For example, the following command line specifies LibMain as the entry-point function of MYDLL.DLL.

link -entry:LibMain -out:mydll.dll mydll.exp mydll.obj $(mylibs)

If an entry-point function is specified, the operating system causes it to be called whenever any of the following events occurs:

• The DLL attaches to a process. For processes using load-time dynamic linking, attachment occurs during initialization. For processes using run-time linking, the DLL attaches and the entry-point function is called before LoadLibrary returns.

• The DLL detaches from a process. Detachment occurs when the process terminates or when the DLL is unloaded by a call to the FreeLibrary function. If the process terminates as a result of the TerminateProcess or TerminateThread function, the system does not call the entry-point function.

• The DLL attaches to a thread. Attachment occurs whenever a new thread is created in a process to which a DLL is attached.

• The DLL detaches from a thread. Detachment occurs whenever a thread of an attached process terminates, except as a result of the TerminateThread or TerminateProcess function. When a DLL detaches from a process, the entry-point function is called only once for the entire process rather than once for each existing thread of the process.

The system causes the entry-point function to be called, but it is called in the context of a process or thread attaching to or detaching from the DLL. This allows a DLL to use its entry-point function for allocating memory in the address space of the process or to open handles accessible to the process. When it is called to attach a new thread, the entry-point function can allocate memory for thread local storage (TLS) that is private to the new thread. For more information about thread local storage, see Processes and Threads.

When a DLL's entry-point function is called for process attachment, it returns TRUE to indicate success. For processes using load-time linking, a return value of FALSE causes the process initialization to fail and the process terminates. For processes using run-time linking, a return value of FALSE causes the LoadLibrary function to return NULL, indicating failure. The return value of the entry-point function is disregarded when it is called for any other reasons, such as process detaching, thread attaching, or thread detaching.

+$#A Module-Definition File

A module-definition (.DEF) file is a text file containing one or more module statements that describe various attributes of a DLL. The descriptions and examples in this section are based on the keywords and syntax used by the Win32 software development tools. For complete information about module-definition files, see the documentation included with your development tools.

A DLL requires a .DEF file to create an import library (.LIB) file and an export (.EXP) file. The linker uses the import library to build any executable module that uses the DLL and uses the export file to build the .DLL file.

A DLL's module-definition file must include a LIBRARY statement and an EXPORTS statement. The LIBRARY statement identifies the module-definition file as belonging to a DLL, and it must be the first statement in the file. The name specified in the LIBRARY statement identifies the library in the DLL's import library. The EXPORTS statement lists the names and, optionally, the ordinal values of the functions exported by the DLL. When ordinal values are specified, they must be in the range 1 through N, where N is the number of functions exported by the DLL.

The following .DEF file for a DLL exports three functions and associates an ordinal value with each function.

LIBRARY mydll

EXPORTS

Func_A @1

Func_B @2

Func_C @3

The .DEF file can contain additional statements, specifying other attributes of a DLL. For example, the SECTIONS statements can change the default scope of global and static variables. The following SECTIONS statement makes the DLL's initialized data shared, but its uninitialized variables are not shared.

SECTIONS

.data READ WRITE SHARED ; share initialized variables

+$#A Load-Time Dynamic Linking

Load-time dynamic linking occurs when an application's code explicitly calls a DLL function. When the source code is compiled or assembled, the DLL function call generates an external function reference in the object code. To resolve this external reference, the application must be linked with the import library for the DLL.

Finding an external function in an import library informs the linker that the code for that function is in a DLL. To resolve external references to DLLs, the linker simply adds information to the executable file that tells the system where to find the DLL code when the process starts up.

When the system starts a program that contains dynamically linked references, it uses the information in the program's executable file to locate the required DLLs. The system first searches the set of pre-installed DLLs, such as the performance library (KERNEL32.DLL) and the security library (USER32.DLL). The system then searches for the DLLs in the following sequence:

1. The directory where the executable module for the current process is located.

2. The current directory.

3. The windows system directory. The GetSystemDirectory function retrieves the path of this directory.

4. The windows directory. The GetWindowsDirectory function retrieves the path of this directory.

5. The directories listed in the PATH environment variable.

Note that the LIBPATH environment variable is not used.

If it cannot locate a specified DLL, the system terminates the process and displays a dialog box that reports the error. Otherwise, the system maps the DLL modules into the process's address space.

If any of the DLLs has a DllEntryPoint function, the operating system calls the function. DllEntryPoint specifies a code that indicates the DLL is attaching to the process. If the entry-point function does not return TRUE, the system terminates the process and reports the error. For more information about the entry-point function, see Dynamic-Link Library Entry-Point Function.

Finally, the system modifies the executable code of the process to provide starting addresses for the DLL functions.

Like the rest of a program's code, DLL code is mapped into the address space of the process when the process starts up and is loaded into memory only when needed. As a result, the PRELOAD and LOADONCALL code attributes used by .DEF files to control loading in previous versions of Windows no longer have meaning.

+$#A Run-Time Dynamic Linking

Run-time dynamic linking occurs when the process calls the LoadLibrary function to specify the name of a DLL and the GetProcAddress function to get the starting address of any function in the DLL. Run-time dynamic linking eliminates the need to link the process with an import library. Since the process does not explicitly call the DLL's functions, it does not generate external references to them.

If the call to LoadLibrary specifies a DLL module already mapped into the address space of the calling process, the function simply returns a handle of the DLL and increments the module's reference count. Otherwise, LoadLibrary attempts to locate the DLL using the same search sequence used for load-time dynamic linking (see Load-Time Dynamic Linking). If the search succeeds, the system maps the DLL module into the address space of the process. If the DLL has a DllEntryPoint function, the operating system calls the function in the context of the thread that called LoadLibrary. The entry-point function is not called if the DLL is already attached to the process due to a previous call to LoadLibrary with no corresponding call to the FreeLibrary function.

If the system cannot find the DLL or if the entry-point function returns FALSE, LoadLibrary returns NULL. If LoadLibrary succeeds, it returns a handle of the DLL module. The process can use this handle to identify the DLL in a call to the GetProcAddress or FreeLibrary function.

The GetModuleHandle function also returns a handle used in GetProcAddress or FreeLibrary. GetModuleHandle succeeds only if the DLL module is already mapped into the address space of the process by load-time linking or by a previous call to LoadLibrary. Unlike LoadLibrary, GetModuleHandle does not increment the module's reference count. The GetModuleFileName function retrieves the full path of the module associated with a handle returned by GetModuleHandle or LoadLibrary.

The process can use GetProcAddress to get the starting handle returned by LoadLibrary or the address of a function in the DLL using a DLL module handle returned by either LoadLibrary or GetModuleHandle. The function is specified by using its name or ordinal value as listed in the EXPORTS statement of the DLL's .DEF file.

When the DLL module is no longer needed, the process can call the FreeLibrary function. This function decrements the module's reference count and unmaps it from the address space of the process if the reference count is zero.

Run-time dynamic linking enables the process to continue executing even if a DLL is not available. The process can then use an alternate method to accomplish its objective. For example, if a process is unable to locate one DLL, it can try to use another, or it can notify the user of an error. If the user can provide the full path of the missing DLL, the process can use this information to load the library even though it is not in the normal search path. This situation contrasts with load-time linking in which the operating system simply terminates the process if it cannot find the DLL.

It is not necessary for the developer to know the names of either the DLLs or the functions within a DLL. The process can get this information from a configuration file or from the user.

Run-time dynamic linking can cause problems if the DLL is using the DllEntryPoint function to perform initialization for each thread of a process, because threads existing when LoadLibrary is called will not be initialized. For an example showing how to deal with this problem, see Using Thread Local Storage in a Dynamic-Link Library.

+$#A Dynamic-Link Library Data

Windows supports the use of the following DLL data:

• Global and static variables in a DLL module private to each process that uses the DLL.

• Global and static variables shared by all processes simultaneously using a DLL.

• Dynamically allocated memory private to each process that uses a DLL.

• Dynamically allocated memory shared by multiple processes.

• Static storage private to each thread of a multithreaded process.

The default scope of a DLL's variables is the same as that of any statically linked code. Global variables in a DLL source code file are global to each process using the DLL. Static variables create permanent storage for each process, limiting their scope to the block in which they are declared. As a result, each process has its own instance of a DLL's global and static variables by default.

Win32 development tools enable a DLL's .DEF file to override the default scope of global and static variables. For more information, see Module-Definition File and the documentation included with your development tools.

When a DLL allocates memory using any of the allocation functions (GlobalAlloc, LocalAlloc, HeapAlloc, VirtualAlloc, malloc, and other C run-time allocation functions), the memory is allocated in the address space of the calling process and is accessible only to the threads of that process. A DLL's .DEF file cannot be used to change the scope of dynamically allocated memory.

A DLL can use file mapping to allocate memory that can be shared among processes. For a general discussion of using file mapping to create named shared memory, see File Mapping. For an example that uses the DllEntryPoint function to set up shared memory using file mapping, see Using Shared Memory in a Dynamic-Link Library.

The thread local storage functions enable a DLL to allocate an index for storing and retrieving a different value for each thread of a multithreaded process. For example, a spreadsheet application can create a new instance of the same thread each time the user opens a new spreadsheet. A DLL providing the functions for various spreadsheet operations can use thread local storage to save information about the current state of each spreadsheet (row, column, and so on). For a general discussion of thread local storage, see Processes and Threads. For an example that uses the DllEntryPoint function to set up thread local storage, see Using Thread Local Storage in a Dynamic-Link Library.

+$#A Using Dynamic-Link Libraries

• Creating a simple dynamic-link library

• Using load-time dynamic linking

• Using run-time dynamic linking

• Using shared memory in a dynamic-link library

• Using thread local storage in a dynamic-link library

+$#A Creating a Simple Dynamic-Link Library

DLLs make common functions available to a number of processes or threads at once. The following file, MYPUTS.C, contains a simple string printing function. This DLL does not use a DllEntryPoint function because it has no initialization or cleanup functions to perform.

#include <windows.h>

/*

* A function that writes a null-terminated string to the

* console output.

*/

VOID myPuts(LPTSTR lpszMsg) {

DWORD cchWritten;

HANDLE hStdout;

/* Get a handle to the console output. */

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Write a null-terminated string to the console output. */

while (*lpszMsg)

WriteFile(hStdout, lpszMsg++, 1, &cchWritten, NULL);

}

The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.

LIBRARY myputs

EXPORTS

myPuts

The name specified in the LIBRARY statement identifies the DLL in the DLL's import library. Load-time dynamic linking enables the system to use this name for locating the DLL. If run-time dynamic linking is being used, the LoadLibrary function does not use this name to locate the DLL. The EXPORTS statement indicates that other .EXE or .DLL modules can call the myPuts function.

The source code in MYPUTS.C is compiled to produce an object module (MYPUTS.OBJ). The library manager uses the module-definition file (MYPUTS.DEF) to create the import library (MYPUTS.LIB) and the export file (MYPUTS.EXP). The linker uses MYPUTS.EXP and MYPUTS.OBJ to produce the executable DLL module (MYPUTS.DLL). MYPUTS.LIB is not used in creating MYPUTS.DLL, but the linker uses it to create any executable module (.EXE or .DLL) that calls the myPuts function.

Following are some typical command lines used to compile and link a DLL. For more information about compiling and linking dynamic-link libraries and programs that use them, see the documentation included with your development tools.

cvars=-DWIN32 -D_MT -DSTRICT

# Compile myputs.c to create myputs.obj.

myputs.obj: myputs.c

$(cc) $(cflags) $(cvars) myputs.c

# Create the DLL's import library.

# Also create the myputs.exp export file.

myputs.lib: myputs.def

$(lib) -machine:i386 \

-def:myputs.def \

-out:myputs.lib

# Link myputs.obj and myputs.exp to create myputs.dll.

# myputs.lib is not used here but is included as a

# dependency to force creation of myputs.exp.

myputs.dll: myputs.obj myputs.lib

$(link) \

-out:myputs.dll \

myputs.exp myputs.obj $(conlibs)

# If the DLL has a DllEntryPoint function, substitute a line

# like this to specify the name of the entry-point function.

# $(link) -entry:LibMain \

+$#A Using Load-Time Dynamic Linking

Once you have created a DLL, you can use it in an application. The following file, LOADTIME.C, is the source code for a simple console process that uses the myPuts function from MYPUTS.DLL.

/*

* File: loadtime.c

* A simple program that uses myPuts() from myputs.dll

*/

#include <windows.h>

VOID myPuts(LPTSTR); /* a function from a DLL */

VOID main(VOID) {

myPuts("message printed using the DLL function\n");

}

Because LOADTIME.C calls the DLL function explicitly, the executable module for the application must be linked with the import library (MYPUTS.LIB). Following are some typical command lines used for compiling and linking a program that uses a DLL.

# Compile loadtime.c to create loadtime.obj.

loadtime.obj: loadtime.c

$(cc) $(cflags) $(cvars) loadtime.c

# Link loadtime.obj and myputs.lib to create loadtime.exe.

loadtime.exe: loadtime.obj myputs.lib

$(link) $(conflags) \

-out:loadtime.exe loadtime.obj myputs.lib $(conlibs)

+$#A Using Run-Time Dynamic Linking

You can use the same DLL in both load-time and run-time dynamic linking. If the system can find the specified DLL module, the following source code produces the same output as the load-time example in the previous section. The program uses the LoadLibrary function to get a handle of MYPUTS.DLL. If LoadLibrary succeeds, the program uses the returned handle in the GetProcAddress function to get the starting address of the DLL's myPuts function. After calling the DLL function, the program calls the FreeLibrary function to detach the DLL from the process.

The following example illustrates an important difference between run-time and load-time dynamic linking. If the MYPUTS.DLL file is not available, the application using load-time dynamic linking simply terminates. The run-time dynamic linking example, however, can respond to the error.

/*

* File: runtime.c

* A simple program that uses LoadLibrary and

* GetProcAddress to access myPuts() from the myputs DLL.

*/

#include <stdio.h>

#include <windows.h>

typedef VOID (*MYPROC)(LPTSTR);

VOID main(VOID) {

HINSTANCE hinstLib;

MYPROC ProcAdd;

BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;

/* Get a handle to the DLL module. */

hinstLib = LoadLibrary("myputs");

/* If the handle is valid, try to get the function address. */

if (hinstLib != NULL) {

ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts");

/* If the function address is valid, call the function. */

if (fRunTimeLinkSuccess = (ProcAdd != NULL))

(ProcAdd) ("message via DLL function\n");

/* Free the DLL module. */

fFreeResult = FreeLibrary(hinstLib);

}

/* If unable to call the DLL function, use an alternative. */

if (! fRunTimeLinkSuccess)

printf("message via alternative method\n");

}

Because the program uses run-time dynamic linking, it is not necessary to link with the DLL's import library when creating the program's executable module. The following lines are typical of the command lines used to compile and link a program using run-time linking.

# Compile runtime.c to create runtime.obj.

runtime.obj: runtime.c

$(cc) $(cflags) $(cvars) runtime.c

# Link runtime.obj to create loadtime.exe.

runtime.exe: runtime.obj

$(link) $(conflags) \

-out:runtime.exe runtime.obj $(conlibs)

+$#A Using Shared Memory in a Dynamic-Link Library

This section shows the use of a DLL's entry-point function to set up memory that can be shared by multiple processes. In this DLL, the name of the entry-point function is LibMain. The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.

LIBRARY myputs

EXPORTS

myPuts

The example uses file mapping to map a block of named shared memory into the address space of each process to which the DLL is attached. The entry-point function calls the CreateFileMapping function to create a file-mapping object whenever the DLL attaches to a new process. This function can be used to either create or open a named file-mapping object. The first process to which the DLL attaches creates the file-mapping object. Subsequent processes open a handle of the existing object. The GetLastError function triggers initialization of the shared memory because it returns NO_ERROR to the first process and ERROR_ALREADY_EXISTS to subsequent processes.

The MapViewOfFile function maps the file-mapping object into the address space of each process, returning a pointer to the memory. Note that the shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of lpvMem, which is declared as a global variable so that it is available to the DLL's functions. The example assumes that the DLL's global data is not shared, so each attaching process has its own instance of lpvMem. By default, DLL data is not shared, but the default can be modified by the DLL's module-definition (.DEF) file (see Module-Definition File).

Any shared DLL memory persists only as long as the DLL is attached to a process. In this example, the shared memory is released when the last handle of the file-mapping object is closed. To create persistent shared memory, a DLL can create a detached process (see Processes and Threads) when the DLL attaches to the first process. If this detached process uses the DLL and does not terminate, it has a handle of the file-mapping object that prevents the shared memory from being released.

/*

* File: dllshmem.c

*

* A DLL entry-point function sets up shared memory using

* named file mapping.

*/

#include <windows.h>

#include <memory.h>

#define SHMEMSIZE 4096

static LPVOID lpvMem = NULL; /* address of shared memory */

/*

* LibMain() is the DllEntryPoint function for this DLL.

*/

BOOL LibMain(HINSTANCE hinstDLL, /* DLL module handle */

DWORD fdwReason, /* reason called */

LPVOID lpvReserved) { /* reserved */

HANDLE hMapObject = NULL; /* handle to file mapping */

BOOL fInit, fIgnore;

switch (fdwReason) {

/*

* The DLL is attaching to a process due to process

* initialization or a call to LoadLibrary.

*/

case DLL_PROCESS_ATTACH:

/* Create a named file mapping object */

hMapObject = CreateFileMapping(

(HANDLE) 0xFFFFFFFF, /* use paging file */

NULL, /* no security attr. */

PAGE_READWRITE, /* read/write access */

0, /* size: high 32-bits */

SHMEMSIZE, /* size: low 32-bits */

"dllmemfilemap"); /* name of map object */

if (hMapObject == NULL)

return FALSE;

/* The first process to attach initializes memory. */

fInit = (GetLastError() != ERROR_ALREADY_EXISTS);

/* Get a pointer to the file-mapped shared memory. */

lpvMem = MapViewOfFile(

hMapObject, /* object to map view of */

FILE_MAP_WRITE, /* read/write access */

0, /* high offset: map from */

0, /* low offset: beginning */

0); /* default: map entire file */

if (lpvMem == NULL)

return FALSE;

/* Initialize memory if this is the first process. */

if (fInit)

memset(lpvMem, '\0', SHMEMSIZE);

break;

/* The attached process creates a new thread. */

case DLL_THREAD_ATTACH:

break;

/* The thread of the attached process terminates. */

case DLL_THREAD_DETACH:

break;

/*

* The DLL is detaching from a process due to

* process termination or a call to FreeLibrary.

*/

case DLL_PROCESS_DETACH:

/* Unmap shared memory from the process's address space. */

fIgnore = UnmapViewOfFile(lpvMem);

/* Close the process's handle to the file-mapping object. */

fIgnore = CloseHandle(hMapObject);

break;

default:

break;

}

return TRUE;

UNREFERENCED_PARAMETER(hinstDLL);

UNREFERENCED_PARAMETER(lpvReserved);

}

/*

* SetSharedMem sets the contents of shared memory.

*/

VOID SetSharedMem(LPTSTR lpszBuf) {

LPTSTR lpszTmp;

/* Get the address of the shared memory block. */

lpszTmp = (LPTSTR) lpvMem;

/* Copy the null-terminated string into shared memory. */

while (*lpszBuf)

*lpszTmp++ = *lpszBuf++;

*lpszTmp = '\0';

}

/*

* GetSharedMem gets the contents of shared memory.

*/

VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize) {

LPTSTR lpszTmp;

/* Get the address of the shared memory block. */

lpszTmp = (LPTSTR) lpvMem;

/* Copy from shared memory into the caller's buffer. */

while (*lpszTmp && --cchSize)

*lpszBuf++ = *lpszTmp++;

*lpszBuf = '\0';

}

+$#A Using Thread Local Storage in a Dynamic-Link Library

This section shows the use of a DLL's entry-point function to set up a thread local storage (TLS) index to provide private storage for each thread of a multithreaded process.

The entry-point function uses the TlsAlloc function to allocate a TLS index whenever the DLL attaches to a process. Each thread of the process can then use this index to store a pointer to its own block of memory.

When the entry-point function is called with the DLL_PROCESS_ATTACH value, it peforms the following actions:

1. Uses the TlsAlloc function to allocate a TLS index.

2. Allocates a block of memory to be used exclusively by the initial thread of the process.

3. Uses the TLS index in a call to the TlsSetValue function to store a pointer to the allocated memory.

Each time the process creates a new thread, the entry-point function is called with the DLL_THREAD_ATTACH value. The entry-point function then allocates a block of memory for the new thread and stores a pointer to it by using the TLS index. Each thread can use the TLS index in a call to TlsGetValue to retrieve the pointer to its own block of memory.

When a thread terminates, the entry-point function is called with the DLL_THREAD_DETACH value and the memory for that thread is freed. When a process terminates, the entry-point function is called with the DLL_PROCESS_DETACH value and the TLS index is freed.

The TLS index is stored in a global variable, making it available to all of the DLL's functions. The following example assumes that the DLL's global data is not shared, because the TLS index is not necessarily the same for each process to which the DLL is attached.

static DWORD dwTlsIndex; /* address of shared memory */

/*

* LibMain() is the DllEntryPoint function for this DLL.

*/

BOOL LibMain(HINSTANCE hinstDLL, /* DLL module handle */

DWORD fdwReason, /* reason called */

LPVOID lpvReserved) {/* reserved */

LPVOID lpvData;

BOOL fIgnore;

switch (fdwReason) {

/*

* The DLL is attaching to a process due to process

* initialization or a call to LoadLibrary.

*/

case DLL_PROCESS_ATTACH:

/* Allocate a TLS index. */

if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF)

return FALSE;

/* No break: Initialize the index for first thread. */

/* The attached process creates a new thread. */

case DLL_THREAD_ATTACH:

/* Initialize the TLS index for this thread. */

lpvData = (LPVOID) LocalAlloc(LPTR, 256);

if (lpvData != NULL)

fIgnore = TlsSetValue(dwTlsIndex, lpvData);

break;

/* The thread of the attached process terminates. */

case DLL_THREAD_DETACH:

/* Release the allocated memory for this thread. */

lpvData = TlsGetValue(dwTlsIndex);

if (lpvData != NULL)

LocalFree((HLOCAL) lpvData);

break;

/*

* The DLL is detaching from a process due to

* process termination or call to FreeLibrary.

*/

case DLL_PROCESS_DETACH:

/* Release the allocated memory for this thread. */

lpvData = TlsGetValue(dwTlsIndex);

if (lpvData != NULL)

LocalFree((HLOCAL) lpvData);

/* Release the TLS index. */

TlsFree(dwTlsIndex);

break;

default:

break;

}

return TRUE;

UNREFERENCED_PARAMETER(hinstDLL);

UNREFERENCED_PARAMETER(lpvReserved);

}

When a process uses load-time linking with this DLL, the entry-point function is sufficient to manage the thread local storage. Problems can occur with a process that uses run-time linking because the TLS memory is not allocated for threads that exist before the LoadLibrary function is called to attach the process. The following example solves this problem by checking the value returned by the TlsGetValue function and allocating memory if the value indicates that the TLS slot for this thread is not set.

LPVOID lpvData;

/* Retrieve a data pointer for the current thread. */

lpvData = TlsGetValue(dwTlsIndex);

/* If NULL, allocate memory for this thread. */

if (lpvData == NULL) {

lpvData = (LPVOID) LocalAlloc(LPTR, 256);

if (lpvData != NULL)

TlsSetValue(dwTlsIndex, lpvData);

}

+$#A Dynamic-Link Library Functions

Following are the functions used in dynamic linking.

DisableThreadLibraryCalls
DllEntryPoint
FreeLibrary
FreeLibraryAndExitThread
GetModuleFileName
GetModuleHandle
GetProcAddress
LoadLibrary
LoadLibraryEx
LoadModule

Obsolete or Deleted Functions

FreeModule
FreeProcInstance
GetCodeHandle
GetInstanceData
GetModuleUsage
MakeProcInstance

Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle


Edit Controls.rtf

+$#AK Edit Controls

Microsoft® Windows® provides dialog boxes and controls to support communication between the application and the user. An edit control is a rectangular control window typically used in a dialog box to permit the user to enter and edit text from the keyboard.

+$#A About Edit Controls

An edit control is selected and receives the input focus when a user clicks the mouse inside it or presses the TAB key. After it is selected, the edit control displays its text (if any) and a flashing caret that indicates the insertion point. The user can then enter text, move the insertion point, or select text to be moved or deleted by using the keyboard or the mouse. An edit control can send notification messages to its parent window in the form of WM_COMMAND messages. For more information about messages from an edit control, see Edit Control Notification Messages. A parent window can send messages to an edit control in a dialog box by calling the SendDlgItemMessage function. Each of the messages sent to edit controls are discussed in this overview.

Windows provides both single line edit controls (sometime called SLEs) and multiline edit controls (sometimes called MLEs). Edit controls belong to the EDIT window class.

A combo box is a control that combines much of the functionality of an edit control and a list box. In a combo box, the edit control displays the current selection and the list box presents options a user can select. For more information about combo boxes, see Combo Boxes.

Many developers use the dialog boxes provided in the common dialog box library (COMDLG32.DLL) to perform tasks that otherwise might require customized edit controls. For information about common dialog boxes, see Common Dialog Box Library.

+$#A Edit Control Styles

Windows provides several edit control styles. An individual edit control can have several styles at the same time. Most developers use standalone tools to develop dialog boxes and so may not need to specify edit control styles explicitly. If an application creates an edit control using the CreateWindow or CreateWindowEx function, however, it must specify these edit control styles.

Every edit control specifies a combination of style values that define the appearance and features of the edit control. The style values can establish the appearance of a single-line or multiline edit control, align the text in the control, and determine how, and even if, text appears in the edit control. The number and type of styles the application uses depend on the type and purpose of the edit control.

There are two line styles for edit controls. The default is a single-line edit control that doesn't require an associated style. An application can create a multiline edit control by using the ES_MULTILINE style.

There are three styles that cause Windows to align the text in an edit control. The ES_LEFT, ES_CENTER, and ES_RIGHT styles determine whether text is aligned on the left, center, or right, respectively. These styles apply only to multiline edit controls.

An application can use a style to determine how Windows displays text that a user enters into an edit control. The ES_LOWERCASE style converts the text into lowercase characters; the ES_UPPERCASE style converts the text into uppercase characters. Some applications may need to convert the text in a Windows string (such as a filename) into a specific character set. The ES_OEMCONVERT style ensures the proper conversion of characters in these instances. For more information about character sets, see Consoles and Character-Mode Support.

When the amount of text to be displayed exceeds the size of the edit control, an application can use two styles to scroll the text through the edit control. The ES_AUTOHSCROLL style automatically scrolls text horizontally in single-line and multiline edit controls. When the application has a multiline edit control, it can also use the ES_AUTOVSCROLL style to automatically scroll text vertically, if necessary.

Other available styles define different aspects of an edit control. The ES_NUMBER style (available in version 4.0 or later) restricts input to the edit control to digits only. The ES_NOHIDESEL style specifies that the selected text is not hidden when the edit control loses the keyboard focus. The ES_READONLY style makes the edit control read-only. The ES_PASSWORD style displays all characters in the edit control as asterisks. (An application can define a different character to display by using the EM_SETPASSWORDCHAR message, as described later in this topic.) In multiline edit controls, an application can specify the ES_WANTRETURN style to request that Windows insert a carriage return when the user presses the ENTER key in the edit control.

By default, an edit control has no border. To give it one, an application can use the WS_BORDER window style.

+$#A The Text Buffer

Windows stores edit control text in a buffer and copies it to the control, as necessary. This section discusses the tools Windows provides to allocate and initialize the buffer and to change its characteristics.

+$#A Allocating a Text Buffer

When Windows creates an edit control, it automatically creates a text buffer, sets its initial size, and increases the size as necessary. The size can be up to a predefined limit of approximately 32 kilobytes for single-line edit controls. Because this limit can change, it is called a soft limit. An application can set a hard limit to the buffer size by sending an EM_SETLIMITTEXT message to the edit control. If the buffer exceeds either limit, Windows sends the application an EN_ERRSPACE message. An application can retrieve the current text limit by sending an EM_GETLIMITTEXT message.

Windows typically creates an edit control buffer in a dialog box, using memory outside the application's data segment. An application can suppress this default allocation behavior and create the buffer from its local heap by using the DS_LOCALEDIT style when creating the edit control. An application that uses the DS_LOCALEDIT style is responsible for all buffer allocations. To make the initial allocation, an application can call the LocalAlloc function and pass the returned buffer handle to the edit control by sending it an EM_SETHANDLE message. To make subsequent allocations (in response to an EN_ERRSPACE message, for example), an application should save the current buffer content (if necessary) and obtain a new buffer as follows:

1. Retrieve the handle of the memory currently allocated for the text in a multiline edit control by sending the control an EM_GETHANDLE message.

2. Free the buffer by calling the LocalFree function.

3. Obtain a new buffer (and buffer handle) by calling LocalAlloc.

4. Give the buffer handle to Windows by sending the control an EM_SETHANDLE message.

The EM_SETHANDLE and EM_GETHANDLE messages apply only to multiline edit controls.

An application that uses the default allocation behavior (that is, does not use the DS_LOCALEDIT style) must not send EM_SETHANDLE and EM_GETHANDLE messages to the edit control.

Sending an EM_SETHANDLE message has several side effects: it clears the undo flag (making the EM_CANUNDO message return zero), it clears the modify flag (making the EM_GETMODIFY message return zero), and it redraws the edit control window.

+$#A Initializing a Text Buffer

An application can initialize or reinitialize an edit control's text buffer by calling the SetDlgItemText function. An application can retrieve the content of a text buffer by calling the GetDlgItemText function.

+$#A Making a Text Buffer Read-Only

For each edit control, Windows maintains a read-only flag that indicates whether the control's text is read-write (the default) or read-only. An application can set the read-write or read-only flag for the text by sending the control an EM_SETREADONLY message. To determine whether an edit control is read-only, an application can call the GetWindowLong function using the GWL_STYLE constant. The EM_SETREADONLY message applies to both single-line and multiline edit controls.

+$#A Changing the Formatting Rectangle

The visibility of an edit control's text is governed by the dimensions of its window rectangle and its formatting rectangle. The window rectangle is the client area of the window containing the edit control. The formatting rectangle is a construct maintained by Windows for formatting the text displayed in the window rectangle. When an edit control is first displayed, the two rectangles are identical on the screen. An application can make the formatting rectangle larger than the window rectangle (thereby limiting the visibility of the edit control's text) or smaller than the window rectangle (thereby creating extra white space around the text).

An application can set the coordinates of an edit control's formatting rectangle by sending it an EM_SETRECT message. The EM_SETRECT message also automatically redraws the edit control's text. To establish the coordinates of the formatting rectangle without redrawing the control's text, an application can send the control an EM_SETRECTNP message. To retrieve the coordinates of the formatting rectangle, an application can send the control an EM_GETRECT message. These messages apply to multiline edit controls only.

+$#A Text Operations

Windows automatically processes all user-initiated text operations and notifies the application when the operations are completed. This section discusses user-initiated text operations and the application's response.

+$#A Character Sets

Windows (except version 4.0) supports two character sets: Unicode™, a two-byte character set, and ANSI, a one-byte character set that is a strict subset of Unicode. In ANSI, n characters equals n bytes, but in Unicode, n characters equals 2n bytes. For more information about Unicode and character sets, see Unicode and Character Sets.

+$#A Fonts

An application can change the font that an edit control uses by sending the WM_SETFONT message. Most applications do this while processing the WM_INITDIALOG message. Changing the font does not change the size of the edit control; applications that send the WM_SETFONT message may have to retrieve the font metrics for the text and recalculate the size of the edit control. For more information about fonts and font metrics, see Fonts and Text.

+$#A Selecting an Edit Control

The user can select an edit control by clicking it with the mouse or by tabbing to it. The "tabbing" method is part of a predefined keyboard interface that Windows provides. For a complete description of this interface, see Dialog Boxes.) When the user selects an edit control, Windows gives the control the keyboard focus and highlights its text by using reverse video.

+$#A Selecting Text

After selecting an edit control, the user can select text in the control by using the mouse or the keyboard. An application can retrieve the starting and ending character positions of the current selection in an edit control by sending the control an EM_GETSEL message. The return value for the ending position is one greater than the last character in the selection (that is, the position of the first character following the last selected character).

An application can also select text in an edit control by sending the control an EM_SETSEL message with the starting and ending character indices for the selection. For example, the application can use EM_SETSEL with EM_REPLACESEL to delete text from an edit control.

These three messages apply to both single-line and multiline edit controls.

+$#A Replacing Text

An application can replace selected text in an edit control by sending the control an EM_REPLACESEL message with a pointer to the replacement text. If there is no current selection, EM_REPLACESEL inserts the replacement text at the insertion point. The application may receive an EN_ERRSPACE notification message if the replacement text exceeds the available memory. This message applies to both single-line and multiline edit controls.

An application can use EM_REPLACESEL to replace part of an edit control's text or the SetDlgItemText function to replace all of it.

+$#A Cut, Copy, Paste, and Clear Operations

Windows provides four messages for moving text between an edit control and the clipboard. The WM_COPY message copies the current selection (if any) from an edit control to the clipboard without deleting it from the edit control. The WM_CUT message deletes the current selection, if any, in the edit control and copies the deleted text to the clipboard. The WM_CLEAR message deletes the current selection (if any) from an edit control, but does not copy it to the clipboard (unless the user pressed the SHIFT key). The WM_PASTE message copies text from the clipboard into an edit control at the insertion point. These four messages apply to both single-line and multiline edit controls.

With version 4.0 or later, an edit control includes a built in context menu that makes it easy for the user to move text between the edit control and the clipboard. The context menu appears when the user clicks the control using the right mouse button. The menu items in the context menu include Undo, Cut, Copy, Paste, Delete, and Select All.

+$#A Modifying Text

The user can select, delete, or move text in an edit control. Windows maintains an internal flag for each edit control indicating whether the content of the control has been modified. Windows clears this flag when it creates the control and sets the flag whenever the text in the control is modified. An application can retrieve the modification flag by sending the control an EM_GETMODIFY message and set or clear the modification flag by sending the control an EM_SETMODIFY message. These messages apply to both single-line and multiline edit controls.

+$#A Limiting User-Entered Text

The default limit to the amount of text a user can enter in an edit control is 32 kilobytes. An application can change the amount of text the user can enter by sending the control an EM_SETLIMITTEXT message. This message sets a hard limit to the number of bytes the user can enter into an edit control, but affects neither text that is already in the control when the message was sent nor text copied to the control by the SetDlgItemText function or the WM_SETTEXT message. For example, suppose that the application uses the SetDlgItemText function to place 500 bytes in an edit control, and the user also enters 500 bytes (1000 bytes total). If the application then sends an EM_SETLIMITTEXT message limiting user-entered text to 300 bytes, the 1000 bytes already in the edit control remain there, and the user cannot add any more text. On the other hand, if the application sends an EM_SETLIMITTEXT message limiting user-entered text to 1300 bytes, the 1000 bytes remain, but the user can add 300 more bytes.

When the user reaches the character limit of an edit control, Windows sends the application a WM_COMMAND message containing an EN_MAXTEXT notification message. This notification message does not mean that memory has been exhausted, but that the limit for user-entered text has been reached; the user cannot enter any more text. To change this limit, an application must send the control a new EM_SETLIMITTEXT message with a higher limit.

As an example of the use of EM_SETLIMITTEXT and EN_MAXTEXT, suppose that the application must limit the user to no more than four characters in an edit control. The application would use EM_SETLIMITTEXT to specify a four-character limit. If the user tried to enter a fifth character, Windows would send an EN_MAXTEXT notification message to the application.

+$#A Character and Line Operations

The Microsoft® Win32® application programming interface (API) provides several messages that return information about the characters and lines in an edit control. Most of the messages return an index, usually a zero-based number, to refer to a character or line. Given this, a single-line edit control containing n characters, the line index is zero and the characters are indexed from zero to n − 1. In a multiline edit control containing m lines and n characters, the lines are indexed from zero to m − 1, and the characters are indexed from zero to n − 1. Note that character indexing ignores linebreaks.

An application can determine the number of characters in an edit control by sending the WM_GETTEXTLENGTH message to the edit control. This message returns the length, in characters (not including the terminating null character), of the text in a single-line or multiline edit control. The EM_LINELENGTH message returns the length, in characters, of a line specified by the character index of a character in the line. The returned length does not include any selected characters. An application can use these messages in a single-line or multiline edit control.

The EM_GETFIRSTVISIBLELINE message returns the zero-based index of the uppermost visible line in a multiline edit control, or the zero-based index of the first visible character in a single-line edit control. An application can copy a line from an edit control to a buffer by sending the EM_GETLINE message to the edit control. The line is specified by its line index and the first word of the receiving buffer contains the maximum number of bytes to be copied to the buffer. The return value is the number of bytes copied. This message can also be used in a single-line or multiline edit control.

There are unique messages available to return the information about a line in a multiline edit control. The EM_GETLINECOUNT message returns the number of lines in an edit control. An application can determine the index of a character in a specific line by using the EM_LINEFROMCHAR and EM_LINEINDEX messages. The EM_LINEFROMCHAR message returns the index of the line containing a specified character index. This message is the reverse of the EM_LINEINDEX message, which returns the index of the first character in a specified line.

+$#A Scrolling Text in an Edit Control

An application can implement scrolling in an edit control by using the automatic scrolling styles discussed in Edit Control Styles or by explicitly adding scroll bars to the control. To add a horizontal scroll bar, use the style WS_HSCROLL; to add a vertical scroll bar, use the style WS_VSCROLL. An edit control with scroll bars processes its own scroll bar messages. For detailed information about adding scroll bars to edit controls, see Scroll Bars.

Windows provides three messages that an application can send to an edit control with scroll bars. The EM_LINESCROLL message can scroll a multiline edit control both vertically and horizontally. The lParam parameter specifies the number of lines to scroll vertically starting from the current line and the wParam parameter specifies the number of characters to scroll horizontally, starting from the current character. The edit control doesn't acknowledge horizontal scrolling messages if it has the ES_CENTER or ES_RIGHT style. This message applies to multiline edit controls only.

The EM_SCROLL message scrolls a multiline edit control vertically, which is the same effect as sending a WM_VSCROLL message. The wParam parameter specifies the scrolling action. The EM_SCROLL message applies to multiline edit controls only.

The EM_SCROLLCARET message scrolls the caret into view in an edit control. This was done in previous versions of Windows by specifying wParam = FALSE in an EM_SETSEL message. A Win32-based application should use the EM_SCROLLCARET message for the task.

+$#A Tab Stops and Margins

An application can set tab stops in a multiline edit control by using the EM_SETTABSTOPS message. (The default for a tab stop is eight characters.) When an application adds text to the edit control, tab characters in the text automatically generate space up to the next tab stop. The EM_SETTABSTOPS message does not automatically cause Windows to redraw the text. To do that, an application can call the InvalidateRect function. The EM_SETTABSTOPS message applies to multiline edit controls only.

An application can set width of the left and right margins for an edit control by using the EM_SETMARGINS message. After sending this message, Windows redraws the edit control to reflect the new margin settings. An application can retrieve the width of the left or right margin by sending the EM_GETMARGINS message. By default, the edit control margins are set to be just wide enough to accommodate the largest character horizontal overhang (negative ABC widths) for the font currently in use in the edit control.

+$#A Password Characters

An application can use a password character in an edit control to conceal user input. When a password character is set, it is displayed in place of each character the user types. When a password character is removed, the control displays the characters the user types. If the application creates an edit control using the style ES_PASSWORD, the default password character is an asterisk (*). An application can use the EM_SETPASSWORDCHAR message to remove or define a different password character and the EM_GETPASSWORDCHAR message to retrieve the current password character. These messages apply to single-line edit controls only.

+$#A Using Integers

Windows provides two integer-conversion functions for edit controls designed to contain numbers only. The SetDlgItemInt function creates the string representation of a specified integer (signed or unsigned) and sends the string to an edit control. SetDlgItemInt returns no value. The GetDlgItemInt function creates an integer (signed or unsigned) from its string representation in an edit control. GetDlgItemInt returns the integer (or an error value).

+$#A Undoing Text Operations

Every edit control maintains an undo flag that indicates whether an application can reverse (undo) the most recent operation on the edit control (to undo a text deletion, for example). The edit control sets the undo flag to indicate that the operation can be undone and resets it to indicate that the operation cannot be undone. An application can determine the setting of the undo flag by sending the control an EM_CANUNDO message.

An application can undo the most recent operation by sending the control an EM_UNDO message. An operation can be undone provided no other edit control operation occurs first. For example, the user can delete text, replace the text (undo the deletion), and then delete the text again (undo the replacement). The EM_UNDO message applies to both single-line and multiline edit controls and always works for single-line edit controls.

An application can reset an edit control's undo flag by sending the control an EM_EMPTYUNDOBUFFER message. Windows automatically resets the undo flag whenever an edit control receives an EM_SETHANDLE or WM_SETTEXT message. The SetDlgItemText function sends a WM_SETTEXT message.

+$#A Wordwrap Functions

An application can use wordwrap functions with multiline edit controls to locate the word or word fragment that should be wrapped to the next line. Using the default wordwrap function provided by Windows, lines always end at the spaces between words. An application can specify its own wordwrap function by supplying a EditWordBreakProc wordwrap function and sending an edit control an EM_SETWORDBREAKPROC message. An application can retrieve the address of the current wordwrap function by sending the control an EM_GETWORDBREAKPROC message.

An application may direct a multiline edit control to add or remove a soft linebreak character (two carriage returns and a linefeed) automatically at the end of wrapped text lines. An application can turn this feature on or off by sending the edit control an EM_FMTLINES message. This message applies only to multiline edit controls and does not affect a line that ends with a hard linebreak (one carriage return and a linefeed entered by the user).

+$#A Retrieving Points and Characters

An application can determine which character is closest to the specified point in an edit control by sending the EM_CHARFROMPOS message. The message returns the character index and line index of the character nearest the point. Similarly, an application can determine the client coordinates of the specified character in an edit control by sending the EM_POSFROMCHAR message. The application specifies the index of a character and the message returns the x and y coordinates of the upper left corner of the character.

+$#A Edit Control Notification Messages

The user makes editing requests by using the keyboard and mouse. Windows sends each request to the edit control's parent window in the form of a WM_COMMAND message. The message includes the edit control identifier in the low-order word of the wParam parameter, the handle of the edit control in the lParam parameter, and an edit control notification message corresponding to the user's action in the high-order word of the wParam parameter.

Portability issue In Microsoft® Windows NT™, the placement of the notification code in a notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x applications that process notification messages must modify this code when porting to Windows NT.

An application should examine each notification message and respond appropriately. The following table lists each edit control notification message and the action that generates it.

Notification message | User action
EN_CHANGE | The user has modified text in an edit control. Windows updates the display before sending this message (unlike EN_UPDATE).
EN_ERRSPACE | The edit control cannot allocate enough memory to meet a specific request.
EN_HSCROLL | The user has clicked the edit control's horizontal scroll bar. Windows sends this message before updating the screen.
EN_KILLFOCUS | The user has selected another control.
EN_MAXTEXT | While inserting text, the user has exceeded the specified number of characters for the edit control. Insertion has been truncated. This message is also sent either when an edit control does not have the ES_AUTOHSCROLL style and the number of characters to be inserted exceeds the width of the edit control or when an edit control does not have the ES_AUTOVSCROLL style and the total number of lines to be inserted exceeds the height of the edit control.
EN_SETFOCUS | The user has selected this edit control.
EN_UPDATE | The user has altered the text in the edit control and Windows is about to display the new text. Windows sends this message after formatting the text, but before displaying it, so that the application can resize the edit control window.
EN_VSCROLL | The user has clicked the edit control's vertical scroll bar. Windows sends this message before updating the screen.

In addition, the system sends a WM_CTLCOLOREDIT message to an edit control's parent window before the edit control is drawn. This message contains a handle of the edit control's display context (DC) and a handle of the child window. The parent window can use these handles to change the edit control's text and background colors.

+$#A Edit Control Default Message Processing

The window procedure for the predefined edit control window class carries out default processing for all messages that the edit control procedure does not process. When the edit control procedure returns FALSE for any message, the predefined window procedure checks the messages and carries out the following default actions.

Message | Default action
EM_CANUNDO | Returns TRUE if the edit control operation can be undone.
EM_CHARFROMPOS | Returns the character index and line index of the character nearest the specified point.
EM_EMPTYUNDOBUFFER | Empties the undo buffer and sets the undo flag retrieved by the EM_CANUNDO message to FALSE. Windows automatically clears the undo flag whenever the edit control receives a WM_SETTEXT or EM_SETHANDLE message.
EM_FMTLINES | Adds or removes soft linebreak characters (two carriage returns and a linefeed) to the ends of wrapped lines in a multiline edit control. It is not processed by single-line edit controls.
EM_GETFIRSTVISIBLELINE | Returns the zero-based index of the first visible character in a single-line edit control or the zero-based index of the uppermost visible line in a multiline edit control.
EM_GETHANDLE | Returns a handle identifying the buffer containing the multiline edit control's text. It is not processed by single-line edit controls.
EM_GETLIMITTEXT | Returns the current text limit, in characters.
EM_GETLINE | Copies characters in a single-line edit control to a buffer and returns the number of characters copied. In a multiline edit control, retrieves a line of text from the control and returns the number of characters copied.
EM_GETLINECOUNT | Returns the number of lines in the edit control.
EM_GETMARGINS | Returns the widths of the left and right margins.
EM_GETMODIFY | Returns a flag indicating whether the content of an edit control has been modified.
EM_GETPASSWORDCHAR | Returns the character that edit controls use in conjunction with the ES_PASSWORD style.
EM_GETRECT | Returns the coordinates of the formatting rectangle in an edit control.
EM_GETSEL | Returns the starting and ending character positions of the current selection in the edit control.
EM_GETTHUMB | Returns the position of the scroll box.
EM_GETWORDBREAKPROC | Returns the address of the current wordwrap function in an edit control.
EM_LINEFROMCHAR | Returns the zero-based number of the line in a multiline edit control that contains a specified character index This message is the reverse of the EM_LINEINDEX message. It is not processed by single-line edit controls.
EM_LINEINDEX | Returns the character of a line in a multiline edit control. This message is the reverse of the EM_LINEFROMCHAR message. It is not processed by single-line edit controls.
EM_LINELENGTH | Returns the length, in characters, of a single-line edit control. In a multiline edit control, returns the length, in characters, of a specified line.
EM_LINESCROLL | Scrolls the text vertically in a single-line edit control or horizontally in a multiline edit control (when the control has the ES_LEFT style). The lParam parameter specifies the number of lines to scroll vertically, starting from the current line. The wParam parameter specifies the number of characters to scroll horizontally, starting from the current character.
EM_POSFROMCHAR | Returns the client coordinates of the specified character.
EM_REPLACESEL | Replaces the current selection with the text in an application-supplied buffer, sends the parent window EN_UPDATE and EN_CHANGE messages, and updates the undo buffer.
EM_SCROLL | Scrolls the text vertically in a multiline edit control. This message is equivalent to sending a WM_VSCROLL message to the edit control. It is not processed by single-line edit controls.
EM_SCROLLCARET | Scrolls the caret into view in an edit control. In earlier versions of Windows this was done via EM_SETSEL using particular parameters. A Win32-based application should use EM_SCROLLCARET.
EM_SETFONT | Unsupported.
EM_SETHANDLE | Sets a handle to the memory used as a text buffer, empties the undo buffer, resets the scroll positions to zero, and redraws the window.
EM_SETLIMITTEXT | Sets the maximum number of characters the user may enter in the edit control. For single-line edit controls, this value is either 0x7FFFFFFE (0x7FFE for version 4.0) or the value of the wParam parameter, whichever is smaller. For multiline edit controls, this value is either 0xFFFFFFFF (0xFFFF for version 4.0) or the value of the wParam parameter, whichever is smaller.
EM_SETMARGINS | Sets the widths of the left and right margins, and redraws the edit control to reflect the new margins.
EM_SETMODIFY | Sets or clears the modification flag to indicate whether the edit control has been modified.
EM_SETPASSWORDCHAR | Defines the character that edit controls use in conjunction with the ES_PASSWORD style.
EM_SETREADONLY | Sets or removes the read-only style (ES_READONLY) in an edit control.
EM_SETRECT | Sets the formatting rectangle for the multiline edit control and redraws the window. It is not processed by single-line edit controls.
EM_SETRECTNP | Sets the formatting rectangle for the multiline edit control but does not redraw the window. It is not processed by single-line edit controls.
EM_SETSEL | Selects a range of characters in the edit control by setting the starting and ending positions to be selected.
EM_SETTABSTOPS | Sets tab-stop positions in the multiline edit control. It is not processed by single-line edit controls.
EM_SETWORDBREAKPROC | Replaces the default wordwrap function with an application-defined wordwrap function.
EM_UNDO | Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text. If necessary, sends the EN_UPDATE and EN_CHANGE notification messages to the parent window.
WM_CHAR | Writes a character to the single-line edit control and sends the EN_UPDATE and EN_CHANGE notification messages to the parent window. Writes a character to the multiline edit control. Handles the accelerator keys for standard functions, such as CTRL+C for copying and CTRL+V for pasting. In multiline edit controls, also processes TAB, and CTRL+TAB keystrokes to move among the controls in a dialog box and to insert tabs into multiline edit controls. Uses the MessageBeep function for illegal characters.
WM_CLEAR | Clears the current selection, if any, in an edit control. If there is no current selection, deletes the character to the right of the caret. If the user presses the SHIFT key, this cuts the selection to the clipboard, or deletes the character to the left of the caret when there is no selection. If the user presses the CTRL key, this deletes the selection, or deletes to the end of the line when there is no selection.
WM_COPY | Copies text to the clipboard unless the style is ES_PASSWORD, in which case the message returns zero.
WM_CREATE | Creates the edit control and notifies the parent window with TRUE for success or −1 for failure.
WM_CUT | Cuts the selection to the clipboard, or deletes the character to the left of the cursor if there is no selection.
WM_ENABLE | Causes the rectangle to be redrawn in gray for single-line edit controls. Returns the enabled state for single-line and multiline edit controls.
WM_ERASEBKGND | Fills the multiline edit control window with the current color of the edit control.
WM_GETDLGCODE | Returns the following values: DLGC_WANTCHARS, DLGC_HASSETSEL, and DLGC_WANTARROWS. In multiline edit controls, it also returns DLGC_WANTALLKEYS. If the user presses ALT+BACKSPACE, it also returns DLGC_WANTMESSAGE.
WM_GETFONT | Returns the handle of the font being used by the control, or NULL if the control uses the system font.
WM_GETTEXT | Copies the specified number of characters to a buffer and returns the number of characters copied.
WM_GETTEXTLENGTH | Returns the length, in characters, of the text in an edit control. The length does not include the null- terminating character.
WM_HSCROLL | Scrolls the text in a multiline edit control horizontally and handles scroll box movement.
WM_KEYDOWN | Performs standard processing of the virtual-key codes.
WM_KILLFOCUS | Removes the keyboard focus of an edit control window, destroys the caret, hides the current selection, and notifies the parent window that the edit control has lost the focus.
WM_LBUTTONDBLCLK | Clears the current selection and selects the word under the cursor. If the SHIFT key is down, extends the selection to the word under the cursor.
WM_LBUTTONDOWN | Changes the current insertion point. If the SHIFT key is down, it extends the selection to the position of the cursor. In multiline edit controls, also sets the timer to automatically scroll when the user holds down the mouse button outside the multiline edit control window.
WM_LBUTTONUP | Releases the mouse capture and sets the text insertion point in the single-line edit control. In a multiline edit control, it also kills the timer set in the WM_LBUTTONDOWN message.
WM_MOUSEMOVE | Changes the current selection in the single-line edit control, if the mouse button is down. In a multiline edit controls, also sets the timer to automatically scroll if the user holds down the mouse button outside the multiline edit control window.
WM_NCCREATE | Points to the CREATESTRUCT structure for the window. This message is sent to the WM_CREATE message when a window is first created.
WM_NCDESTROY | Frees all memory associated with the edit control window, including the text buffer, undo buffer, tab-stop buffer, and highlight brush.
WM_PAINT | Erases the background, fills the window with the current color of the edit control window, draws the border (if any), sets the font and draws any text, and shows the text-insertion caret.
WM_PASTE | Pastes text from the clipboard into the edit control window at the caret position.
WM_SETFOCUS | Sets the keyboard focus of an edit control window (shows the current selection, if it was hidden, and creates the caret).
WM_SETFONT | Sets the font and optionally redraws the edit control.
WM_SETTEXT | Copies text to the single-line edit control, notifies the parent window when there is insufficient memory, empties the undo buffer, and sends the EN_UPDATE and EN_CHANGE notification messages to the parent window. In multiline edit controls, also rewraps the lines (if necessary) and sets the scroll positions.
WM_SIZE | Changes the size of the edit control window and ensures that the minimum size accommodates the height and width of a character.
WM_SYSCHAR | Returns TRUE if the user presses ALT+BACKSPACE; otherwise, it takes no action.
WM_SYSKEYDOWN | Undoes the last action if the user presses ALT+BACKSPACE; otherwise, it takes no action.
WM_TIMER | Scrolls the text in the edit control window if the user holds down the mouse button outside the multiline edit control window.
WM_UNDO | Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text. If necessary, sends the EN_UPDATE and EN_CHANGE notification messages to the parent window.
WM_VSCROLL | Scrolls a multiline edit control vertically and handles scroll box movement. It is not processed by single-line edit controls.

The predefined edit control window procedure passes all other messages to the DefWindowProc function for default processing.

+$#A Using Edit Controls

Edit controls are typically used in dialog boxes, but you can use them in the client area of a standard window as well. Single-line edit controls are useful for retrieving a single string from the user. Multiline edit controls make it easy for your application to implement most of the features of a simple word processor.

• Simple word processing with an edit control

• Using single-line edit controls

+$#A Simple Word Processing with an Edit Control

The following example implements much of the functionality of a simple word processor by filling the client area of a window with a multiline edit control. The system automatically performs wordwrap operations for this edit control and also handles the processing for the vertical scroll bar (created by specifying ES_AUTOVSCROLL in the call to the CreateWindow function). The WM_COMMAND message processes menu items; they allow the user to undo the previous action, cut or copy selections to the clipboard, paste text from the clipboard, and delete the current selection.

LONG APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

static HWND hwndEdit;

CHAR lpszTrouble[] = "When in the Course of human Events "

"it becomes necessary for one People "

"to dissolve the Political Bands which "

"have connected them with another, and "

"to assume among the Powers of the "

"Earth, the separate and equal Station "

"to which the Laws of Nature and of "

"Nature's God entitle them, a decent "

"Respect to the Opinions of Mankind "

"requires that they should declare the "

"causes which impel them to the "

"Separation. ";

switch (message) {

case WM_CREATE:

hwndEdit = CreateWindow(

"EDIT", /* predefined class */

NULL, /* no window title */

WS_CHILD | WS_VISIBLE | WS_VSCROLL |

ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,

0, 0, 0, 0, /* set size in WM_SIZE message */

hwnd, /* parent window */

(HMENU) ID_EDITCHILD, /* edit control ID */

(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),

NULL); /* pointer not needed */

/* Add text to the window. */

SendMessage(hwndEdit, WM_SETTEXT, 0,

(LPARAM) lpszTrouble);

return 0;

case WM_COMMAND:

switch (wParam) {

case IDM_EDUNDO:

/*

* Send WM_UNDO only if there is something

* to be undone.

*/

if (SendMessage(hwndEdit, EM_CANUNDO, 0, 0))

SendMessage(hwndEdit, WM_UNDO, 0, 0);

else

MessageBox(hwndEdit,

"Nothing to undo.",

"Undo notification", MB_OK);

break;

case IDM_EDCUT:

SendMessage(hwndEdit, WM_CUT, 0, 0);

break;

case IDM_EDCOPY:

SendMessage(hwndEdit, WM_COPY, 0, 0);

break;

case IDM_EDPASTE:

SendMessage(hwndEdit, WM_PASTE, 0, 0);

break;

case IDM_EDDEL:

SendMessage(hwndEdit, WM_CLEAR, 0, 0);

break;

case IDM_PASSWORD:

DialogBox(hinst, /* current instance */

"PassBox", /* resource to use */

hwnd, /* parent handle */

(DLGPROC) PassProc);

break;

case IDM_WRAP:

SendMessage(hwndEdit,

EM_SETWORDBREAKPROC,

(WPARAM) 0,

(LPARAM) (EDITWORDBREAKPROC) WordBreakProc);

SendMessage(hwndEdit,

EM_FMTLINES,

(WPARAM) TRUE,

(LPARAM) 0);

SendMessage(hwndEdit,

EM_SETSEL,

0, -1); /* select all text */

SendMessage(hwndEdit, WM_CUT, 0, 0);

SendMessage(hwndEdit, WM_PASTE, 0, 0);

break;

case IDM_ABOUT:

DialogBox(hinst, /* current instance */

"AboutBox", /* resource to use */

hwnd, /* parent handle */

(DLGPROC) About);

break;

default:

return DefWindowProc(hwnd, message, wParam, lParam);

}

break;

case WM_SETFOCUS:

SetFocus(hwndEdit);

return 0;

case WM_SIZE:

/*

* Make the edit control the size of the window's

* client area.

*/

MoveWindow(hwndEdit,

0, 0, /* starting x- and y-coordinates */

LOWORD(lParam), /* width of client area */

HIWORD(lParam), /* height of client area */

TRUE); /* repaint window */

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

default:

return DefWindowProc(hwnd, message, wParam, lParam);

}

return NULL;

}

+$#A Using Single-Line Edit Controls

The example in this section demonstrates how to use a window procedure to produce a dialog box that prompts the user to enter a password.

The single-line edit control in the Password dialog box has the ES_PASSWORD style. By default, edit controls with this style display an asterisk for each character the user types. This example, however, uses the EM_SETPASSWORDCHAR message to change the default character from an asterisk to a plus sign (+).

This window procedure changes the default push button from Cancel to OK as soon as the user enters text in the edit control. If the user presses the OK button, the window procedure uses the EM_LINELENGTH and EM_GETLINE messages to retrieve the text.

LRESULT CALLBACK PassProc(hDlg, message, wParam, lParam)

HWND hDlg; /* window handle of the dialog box */

UINT message; /* type of message */

UINT wParam; /* message-specific information */

LONG lParam;

{

CHAR lpszPassword[16];

WORD cchPassword;

switch (message) {

case WM_INITDIALOG:

/* Set password character to a plus sign (+) */

SendDlgItemMessage(hDlg,

IDE_PASSWORDEDIT,

EM_SETPASSWORDCHAR,

(WPARAM) '+',

(LPARAM) 0);

/* Set the default push button to "Cancel." */

SendMessage(hDlg,

DM_SETDEFID,

(WPARAM) IDCANCEL,

(LPARAM) 0);

return TRUE;

case WM_COMMAND:

/*

* Set the default push button to "OK" when the user

* enters text.

*/

if(HIWORD (wParam) == EN_CHANGE &&

LOWORD(wParam) == IDE_PASSWORDEDIT)

SendMessage(hDlg,

DM_SETDEFID,

(WPARAM) IDOK,

(LPARAM) 0);

switch(wParam) {

case IDOK:

/* Get number of characters. */

cchPassword = (WORD) SendDlgItemMessage(hDlg,

IDE_PASSWORDEDIT,

EM_LINELENGTH,

(WPARAM) 0,

(LPARAM) 0);

if (cchPassword >= 16) {

MessageBox(hDlg,

"Too many characters.",

"Error",

MB_OK);

EndDialog(hDlg, TRUE);

return FALSE;

}

else if (cchPassword == 0) {

MessageBox(hDlg,

"No characters entered.",

"Error",

MB_OK);

EndDialog(hDlg, TRUE);

return FALSE;

}

/*

* Put the number of characters into first word

* of buffer.

*/

*((LPWORD)lpszPassword) = cchPassword;

/* Get the characters. */

SendDlgItemMessage(hDlg,

IDE_PASSWORDEDIT,

EM_GETLINE,

(WPARAM) 0, /* line 0 */

(LPARAM) lpszPassword);

/* Null-terminate the string. */

lpszPassword[cchPassword] = 0;

MessageBox(hDlg,

lpszPassword,

"Did it work?",

MB_OK);

/* Call a local password-parsing function. */

ParsePassword(lpszPassword);

EndDialog(hDlg, TRUE);

return TRUE;

case IDCANCEL:

EndDialog(hDlg, TRUE);

return TRUE;

}

return 0;

}

return FALSE;

UNREFERENCED_PARAMETER(lParam);

}

+$#AK Edit Control Reference

The following functions and messages are associated with edit controls.

+$#A Edit Control Functions

Following are the functions used with edit controls.

EditWordBreakProc

+$#A Edit Control Messages

Following are the messages used with edit controls.

EM_CANUNDO
EM_CHARFROMPOS
EM_EMPTYUNDOBUFFER
EM_FMTLINES
EM_GETFIRSTVISIBLELINE
EM_GETHANDLE
EM_GETLIMITTEXT
EM_GETLINE
EM_GETLINECOUNT
EM_GETMARGINS
EM_GETMODIFY
EM_GETPASSWORDCHAR
EM_GETRECT
EM_GETSEL
EM_GETWORDBREAKPROC
EM_LINEFROMCHAR
EM_LINEINDEX
EM_LINELENGTH
EM_LINESCROLL
EM_POSFROMCHAR
EM_REPLACESEL
EM_SCROLL
EM_SCROLLCARET
EM_SETHANDLE
EM_SETLIMITTEXT
EM_SETMARGINS
EM_SETMODIFY
EM_SETPASSWORDCHAR
EM_SETREADONLY
EM_SETRECT
EM_SETRECTNP
EM_SETSEL
EM_SETTABSTOPS
EM_SETWORDBREAKPROC
EM_UNDO
EN_CHANGE
EN_ERRSPACE
EN_HSCROLL
EN_KILLFOCUS
EN_MAXTEXT
EN_SETFOCUS
EN_UPDATE
EN_VSCROLL
WM_COMMAND
WM_COPY
WM_CTLCOLOREDIT
WM_CUT
WM_PASTE
WM_UNDO

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide CHM facilement


End User Defined Characters.rtf

+$#AK End User Defined Characters

End-user-defined characters (EUDC) are customized characters that users install for viewing and printing documents.

This overview discusses the TrueType fonts that support end-user-defined characters and describes the functions used to access and manage these characters in Win32-based applications.

+$#A About End User Defined Characters

End-user-defined characters give users a way to form names and other words using characters that are not available in standard screen and printer fonts.

End-user-defined characters are available only in Asian-language versions of the Microsoft Windows operating system.

+$#A Character Sets and Fonts

An end-user-defined character is always associated with a double-byte character set (DBCS) and a TrueType font. Applications identify the specified character by using the character's assigned DBCS character value, and the operating system uses this value to locate shape and style information in a corresponding TrueType font. The shape and style information specifies how the character is drawn on the screen or printed page.

The DBCS character values that can be assigned depend on the specified character set. Each set has at least one range of reserved values for use as end-user-defined characters. The system or applications explicitly define these ranges by setting appropriate values under the EUDCCodeRange key in the registry. In the registry, each character set is identified by a unique code-page number.

To create an end-user-defined character, the user chooses a character value that is within the specified range and adds the shape and style information to the TrueType font in the entry that corresponds to that character value. Users create the shape and style information using an EUDC editor or by purchasing end-user-defined font packages from font vendors. Any DBCS TrueType font can contain end-user-defined characters. The font is called a "separate" EUDC font if it contains only end-user-defined characters. The font is an "integrated" EUDC font if it contains standard characters as well as end-user-defined characters.

Separate EUDC fonts are said to be either "typeface-aware" or "typeface-unaware." A typeface-unaware font is designed to be a general purpose font that can be used with fonts of different typeface styles and of different implementations, such as GDI raster, WIFE, device, and TrueType fonts. A typeface-aware font is designed for use with a specific TrueType font.

The system default EUDC font is a typeface-unaware font that the system automatically associates with all DBCS fonts except those TrueType fonts that have explicitly associated typeface-aware fonts. Applications set the system default EUDC font by setting the value of the SystemDefaultEUDCFont name under the EUDC key in the registry. Similarly, applications can associate typeface-aware fonts with corresponding TrueType fonts by specifying a typeface name and associated font file under the EUDC key. Separate EUDC fonts cannot be associated with integrated EUDC fonts.

The system "hides" the system default EUDC and typeface-aware fonts. This means applications cannot enumerate or otherwise examine these fonts using GDI functions. Applications, such as EUDC editors and Control Panel, must use the registry entries to add, modify, and delete EUDC fonts.

End-user-defined characters can also be used in Unicode-enabled applications. The reserved ranges for each character set are mapped to corresponding values in the Unicode private use area (values 0xE000 and higher). There are mappings for both Japanese and Chinese character sets. For a list of these mappings, see Japanese EUDC Ranges and Chinese EUDC Ranges.

+$#A Writing, Mapping, and Sorting Characters

Applications write end-user-defined characters to the screen or printer the same way as writing other characters, by using output functions such as TextOut and ExtTextOut. These functions automatically retrieve character information from EUDC fonts if EUDC is enabled. When writing end-user-defined characters, the action of the text output function depends on the currently selected font. If the selected font is an integrated EUDC font, the function retrieves character information from that font. If the selected font is a DBCS TrueType font that has an associated typeface-aware EUDC font, the function retrieves information from the specified typeface-aware font. If the selected font does not have an associated typeface-aware font, the function retrieves information from the system default EUDC font. If the character is not in the system default EUDC font or there is no system default EUDC font, the function writes the default character defined by the selected font.

Applications can map end-user-defined characters to and from Unicode by using the MultiByteToWideChar and WideCharToMultiByte functions. The MultiByteToWideChar function maps an end-user-defined character to a character position in the Unicode private use area. The WideCharToMultiByte function maps a character in the private use area to its multibyte character counterpart, if such a mapping exists. The code page specified in this function must contain an EUDC code range for the mapping to occur. If it does not, the function returns the default character for any characters in the Unicode private use area.

Both MultiByteToWideChar and WideCharToMultiByte preserve the "round trip" mapping of end-user-defined characters from a code page to Unicode and back to the original code page. But these functions do not necessarily preserve mapping to other code pages. For example, if a character from code page 932 is mapped to Unicode, then mapped from Unicode to another code page with an EUDC range, it is likely that the original character will be mapped to a different end-user-defined character in the destination code page. Similarly, mapping a Unicode string to a code page that has an EUDC range may have unintended results. If the Unicode string contains a private-use code point, that code point may be mapped to an end-user-defined character whether or not the code point actually represents such a character.

Applications can compare strings that contain end-user-defined characters by using the CompareStringA function. The function maps the characters to the Unicode end user zone before comparing character values. Applications can create a sort key for the string by using the LCMapStringA function and the LCMAP_SORTKEY value. This function maps characters to Unicode first. All characters in the private use area are sorted after all other Unicode characters. Within the area, characters are sorted in numerical order. If an application attempts to retrieve CTYPE ifnormation for an end-user-defined character by using the GetStringTypeA function, the function returns NULL for each character.

+$#A End User Defined Character Reference

This section provides reference information on:

• Registry entries.

• End user defined character constants.

+$#A Registry Entries

The following registry information shows the appropriate entries for use with end user defined characters.

+$#A EUDCCodeRange

EUDCCodeRange
CodePage=FromTo[,FromTo]

The EUDCCodeRange registry entry contains entries that define end-user-defined characters (EUDC) code ranges for the various code pages (character sets).

EUDCCodeRange Key

The EUDCCodeRange key has the following location in the registry:

HKEY_LOCAL_MACHINE\ System\CurrentControlSet\Control\NLS\CodePage\EUDCCodeRange

The key contains one or more values specifying the range of character values within a codepage that are available for end-user-defined characters. Under this key, a value name is one of these code-page values:

Value | Meaning
932 | Japan
936 | Simplified Chinese
949 | Unified Hangeul Code (Hangeul TongHabHyung Code)
950 | Traditional Chinese
1361 | Johab

The value associated with each name is a string consisting of one or more pairs of 4-digit hexadecimal numbers, with the numbers in each pair separated by a hyphen (-) and each pair separated from any preceding pair by a comma (,). The following example illustrates this format:

HKEY_LOCAL_MACHINE\

System\CurrentControlSet\Control\NLS\CodePage\EUDCCodeRange

923=F040-F9FC

950=FA40-FEFE, 8E40-A0FE, 8140-8DFE, C6A1-C8FE

CodePage

A user-defined value name that identifies the code page. It must be the letters "CP" followed by 5 decimal digits specifying the code page.

FromTo

A string value consisting of a pair of 4-digit hexadecimal values separated by a hyphen (-). Multiple FromTo values may be specified, but each must be separated from the previous by a comma (,).

The following examples show possible EUDC ranges for code pages 923 and 950:

CP00923=F040-F9FC
CP00950=FA40-FEFE,8E40-A0FE,8140-8DFE,C6A1-C8FE

+$#A EUDC

EUDC
SystemDefaultEUDCFont=TrueTypeEUDCFontFileName
TrueTypeFontTypeface=TrueTypeEUDCFontFileName

EUDC Key

The EUDC registry key has the following location in the registry:

HKEY_CURRENT_USER\EUDC

The EUDC key contains one or more subkeys with each subkey containing values defining the fonts associated with end-user-defined characters for a given code page. Under the EUDC, a subkey name can be one of these code-page numbers:

Value | Meaning
932 | Japan
936 | Simplified Chinese
949 | Unified Hangeul Code (Hangeul TongHabHyung Code)
950 | Traditional Chinese
1361 | Johab

One value under the subkey has the name SystemDefaultEUDCFont. Other values, if any, have names that identify a typeface. The values associated with these names are strings specifying the name of TrueType font file containing the given font. The following example shows the EUDC key for code page 932:

HKEY_CURRENT_USER\EUDC\932

SystemDefaultEUDCFont=EUDC.TTF

MS Mincho=MINEUDC.TTF

MS Gothic=GTEUDC.TTF

SystemDefaultEUDCFont

Predefined value name used to set the system default font. The corresponding value must be a string that identifies a typeface-unaware EUDC font. There is no system default EUDC font unless this entry is explicitly specified.

TrueTypeFontTypeface

A user-defined value name that identifies the typeface name associated with a TrueType font. The corresponding value must be a string that identifies a typeface-aware EUDC font.

TrueTypeEUDCFontFileName

A string value consisting of the filename of a "separate" EUDC font file.

The following example sets the system default EUDC font to be EUDC.TTF and associates the typeface-aware fonts, MINEUDC.TTF and GOTEUDC.TTF, with the typeface names "MS Mincho" and "MS Gothic":

SystemDefaultEUDCFont=EUDC.TTF
MS Mincho=MINEUDC.TTF
MS Gothic=GOTEUDC.TTF

+$#A End User Defined Character Constants

The following constants are used with end user defined characters in the following Far Eastern languages.

+$#A Japanese EUDC Ranges

The following table shows the mapping between the Japanese Microsoft Standard Character Set (ShiftJIS) and Unicode.

Shift JIS | Unicode
F040 | U+E000
F041 | U+E001
F042 | U+E002
: | :
F9FA | U+E755
F9FB | U+E756
F9FC | U+E757

+$#A Chinese EUDC Ranges

The following table shows the mapping between the Chinese character sets (BIG-5 and GB 2312-80) and Unicode.

BIG-5 | Unicode
FA40 - FEFE | U+E000 - U+E310
8E40 - A0FE | U+E311 - U+EEB7
8140 - 8DFE | U+EEB8 - U+F6B0
C6A1 - C8FE | U+F6B1 - U+F8FF

GB 2312-80 | Unicode
F8A1 - FEFE | U+E000 - U+E29F
AAA1 - AFFE | U+E2A0 - U+E4DF

+$#A Korean EUDC Ranges

The following table shows the mapping from Korean EUDC characters (Unified Hangeul Code) and Unicode.

Unified Hangeul | Unicode
C9A1 - C9FE | U+E000 - U+E05D
FEA1 - FEFE | U+E05E - U+E0BB

Créé avec HelpNDoc Personal Edition: Générateur facile de livres électroniques et documentation


Enhanced Metafile Records.rtf

+$#AK Enhanced Metafile Records

An enhanced metafile is an array of variable-length structures called enhanced metafile records. This overview contains definitions for enhanced metafile records.

+$#A About Enhanced Metafile Records

The first records in a metafile specify general information, such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of the metafile, identify the graphics device interface (GDI) functions used to create the picture.

+$#A Enhanced Metafile Record Structure

At the beginning of every enhanced metafile record is an EMR structure, which contains two members. The first member, iType, identifies the record type ⎯ that is, the GDI function whose parameters are contained in the record. Because the structures are variable in length, the other member, nSize, contains the size of the record. Immediately following the nSize member are the remaining parameters, if any, of the GDI function.

+$#A Playing Back Enhanced Metafiles

Applications can play back enhanced metafiles using the PlayEnhMetaFile or EnumEnhMetaFile function. If using PlayEnhMetaFile to draw the picture, you pass a handle to the enhanced metafile without being concerned with the format of the enhanced metafile records. However, it is sometimes desirable to enumerate the records in the enhanced metafile to search for a particular GDI function and modify the parameters of the function in some manner. To do this, you can use EnumEnhMetaFile and provide a callback function of the type ENHMFENUMPROC to process the enhanced metafile records. To modify the parameters for an enhanced metafile record, you must know the format of the parameters within the record.

+$#A Enhanced Metafile Reference

The following structures are used with enhanced metafile records. Note that the first structure, EMR, is used as the first member of the remaining structures.

EMR
EMRANGLEARC
EMRARC, EMRARCTO, EMRCHORD, EMRPIE
EMRBITBLT
EMRCREATEBRUSHINDIRECT
EMRCREATECOLORSPACE
EMRCREATEDIBPATTERNBRUSHPT
EMRCREATEMONOBRUSH
EMRCREATEPALETTE
EMRCREATEPEN
EMRELLIPSE, EMRRECTANGLE
EMREOF
EMREXCLUDECLIPRECT, EMRINTERSECTCLIPRECT
EMREXTCREATEFONTINDIRECTW
EMREXTCREATEPEN
EMREXTFLOODFILL
EMREXTSELECTCLIPRGN
EMREXTTEXTOUTA, EMREXTTEXTOUTW
EMRFILLRGN
EMRFRAMERGN
EMRGDICOMMENT
EMRINVERTRGN, EMRPAINTRGN
EMRLINETO, EMRMOVETOEX
EMRMASKBLT
EMRMODIFYWORLDTRANSFORM
EMROFFSETCLIPRGN
EMRPLGBLT
EMRPOLYDRAW
EMRPOLYDRAW16
EMRPOLYPOLYLINE, EMRPOLYPOLYGON
EMRPOLYPOLYLINE16, EMRPOLYPOLYGON16
EMRPOLYTEXTOUTA, EMRPOLYTEXTOUTW
EMRRESIZEPALETTE
EMRRESTOREDC
EMRROUNDRECT
EMRSELECTOBJECT, EMRDELETEOBJECT
EMRSELECTPALETTE
EMRSETARCDIRECTION
EMRSETCOLORADJUSTMENT
EMRSETDIBITSTODEVICE
EMRSETMAPPERFLAGS
EMRSETBKCOLOR, EMRSETTEXTCOLOR
EMRSETMITERLIMIT
EMRSETPALETTEENTRIES
EMRSETPIXELV
EMRSETWORLDTRANSFORM
EMRSTRETCHDIBITS
EMRSTRETCHBLT
EMRTEXT
EMRFILLPATH, EMRSTROKEANDFILLPATH, EMRSTROKEPATH
EMRPOLYLINE, EMRPOLYBEZIER, EMRPOLYGON, EMRPOLYBEZIERTO, EMRPOLYLINETO
EMRPOLYLINE16, EMRPOLYBEZIER16, EMRPOLYGON16, EMRPOLYBEZIERTO16, EMRPOLYLINETO16
EMRSCALEVIEWPORTEXTEX, EMRSCALEWINDOWEXTEX
EMRSELECTCOLORSPACE, EMRDELETECOLORSPACE
EMRSETVIEWPORTORGEX, EMRSETWINDOWORGEX, EMRSETBRUSHORGEX
EMRSETVIEWPORTEXTEX, EMRSETWINDOWEXTEX

+$#A Enhanced Metafile Records with No Parameters

typedef struct tagABORTPATH

{

EMR emr;

} EMRABORTPATH, *PEMRABORTPATH,
EMRBEGINPATH, *PEMRBEGINPATH,
EMRENDPATH, *PEMRENDPATH,
EMRCLOSEFIGURE, *PEMRCLOSEFIGURE,
EMRFLATTENPATH, *PEMRFLATTENPATH,
EMRWIDENPATH, *PEMRWIDENPATH,
EMRSETMETARGN, *PEMRSETMETARGN,
EMRSAVEDC, *PEMRSAVEDC,
EMRREALIZEPALETTE, *PEMRREALIZEPALETTE;

Contains data for the AbortPath, BeginPath, EndPath, CloseFigure, FlattenPath, WidenPath, SetMetaRgn, SaveDC, and RealizePalette enhanced metafile records.

+$#A Enhanced Metafile Records with One Parameter

typedef struct tagEMRSELECTCLIPPATH
{
EMR emr;

DWORD iMode;

} EMRSELECTCLIPPATH, *PEMRSELECTCLIPPATH,
EMRSETBKMODE, *PEMRSETBKMODE,
EMRSETMAPMODE, *PEMRSETMAPMODE,
EMRSETPOLYFILLMODE, *PEMRSETPOLYFILLMODE,
EMRSETROP2, *PEMRSETROP2,
EMRSETSTRETCHBLTMODE, *PEMRSETSTRETCHBLTMODE,
EMRSETTEXTALIGN, *PEMRSETTEXTALIGN,
EMRENABLEICM, *PEMRENABLEICM

Contains parameters for the SelectClipPath, SetBkMode, SetPolyFillMode, SetROP2, SetStretchBltMode, SetTextAlign, and EnableICM enhanced metafile records.

Members

emr

Base structure for all record types.

iMode

Value and meaning that varies depending on the function contained in the enhanced metafile record. For a description of this member, see the documentation of the functions contained in this record .

See Also

SelectClipPath, SetBkMode, SetPolyFillMode, SetROP2, SetStretchBltMode, SetTextAlign

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques facilement


Errors.rtf

+$#AK Errors

Well-written applications include code that allows them to recover gracefully from unexpected errors. When an error occurs, the application may need to request user intervention, or it may be able to recover without help. In extreme cases, the application may log the user off or shut down the system.

+$#A About Errors

Microsoft® Windows® includes a special error-handling mechanism for exceptions, such as divide-by-zero errors. For more information about exception handling, see Structured Exception Handling.

+$#A Notifying the User

To notify the user that some kind of error has occurred, many applications simply produce a sound by using the Beep or MessageBeep function or flash the window by using the FlashWindow function. An application can also use these functions to call attention to an error and then display a message box or an error message containing details about the error.

+$#A Logging Off or Shutting Down

An application can use the ExitWindows function to log the current user off. The ExitWindowsEx function can also perform the following tasks:

• Shut down the system.

• Shut down the system and restart it.

• Log off and force all running applications to terminate.

Logging off stops all processes associated with the security context of the process that called the exit function, logs the user off the system, and displays the Logon dialog box.

Shutting down brings the system to a condition in which it is safe to turn off the computer. All file-system buffers are flushed to the disk, and a message box is displayed informing the user that the computer can be turned off. If the reboot option is specified, the computer restarts.

The calling process must have the SE_SHUTDOWN_NAME privilege to shut down or to shut down and restart. For an example that enables this privilege and shuts down the system, see Shutting Down.

+$#A Last-Error Code

When an error occurs, most functions in the Microsoft® Win32® application programming interface (API) return an error code, usually FALSE, NULL, 0xFFFFFFFF, or −1. Most functions in the Win32 API also set an internal error code called the last-error code. When a function succeeds, the last-error code is not reset. The error code is maintained separately for each running thread; an error in one thread does not overwrite the last-error code in another thread. An application can retrieve the last-error code by using the GetLastError function; the error code may tell more about what actually occurred to make the function fail.

The SetLastError function sets the error code for the current thread. The SetLastErrorEx function also allows the caller to set an error type indicating the severity of the error. These functions are intended primarily for dynamic-link libraries (DLLs), so they can emulate the behavior of the Win32 API.

The Win32 API defines a set of error codes that can be set as last-error codes or be returned by these functions..

Error codes are 32-bit values (bit 31 is the most significant bit). Bit 29 is reserved for application-defined error codes; no Win32 API error code has this bit set. Developers who define an error code for an application should set this bit to indicate that the error code has been defined by an application and to ensure that the error code does not conflict with any system-defined error codes.

+$#A Error Message Boxes

Some errors, such as a drive-not-ready error (when the door of the floppy disk drive is open), cause Windows to display a message box informing the user that an error has occurred. Applications that handle these errors without user intervention can use the SetErrorMode function to suppress error message boxes.

+$#A Using Errors

• Notifying the user of errors - example

• Logging off

• Shutting down

• Retrieving the last-error code

+$#A Notifying the User of Errors - Example

The following example flashes a window and plays the system exclamation sound.

FlashWindow(hwnd, TRUE); /* invert the title bar */

Sleep(500); /* wait a bit */

FlashWindow(hwnd, TRUE); /* invert again */

/* Play the system exclamation sound. */

MessageBeep(MB_ICONEXCLAMATION);

+$#A Logging Off

The ExitWindows function logs the current user off. All applications must agree to terminate before the user logs off. If any application returns FALSE when it processes the WM_QUERYENDSESSION message, the user is not logged off. If your application handles the WM_QUERYENDSESSION message, you can allow the user to cancel the log-off operation, even if another application or Windows originated the end-session request.

The following example logs off the current user, unless the user selects the No button in the message box displayed when the application receives the WM_QUERYENDSESSION message.

ExitWindows(0, 0); /* log the current user off */

case WM_QUERYENDSESSION: /* in application's MainWndProc */

{

int i = MessageBox(NULL, "Shutdown?",

"Received WM_QUERYENDSESSION", MB_YESNO);

/*

* Return TRUE to allow shutdown, FALSE to stop.

*/

return i == IDYES;

break;

}

+$#A Shutting Down

The ExitWindowsEx function can be used to shut down the system. Shutting down flushes file buffers to disk and brings the system to a condition in which it is safe to turn off the computer. The calling process must have the SE_SHUTDOWN_NAME privilege to shut down the system.

The following example enables the SE_SHUTDOWN_NAME privilege and then shuts down the system.

HANDLE hToken;

TOKEN_PRIVILEGES tkp;

/* Get a token for this process. */

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

error("OpenProcessToken");

/* Get the LUID for the shutdown privilege. */

LookupPrivilegeValue(NULL, TEXT("SE_SHUTDOWN_NAME"),

&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get the shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,

(PTOKEN_PRIVILEGES)NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)

error("AdjustTokenPrivileges");

/* Shut down the system and force all applications to close. */

if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0))

error("ExitWindowsEx");

For more information about setting security privileges, see Security.

+$#A Retrieving the Last-Error Code

When a function in the Win32 API fails, it sets the last-error code. If your application needs more details about an error, it can retrieve the last-error code.

The following example shows an error-handling function used by the other examples in this topic.

void error(LPSTR lpszFunction)

{

CHAR szBuf[80];

DWORD dw = GetLastError();

sprintf(szBuf, "%s failed: GetLastError returned %u\n",

lpszFunction, dw);

MessageBox(NULL, szBuf, "ERROR", MB_OK);

ExitProcess(dw);

}

+$#A Error Functions

Following are the functions used with errors.

Beep
ExitWindows
ExitWindowsEx
FlashWindow
GetLastError
MessageBeep
SetErrorMode
SetLastError
SetLastErrorEx

Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle


Event Logging.rtf

+$#AK Event Logging

When an error occurs in an application written for Microsoft® Windows®, the system administrator or support personnel must determine what caused the error, attempt to recover any lost data, and prevent the error from recurring. If applications record important events such as low-memory conditions or excessive disk retries, the record of this information, called an event log, can be used to help determine what conditions may have caused the error and the context in which the error occurred. (Event logging can also be applied to the Windows operating system and to system services.) By periodically viewing the event log, the system administrator may be able to identify problems before they cause serious damage.

Many applications record errors and events in various proprietary error logs. These proprietary logs have different formats, apply different user interfaces for their display, and cannot be merged to provide a complete report. But all of these logs store similar information and present the logged data in similar ways.

Event logging in Windows provides a standard, centralized way for applications (and Windows) to record important software and hardware events. It also supplies a standard user interface for viewing the logs and a programming interface for examining the logs. Event logging provides a means to merge events from various sources into a single informative story.

+$#A Event Logging Model

Event logs store records of significant events on behalf of the Windows operating system and the applications that run with Windows. Generally, applications should log only information that could be useful in diagnosing a hardware or software problem.

Events are classified as information, warnings, and errors. All event classifications have well-defined common data and can optionally include event-specific data. For example, information can assert that a service has started. Warnings are used for recoverable problems such as low disk-space conditions. Errors are used for nonrecoverable conditions that might cause an application to fail. In addition to logging event classifications, the security service logs successes and failures in audits.

+$#A Logging Guidelines

Because logging functions are general purpose, developers must decide what information is appropriate to log. It is important to note that logging consumes resources: disk space and logging time. The event log is not intended to be used as a trace facility.

You should log information about events that can be used to diagnose problems after they have occurred. Logging low-memory conditions, for example, can provide information about a problem that the system administrator can solve by adding more memory to the computer.

Following are more examples in which event logging is helpful:

• A disk driver encounters a bad sector on the disk. It may be able to read from or write to the sector after some retries, but the sector will probably go bad eventually. The disk driver should log a warning if it can proceed; otherwise, it should log an error.

• A file system finds a bad sector and fixes it. A large number of events of this type might indicate that the disk is about to fail. The file system logs a warning event.

• A device driver encounters a hardware problem, such as a disk controller time-out, a power failure in a parallel port, or a data error from a network or serial card. Logging information about these events can help in diagnosing hardware problems. The device driver logs the hardware problem.

• An Application encounters resource problems. An application or a device driver may get into a low-memory situation (caused by a code bug, for example) that degrades performance. Logging an event when memory allocation fails might later provide a clue about what went wrong. The application logs the resource problem.

• A server application (a database or a communications server, for example) records events such as a user logging on, opening a database, or starting a file transfer. The server can also log important information as it runs, such as the number of resources the application is using, errors it encounters (cannot access file, host process disconnected, and so on), a corruption in the database (not able to find a leaf node down a certain path in a tree), or whether a file transfer was successful or not. All of this information can be valuable to a support person (or to the developer of the application). The application records such events and logs them as information events.

+$#A Logging Performance and Size

Typically, how much disk space does an event log require? What is the overhead for an application that makes a call to log an event? The answers to these questions depend on how much information the application chooses to log. This is why it is important to log only essential information and to place event logging calls in an error path in the application, rather than place them in the main path, where the performance hit would be more severe.

+$#A Event Logging Management Information

Event logging management information is stored in the Services key of the configuration database and can be modified by a system administrator.

The structure of the configuration information is as follows:

HKEY_LOCAL_MACHINE

SYSTEM

CurrentControlSet

Services

EventLog

Application

Security

System

The EventLog key contains several subkeys, called logfiles. The default logfiles are Application, Security, and System. Each logfile subkey can contain subkeys, called sources. You cannot use a source name that has been used as a logfile name, and source names should not be hierarchical. (The backslash character [\] cannot be used in a registry key.) Each source entry contains information specific to the source of the event, as shown in the following table.

Value | Description
EventMessageFile | Specifies the path for the event identifier message file. This value has the type REG_EXPAND_SZ.
CategoryMessageFile | Specifies the path for the category message file. The event category and event identifier message strings can be in the same file. This value has the type REG_EXPAND_SZ.
ParameterMessageFile | Specifies the path for the event source's parameter message file. This file contains language-independent strings that are to be inserted into the event description strings. You can use the same message file for parameter, category, and event identifier message strings. This value has the type REG_EXPAND_SZ.
CategoryCount | Specifies the number of categories supported. This value has the type REG_DWORD.
TypesSupported | Specifies a bitmask of supported types. This value has the type REG_DWORD.

When an application uses the RegisterEventSource or OpenEventLog function to get a handle of an event log, the event logging service searches for the specified source name in the registry. For example, the Application logfile might have configured sources of Microsoft® SQL Server™ and Microsoft® Excel. If an application uses RegisterEventSource or OpenEventLog with a source name of Application, SQL, or Excel, the event logging service returns a handle to the Application logfile.

An application can use the Application event log without adding a new source key to the registry. If the application calls RegisterEventSource, passing a source name that cannot be found in the registry, the event logging service uses the Application logfile by default. However, because there is not a message or category string file, the event viewer will not be able to map the event identifier or category to a replacement string. For this reason, the recommended procedure is to add a unique source name for the application to the registry. This allows you to specify message files for the event identifier and category in your events. Applications and services should add their source names to the Application logfile. Device drivers should add their source name to the System logfile.

An event viewer application uses the OpenEventLog function to open the event log for an event source. The event viewer can then use the ReadEventLog function to read event records from the log. ReadEventLog returns a buffer containing an EVENTLOGRECORD structure and additional information that describes a logged event. The EventID member of the EVENTLOGRECORD is the identifier of a description string in the source's event message file. The event viewer uses the LoadLibrary function to load the file indicated by the source's EventMessageFile registry value. The viewer then uses the FormatMessage function to retrieve the description string from the loaded module.

The description string may contain insertion string placeholders, such as %n, where %1 indicates the first insertion string, and so on. In this case, the buffer returned by ReadEventLog contains the insertion strings. The NumStrings member of the EVENTLOGRECORD indicates the number of insertion strings. The StringOffset member of the EVENTLOGRECORD indicates the location of the first insertion string in the buffer.

An insertion string may also contain placeholders of the form %%n, where n is the identifier of a string in the source's parameter message file. In this case, the event viewer uses LoadLibrary and FormatMessage to retrieve the insertion string from the file indicated by the source's ParameterMessageFile registry value.

For more information about using the registry, see Registry. For more information about creating and using message files, see your message compiler documentation.

+$#A Event Logging Operations

In the following table are the five operations that can be performed on the event logs.

Operation | Function
Backup | BackupEventLog
Clear | ClearEventLog
Query | GetOldestEventLogRecord, GetNumberOfEventLogRecords
Read | ReadEventLog
Write | ReportEvent

The OpenEventLog, OpenBackupEventLog, RegisterEventSource, DeregisterEventSource, and CloseEventLog functions also open and close event log handles; this provides an object-oriented model for the function set, as well as some performance gain when performing multiple operations on the logs.

The OpenEventLog and ReportEvent functions take an optional server name as a parameter so that the operations can be performed on the remote server. OpenEventLog should be used for reading or performing some administrative operation (backup, clear, query) on the log, and RegisterEventSource should be used for writing to logs.

+$#A Event Log Entry

This guide describes the parameters supplied for logged events.

+$#A Source

Source is the name of the software that logs the event. For many applications, Source may be simply the application name, or it may reflect a subcomponent of a larger application.

+$#A Event Identifier

Event identifiers are looked up within a message file to locate a description string that can be presented to the user. Support personnel also use event identifiers; if a user can identify the source and the event identifier, support personnel will know exactly which event has occurred. Event identifiers are unique to a particular source.

+$#A Type

Windows NT™ defines five types of event logging. Each event must be of a single type, because types cannot be combined.

Event type | Description
Information | Information events are used to note infrequent but significant successful operations. For example, when Microsoft SQL Server successfully loads, it may be appropriate to log a "SQL Server has started" information event. Note that while this is appropriate for major server services, it is generally inappropriate for a desktop application (Microsoft Excel, for example) to log an event each time it is started. Information events should not be used as a trace facility.
Warning | Warning events are used to indicate problems that are not significant, but that may foretell of future errors or other problems. Resource consumption is a good candidate for a Warning event. For example, a warning could be logged if disk space is low. Errors that are recovered without loss of function or data can be classified as warnings.
Error | Error events are used to indicate significant problems that have occurred and that the user should know about. Errors usually indicate a loss of functionality or data. For example, if a service cannot be loaded as the system boots, an Error event can be logged.
Success Audit | Success Audit events are security events that occur when an audited access attempt is successful. For example, a successful logon attempt is a Success Audit event.
Failure Audit | Failure Audit events are security events that occur when an audited access attempt fails. For example, a failed attempt to open a file is a Failure Audit event.

+$#A Category

Categories are used to help organize events so that they can be filtered in the Event Viewer. Each Source can define its own numbered Categories and the text strings to which they are mapped. The Categories must be numbered consecutively beginning with the number 1. With Event Viewer, it is possible to filter by Source and within Source, by Category. The security system uses several categories: Logon/Logoff, File System Access, Privileged Actions, Change in Security Policy. Categories for other parts of the system are developed over time as it becomes clear which events are being logged. An application can define its own categories.

+$#A Strings

Strings are optional language-independent (nonlocalized) strings used to fill in values for placeholders in description strings. Because the strings are not localized, it is critical that this field be used only to store language-independent strings such as numeric values or string tokens (filenames, user names, and so on). String length must not exceed 32 kilobytes − 1 characters.

It is not acceptable to use several strings to "paste" together a larger description or to use nontoken strings. The insertion string should be treated as data, not text. For example, the following example is not recommended:

Str1$ = successfully

Description = "User was %1 added to database."

Using these two strings to form the string, "User was successfully added to database." (the alternative being Str1$ = "not") is not acceptable for three reasons:

1. The strings "successfully" and "not" should be localized.

2. Even if the insertion strings are obtained from language-dependent message files, this is done when the event is logged, not when it is viewed. When the event is viewed, the language may be wrong.

3. Such substitutions of adverbs and adjectives will not work in many other languages. The preceding example should use two separate events.

The following example shows an appropriate use of strings:

Str1$ = "c:\testapp.c".

Description = "Access denied. Attempted to open the file %1"

+$#A Description

Each event is associated with a description located in a message file that is registered by the Source. The description should help the user understand what went wrong and suggest what actions to take. Focus the description toward users solving their own problems, not toward support people or developers. Make the description clear and concise and avoid culture-specific phrases. If your event includes private data in the Data field, include at the end of the description an explanation about what the Data field contains. For example, the network software often notes: "(The SMB is the event data)." As a convention, use parentheses around such remarks, as indicated in this example. Several applications can share the same message file, and the message file can contain descriptions for both the event identifier and the event category.

The description strings are indexed by event identifier, enabling Event Viewer to display event-specific text for any event based on the event identifier. All descriptions are localized and language dependent. Description strings can optionally contain placeholders for insertion strings. Each placeholder is represented by a percent sign and an index number for the string to substitute. For example, the placeholder %1 is replaced by the first insertion string, %5 is replaced by the fifth insertion string, and so on.

+$#A Data

Each event can have event-specific data associated with it. Event Viewer has no information of any of the events, and only displays extra data in a combined hexadecimal and text dump format. Use event-specific data sparingly, including it only if you are sure it will be useful to a support technician or a developer. You might include, for example, data containing a structure filled with information that could help a technician debug an application failure.

Examples of event-specific data are found in many network events; network control blocks (NCBs) are included as event-specific data. In all cases, the last part of the description string should include a note about what information is available as event-specific data. Third-party applications can use the Data field to store information the application can process independently of Event Viewer. For example, you could write your own viewer specific to your events, or write a program that scans the logfiles and makes reports, including information from the event-specific data.

+$#A The EVENTLOGRECORD Structure

The ReadEventLog function uses the EVENTLOGRECORD structure to format event log information. This structure contains information relating to the content of the event record. The actual event record information follows this structure in the buffer returned by the ReadEventLog function.

+$#A Special Cases

Each call to an event logging function is considered to be an atomic operation. For reading the event log, only whole event records are returned. For logging events, each event record is guaranteed to be written as a complete record in the log. The following list describes how the event logging service handles special conditions:

• Multiple calls written to the same log. The event logging service serializes the operations; the event logs are written sequentially in the logfile.

• A call to read and a call to write are made at the same time. What happens depends on where the current read operation is being carried out in the file. If the read position is at the end of the file, either the read operation fails with an "end-of-file" status (if the write operation has not been done), or it returns the new record that the write operation has just written to the log.

• Clearing the log before reading. The read operation returns an "end-of-file" status after the file is cleared.

• Clearing the log before writing. The clear operation truncates the logfile; the write operation then writes the new record at the beginning of the log.

+$#A Using Event Logging

• Adding a source to the registry.

• Reporting an event.

• Querying the event log.

• Reading the event log.

You can use the Windows Registration Database Editor (REGEDIT.EXE) to view the event log registry keys, and you can use Event Viewer to view the event logs.

+$#A Adding a Source to the Registry

You can use the default Application event log without adding your source name to the registry. However, Event Viewer will not be able to map your event identifier codes to message strings unless you register your source and provide a message-file name.

You can add a new source name to the registry by opening a new registry subkey under the Application key and adding registry values to the new subkey. The following code sample opens a new source name called SamplApp and adds to the new subkey a message-file name and a bitmask of supported types.

HKEY hk;

DWORD dwData;

UCHAR szBuf[80];

/*

* Add your source name as a subkey under the Application

* key in the EventLog service portion of the registry.

*/

if (RegCreateKey(HKEY_LOCAL_MACHINE,

"SYSTEM\\CurrentControlSet\\Services\

\\EventLog\\Application\\SamplApp",

&hk))

ErrorExit("could not create registry key");

/* Set the Event ID message-file name. */

strcpy(szBuf, "%SystemRoot%\\System\\SamplApp.dll");

/* Add the Event ID message-file name to the subkey. */

if (RegSetValueEx(hk, /* subkey handle */

"EventMessageFile", /* value name */

0, /* must be zero */

REG_EXPAND_SZ, /* value type */

(LPBYTE) szBuf, /* address of value data */

strlen(szBuf) + 1)) /* length of value data */

ErrorExit("could not set event message file");

/* Set the supported types flags. */

dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |

EVENTLOG_INFORMATION_TYPE;

if (RegSetValueEx(hk, /* subkey handle */

"TypesSupported", /* value name */

0, /* must be zero */

REG_DWORD, /* value type */

(LPBYTE) &dwData, /* address of value data */

sizeof(DWORD))) /* length of value data */

ErrorExit("could not set supported types");

RegCloseKey(hk);

+$#A Reporting an Event

After you have added a source name to the registry, use the RegisterEventSource function to get a handle of the Application event log, as in the following example, which gets the handle and then adds an event to the logs.

LPSTR aszMsg[] = {

"SamplApp",

};

HANDLE h;

h = RegisterEventSource(NULL, /* uses local computer */

"SamplApp"); /* source name */

if (h == NULL)

ErrorExit("could not register event source");

if (!ReportEvent(h, /* event log handle */

EVENTLOG_ERROR_TYPE, /* event type */

0, /* category zero */

0x1003, /* event identifier */

NULL, /* no user security identifier */

1, /* one substitution string */

0, /* no data */

(LPTSTR *) aszMsg, /* address of string array */

NULL)) /* address of data */

ErrorExit("could not report event");

DeregisterEventSource(h);

+$#A Querying the Event Log

The following code sample displays the number of event records currently in the Application event log and the System event log.

/* Open the System log. */

h = OpenEventLog(NULL, /* uses local computer */

"System"); /* source name */

if (h == NULL)

ErrorExit("could not open System event log");

/* Get the number of records in the System event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))

ErrorExit

("could not get number of records");

printf("There are %d records in the System event log.\n", cRecords);

CloseEventLog(h);

/* Open the Application log. */

h = OpenEventLog(NULL, /* uses local computer */

"Application"); /* source name */

if (h == NULL)

ErrorExit("could not open Application event log");

/* Get the number of records in the Application event log. */

if (!GetNumberOfEventLogRecords(h, &cRecords))

ErrorExit("could not get number of records");

printf("There are %d records in the Application event log.\n",

cRecords);

CloseEventLog(h);

+$#A Reading the Event Log

The following code sample reads all the records in the Application logfile and displays the event identifier, event type, and source name for each event log entry.

EVENTLOGRECORD *pevlr;

BYTE bBuffer[BUFFER_SIZE];

DWORD dwRead, dwNeeded, cRecords, dwThisRecord = 0;

/* Open the Application event log. */

h = OpenEventLog(NULL, /* uses local computer */

"Application"); /* source name */

if (h == NULL)

ErrorExit("could not open Application event log");

pevlr = (EVENTLOGRECORD *) &bBuffer;

/*

* Opening the event log positions the file pointer

* for this handle at the beginning of the log.

*

* Read records sequentially until there

* are no more.

*/

while (ReadEventLog(h, /* event log handle */

EVENTLOG_FORWARDS_READ | /* reads forward */

EVENTLOG_SEQUENTIAL_READ, /* sequential read */

0, /* ignored for sequential reads */

pevlr, /* address of buffer */

BUFFER_SIZE, /* size of buffer */

&dwRead, /* count of bytes read */

&dwNeeded)) { /* bytes in next record */

while (dwRead > 0) {

/*

* Print the event ID, type, and source name.

* The source name is just past the end of the

* formal structure.

*/

printf("%02d Event ID: 0x%08X ",

dwThisRecord++, pevlr->EventID);

printf("EventType: %d Source: %s\n",

pevlr->EventType, (LPSTR) ((LPBYTE) pevlr +

sizeof(EVENTLOGRECORD)));

dwRead -= pevlr->Length;

pevlr = (EVENTLOGRECORD *)

((LPBYTE) pevlr + pevlr->Length);

}

pevlr = (EVENTLOGRECORD *) &bBuffer;

}

CloseEventLog(h);

+$#A Event Logging Functions

The following functions are used with event logging.

BackupEventLog
ClearEventLog
CloseEventLog
DeregisterEventSource
GetNumberOfEventLogRecords
GetOldestEventLogRecord
NotifyChangeEventLog
OpenBackupEventLog
OpenEventLog
ReadEventLog
RegisterEventSource
ReportEvent

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques facilement


Extended Menus.rtf

+$#AK Extended Menus

A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with the menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.

This Windows 95 overview describes the extended menu functions and explains how to use them in a Win32-based application.

+$#A Menus and Extended Menus Platform Differences

The overviews for menus and extended menus in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.

+$#A About Extended Menus

{ewl msdncd, EWGraphic, group10809 0 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview About Menus.

This overview describes menus and explains how to use them in applications designed for the Microsoft Win32® application programming interface (API).

The extended menu functions described in this section supercede the menu functions described in Menus (Windows NT). However, the two sets of functions can be used interchangeably insofar as their capabilities overlap.

+$#A Menu Bars and Pop-up Menus

{ewl msdncd, EWGraphic, group10809 1 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Bars and Pop-up Menus.

The menus in an application written for Windows are arranged hierarchically. A menu bar (sometimes called a top-level menu) resides at the uppermost level of the hierarchy; the lower levels consist of pop-up menus (also known as submenus).

The items in a menu bar represent the main categories of commands provided by an application. Selecting an item from the menu bar typically activates a pop-up menu whose items correspond to the commands in a given category. For example, a menu bar might contain a File menu item that, when the user selects it, activates a pop-up menu with such menu items as New, Open, Save, and so on.

Only an overlapped or pop-up window can contain a menu bar; a child window cannot contain one. If the window has a title bar, Windows positions the menu bar just below it. A menu bar is always visible. In contrast, a pop-up menu is not visible until the user selects a menu item that activates it. When the menu is displayed, it is attached to the title bar.

Every menu must have an owner window. Windows sends messages to a menu's owner window when the user selects the menu or chooses a command from the menu. These messages are described in Menu Messages.

+$#A Floating Pop-up Menus

{ewl msdncd, EWGraphic, group10809 2 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Floating Pop-up Menus.

Windows provides a special type of pop-up menu called a floating pop-up menu. A floating pop-up menu is not attached to the menu bar; it can appear anywhere on the screen. An application typically associates a floating pop-up menu with a portion of a window, such as the client area, or with a specific object, such as an icon. For this reason, floating pop-up menus are also called context menus.

A floating pop-up menu remains hidden until the user activates it, typically by clicking the right mouse button. The menu is usually displayed at the position of the caret or mouse cursor.

+$#A The System Menu

{ewl msdncd, EWGraphic, group10809 3 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview The System Menu.

A System menu (also known as the Control menu) is a pop-up menu defined and managed almost exclusively by the operating system. It is represented by a small box containing a horizontal line that is displayed at the left end of the title bar. When the user clicks the box, Windows displays the System menu. Windows also displays the System menu when the user clicks the icon of a minimized application.

A System menu provides a standard set of menu items that the user can choose to change a window's size or position, close the application, or activate the Windows Task List. Items in a System menu can be added, deleted, and modified, but most applications just use the standard set of menu items. An overlapped, pop-up, or child window can have a System menu. It is uncommon for an overlapped or pop-up window not to include a System menu.

When the user chooses a command from a System menu, Windows sends a WM_SYSCOMMAND message to the menu's owner window. In most applications, the window procedure does not process messages from a System menu. Instead, it simply passes the messages to the DefWindowProc function for system-default processing of the message. If a command is added to a System menu, the window procedure must process the command.

+$#A Help Identifier

Associated with each menu bar and pop-up menu is a 32-bit help identifier. If the user presses the F1 key while the menu is active, this value is sent to the owner window as part of a WM_HELP message. For more information about help identifiers, see Help.

+$#A Menu Handles

{ewl msdncd, EWGraphic, group10809 4 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Handles.

The system generates a unique handle for each menu. A menu handle is a value of the HMENU type. An application must specify a menu handle in many of the Windows menu functions. You receive the handle of a menu bar when you create the menu or load a menu resource. For more information about creating and loading menus, see Menu Creation.

To retrieve the handle of a menu that has been created or loaded, use the GetMenu or GetSubMenu function. GetMenu returns the handle of the menu bar assigned to the specified window. GetSubMenu returns the handle of the pop-up menu associated with a pop-up item (the GetMenuItemInfo function can also be used for this purpose.) The GetSystemMenu function retrieves the handle of a window's System menu.

+$#A Menu Items

{ewl msdncd, EWGraphic, group10809 5 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Items.

Each menu contains one or more menu items. By choosing a menu item, a user can carry out a command or open a pop-up menu. The following sections introduce the various types of menu items and the ways in which an application can control a menu item's appearance and functionality.

+$#A Command Items and Pop-up Items

Choosing a menu item may either perform a command or open a pop-up menu. Menu items that perform commands are called command items; those that open pop-up menus are called pop-up items.

When the user chooses a command item, Windows sends a command message to the window that owns the menu. If the command item is on the System menu, Windows sends the WM_SYSCOMMAND message. Otherwise, it sends the WM_COMMAND message.

Associated with each pop-up item is the handle of a corresponding pop-up menu. When the user selects a pop-up item, Windows opens the pop-up menu. No command message is sent to the owner window. However, Windows sends a WM_INITMENUPOPUP message to the owner window before displaying the pop-up menu. You can get the handle of the pop-up menu associated with a pop-up item by using the GetSubMenu or GetMenuItemInfo function (the handle is NULL for command items).

A menu bar typically contains pop-up items, but it may also contain command items. A pop-up menu typically contains command items, but it may also contain pop-up items. By adding pop-up items to a pop-up menu, you can nest pop-up menus to an arbitrary depth. If you include a pop-up item in a pop-up menu, Windows automatically displays a small arrow to the right of the menu item text as a visual cue to the user that a pop-up menu has been nested.

+$#A Menu Item Identifier

{ewl msdncd, EWGraphic, group10809 6 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Menu-Item Identifier.

Associated with each menu item is a 16-bit integer called an identifier. An application should assign a unique identifier to each menu item. When a command item is chosen, the item's identifier is sent to the owner window as part of a WM_COMMAND or WM_SYSCOMMAND message. You can also use an identifier to specify a menu item when you call many menu functions.

Pop-up items have identifiers just as command items do. However, Windows does not send a command message when a pop-up item is selected from a menu. Instead, Windows activates the pop-up menu associated with the menu item.

To retrieve the identifier of the menu item at a specified position, use the GetMenuItemID or GetMenuItemInfo function.

+$#A Menu Item Position

{ewl msdncd, EWGraphic, group10809 7 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Menu-Item Position.

In addition to having a unique identifier, each menu item in a menu bar or pop-up menu has a unique position value. The leftmost item in a menu bar, or the topmost item in a pop-up menu, has position zero. The position value is incremented for subsequent menu items. Windows assigns a position value to all items in a menu, including separators. The following illustration shows the position values of items in a menu bar and a pop-up menu.

{ewc msdncd, EWGraphic, group10809 8 /a "SDK_01.bmp"}

When calling a menu function that modifies or retrieves information about a specific menu item, you can specify the item using either its identifier or its position.

+$#A Default Menu Items

A pop-up menu may contain one default menu item. When the user opens a pop-up by double-clicking, Windows sends a command message to the menu's owner window and closes the menu as if the default item had been chosen. If there is no default command item, the pop-up menu remains open. To retrieve and set the default item for a pop-up menu, use the GetMenuDefaultItem and SetMenuDefaultItem functions.

+$#A Checked and Unchecked Menu Items

{ewl msdncd, EWGraphic, group10809 9 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Checked and Unchecked Menu Items.

A menu item can be either checked or unchecked. Windows displays a bitmap next to checked menu items to indicate their checked state. Windows does not display a bitmap next to unchecked items, unless an application-defined "unchecked" bitmap is specified. Only menu items in a pop-up menu can be checked; items in a menu bar cannot be checked.

Applications typically check or uncheck a menu item to indicate whether an option is in effect. For example, suppose an application has a toolbar that the user can show or hide using a Toolbar command. When the toolbar is hidden, the Toolbar menu item is unchecked. When the user chooses the command, the application checks the menu item and shows the toolbar. To retrieve and set the check state of a menu item, use the GetMenuItemInfo and SetMenuItemInfo functions.

Sometimes, a group of menu items corresponds to a set of mutually exclusive options. In this case, you can indicate the selected option by using a checked radio menu item (analogous to a radio button control). Checked radio items are displayed with a bullet bitmap instead of a check-mark bitmap. To check a menu item and make it a radio item, use the CheckMenuRadioItem function.

By default, Windows displays a check-mark or bullet bitmap next to checked menu items and no bitmap next to unchecked menu items. However, you can use the SetMenuItemBitmaps function to associate application-defined "checked" and "unchecked" bitmaps with a menu item. Windows then uses the specified bitmaps to indicate the menu item's checked or unchecked state.

Application-defined bitmaps associated with a menu item must be the same size as the default check-mark bitmap, the dimensions of which may vary depending on screen resolution. To retrieve the correct dimensions, use the GetMenuCheckMarkDimensions function. You can create multiple bitmap resources for different screen resolutions; create one bitmap resource and scale it, if necessary; or create a bitmap at run time and draw an image in it. The bitmaps may be either monochrome or color. However, because menu items are inverted when highlighted, the appearance of certain inverted color bitmaps may be undesirable.

+$#A Enabled, Disabled, and Grayed Menu Items

{ewl msdncd, EWGraphic, group10809 10 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Enabled, Disabled, and Grayed Menu Items.

A menu item can be enabled, disabled, or grayed. By default, a menu item is enabled. When the user chooses an enabled menu item, Windows sends a command message to the owner window or displays the corresponding pop-up menu, depending on whether the menu item is a command or a pop-up item.

When menu items are not available to the user, they should be disabled or grayed. Disabled and grayed menu items cannot be chosen. Typically, applications gray an unavailable menu item to provide a visual clue to the user that a command is not available; a disabled menu item does not provide this visual clue. A disabled item can serve as a heading for a group of related menu items. (For example, a disabled Save menu item can serve as a heading for a series of enabled command items, such as Save File, Save All, and Save As.) A grayed item can be used when an action is not appropriate. (For example, the Print command in the File menu can be grayed when the system does not have a printer installed.)

The EnableMenuItem function enables, disables, or grays a menu item. To determine whether a menu item is enabled, disabled, or grayed, use the GetMenuItemInfo function.

+$#A Highlighted Menu Items

{ewl msdncd, EWGraphic, group10809 11 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Highlighted Menu Items.

Windows automatically highlights menu items as the user selects them. However, highlighting can be explicitly added or removed from a menu item on the menu bar by using the HiliteMenuItem function. This function has no effect on menu items in pop-up menus. When HiliteMenuItem is used to highlight a menu item, though, the menu item only appears to be selected. If the user presses the ENTER key, the highlighted item is not chosen.

+$#A String, Bitmap, and Owner-Drawn Menu Items

An application can display a menu item using a string or a bitmap. In addition, an application can completely control the appearance of a menu item by using an owner-drawn item. The application is responsible for drawing owner-drawn items in any of their possible states (highlighted, checked, and so on).

For example, an application that has a Font menu might draw each menu item using the corresponding font (that is, the item for roman would be drawn using a roman font, the item for italic in an italic font, and so on).

+$#A Menu Item Separators and Line Breaks

{ewl msdncd, EWGraphic, group10809 12 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Menu-Item Separators and Line Breaks.

Windows provides a special type of menu item, called a separator, that appears as a horizontal line. A separator can be used in a pop-up menu to divide a menu into groups of related items. A separator cannot be used in a menu bar, and the user cannot select a separator.

When a menu bar contains more items than will fit on one line, Windows automatically breaks the line into two or more lines. You can cause a line break to occur at a specific item on a menu bar by assigning the MFT_MENUBREAK type flag to the item. Windows places that item and all subsequent item on a new line.

When a pop-up menu contains more items than will fit in one column, Windows automatically breaks the menu into two or more columns. You can cause a column break to occur at a specific item in a pop-up menu by assigning the MFT_MENUBREAK type flag to the item. Windows places that item and all subsequent items in a new column. The MFT_MENUBARBREAK type flag has the same effect except that a vertical line appears between the new column and the old.

+$#A Keyboard Access to Menus

{ewl msdncd, EWGraphic, group10809 13 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Keyboard Access to Menus.

Windows provides a standard keyboard interface for menus. You can enhance this interface by providing mnemonics and accelerator keys for your menu items. The following sections describe the standard keyboard interface, mnemonics, and accelerator keys.

+$#A Standard Keyboard Interface

{ewl msdncd, EWGraphic, group10809 14 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Standard Keyboard Interface.

Windows is designed to work with or without a mouse or other pointing device. Because Windows provides a standard keyboard interface, the user can use the keyboard to select menu items. This keyboard interface does not need special code. An application receives a command message whether a menu item is selected through the keyboard or by using a mouse. The standard keyboard interface processes the following keystrokes.

Keystroke | Action
Alphabetic character | Selects the first menu item with the specified character as its mnemonic. If the selected item invokes a pop-up menu, the menu is displayed and the first item is highlighted. Otherwise, the menu item is chosen.
ALT | Toggles in and out of menu bar mode.
ALT+SPACEBAR | Displays the System menu.
ENTER | Activates a pop-up menu and selects the first menu item if an item has a pop-up menu associated with it. Otherwise, this keystroke chooses the item as if the user released the mouse button while the item was selected.
ESC | Exits menu mode.
LEFT ARROW | Works like the RIGHT ARROW key, except in the opposite direction. In pop-up menus, this keystroke backs up one column; when the currently selected item is in the far-left column, the previous pop-up menu is selected.
RIGHT ARROW | Cycles to the next top-level menu item. Top-level menu items include menu bar items and the System menu. If the selected item is in a pop-up menu, the next column in the pop-up menu is selected or the next top-level menu item is selected.
UP or DOWN ARROWS | Activates a pop-up menu when pressed in a top-level menu. When pressed in a pop-up menu, either keystroke selects the next or previous item.

+$#A Menu Mnemonics

{ewl msdncd, EWGraphic, group10809 15 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Mnemonics.

The standard keyboard interface for menus can be enhanced by adding mnemonics to menu items. A mnemonic is an underlined letter in the text of a menu item. When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter. The menu bar is active when the user presses and releases the ALT key and the first item on the menu bar is highlighted. A pop-up menu is active when it is displayed.

A mnemonic keystroke for a menu item is created by preceding any character in the item's text string with an ampersand. For example, the text string "&Move" causes Windows to underline the letter "M".

+$#A Menu Keyboard Accelerators

{ewl msdncd, EWGraphic, group10809 16 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Keyboard Accelerators.

In addition to having a mnemonic, a menu item can have a keyboard accelerator associated with it. A keyboard accelerator is different from a mnemonic, because the menu does not have to be active for the accelerator key to work. Also, a mnemonic is always associated with a menu item, while a keyboard accelerator is usually (but does not have to be) associated with a menu item.

Text that identifies the keyboard accelerator is added to the menu item text string. The accelerator text appears to the right of the menu item name, after a backslash and tab character (\t). For example, "&Close\tAlt+F4" represents a Close item with the ALT+F4 key combination as its keyboard accelerator and with the letter "C" as its mnemonic. For more information about accelerators, see Keyboard Accelerators.

+$#A Menu Creation

{ewl msdncd, EWGraphic, group10809 17 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Creation.

You can create a menu using either a menu template or using menu creation functions. Menu templates are typically defined as resources. Menu template resources can be loaded explicitly or assigned as the default menu for a window class. You can also create menu template resources dynamically in memory.

+$#A Menu Template Resources

{ewl msdncd, EWGraphic, group10809 18 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Menu-Template Resources.

Most applications create menus using menu template resources. A menu template defines a menu, including the items in the menu bar and all pop-up menus. For information about creating a menu template resource, see the documentation included with your development tools.

Once you have created a menu template resource and have added it to your application's executable (.EXE) file, you can use the LoadMenu function to load the resource into memory. This function returns the handle of the menu, which you can then assign to a window by using the SetMenu function.

Implementing menus as resources makes an application easier to localize for use in multiple countries. Only the resource-definition file needs to be translated for each language, not the application's source code.

+$#A Menu Template in Memory

{ewl msdncd, EWGraphic, group10809 19 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Template in Memory.

A menu can be created from a menu template that is built in memory at run time. For example, an application that allows a user to customize its menu might create a menu template in memory based on the user's preferences. The application could then save the template in a file or in the registry for future use. To create a menu from a template in memory, use the LoadMenuIndirect function.

There are two types of menu templates. A standard menu template consists of a MENUITEMTEMPLATEHEADER structure followed by one or more MENUITEMTEMPLATE structures. In most cases, an extended menu template is used; it consists of a MENUEX_TEMPLATE_HEADER structure followed by one or more MENUEX_TEMPLATE_ITEM structures.

+$#A Menu Creation Functions

{ewl msdncd, EWGraphic, group10809 20 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Menu-Creation Functions.

Using menu creation functions, you can create menus at run time or add menu items to existing menus. You can use the CreateMenu function to create an empty menu bar and the CreatePopupMenu function to create an empty pop-up menu. To add items to a menu, use the InsertMenuItem function.

+$#A Menu Display

{ewl msdncd, EWGraphic, group10809 21 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Display.

After a menu has been loaded or created, it must be assigned to a window before Windows can display it. This assignment can be done by defining a class menu. For more information about class menus, see Class Menus. You can also assign a menu to a window by specifying the handle of the menu as the hmenu parameter of the CreateWindowEx function or by calling the SetMenu function.

To display a floating pop-up menu, use the TrackPopupMenu or TrackPopupMenuEx function. Floating pop-up menus, also called context menus, are typically displayed when the WM_CONTEXTMENU message is processed.

+$#A Class Menus

{ewl msdncd, EWGraphic, group10809 22 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Class Menus.

You can specify a default menu, called a class menu, when you register a window class. To do so, you assign the name of the menu template resource to the lpszMenuName member of the WNDCLASS structure used to register the class.

By default, every window is assigned the class menu for its window class so that you do not have to explicitly load the menu and assign it to each window. You can override the class menu by specifying a different menu handle in a call to the CreateWindowEx function. You can also change a window's menu after it is created by using the SetMenu function. For more information about window classes, see Window Classes.

+$#A Menu Destruction

{ewl msdncd, EWGraphic, group10809 23 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Destruction.

If a menu is assigned to a window and that window is destroyed, Windows automatically destroys the menu, freeing the menu's handle and the memory occupied by the menu. Windows does not automatically destroy a menu that is not assigned to a window. An application must destroy the unassigned menu by calling the DestroyMenu function. Otherwise, the menu continues to exist in memory even after the application closes.

+$#A Menu Messages

{ewl msdncd, EWGraphic, group10809 24 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Messages Overview.

Windows reports menu-related activity by sending messages to the window procedure of the window that owns the menu. Windows sends a series of messages when the user selects items on the menu bar or clicks the right mouse button to display a floating pop-up menu.

When the user activates an item on the menu bar, the owner window first receives a WM_SYSCOMMAND message. This message includes a flag that indicates whether the user activated the menu using the keyboard (SC_KEYMENU) or mouse (SC_MOUSEMENU). For more information about the keyboard interface for menus, see Keyboard Access to Menus.

Next, before displaying any pop-up menus, Windows sends the WM_INITMENU message to the window procedure so that an application can modify the menus before the user sees them. Windows sends the WM_INITMENU message only once per menu activation.

When the user selects a pop-up item, Windows sends the owner window the WM_INITMENUPOPUP message before displaying the pop-up menu. This message gives the application an opportunity to modify the pop-up menu before it is displayed.

Each time the user moves the highlighting from one item to another, Windows sends a WM_MENUSELECT message to the window procedure of the menu's owner window. This message identifies the currently selected menu item. Many applications provide an information area at the bottom of their main windows and use this message to display additional information about the selected menu item.

When the user chooses a command item from a menu, Windows sends a WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.

Not all menus are accessed through a window's menu bar. Many applications display floating pop-up menus when the user clicks the right mouse button at a specific location. Such applications should process the WM_CONTEXTMENU message and display a floating pop-up menu, if appropriate. If an application does not display a floating pop-up menu, it should pass the WM_CONTEXTMENU message to the DefWindowProc function for default processing.

+$#A Menu Modifications

{ewl msdncd, EWGraphic, group10809 25 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Menu Modifications.

A number of functions enable the developer to change a menu after it has been loaded or created. These changes may include adding or removing menu items and modifying existing menu items.

To add a menu item, use the InsertMenuItem function. You can use the SetMenuItemInfo function to change the attributes of an existing menu item. The lpmii parameter points to a MENUITEMINFO structure, which contains the new attributes and specifies which attributes to change. A menu item's attributes include its type, state, identifier, pop-up menu, bitmaps, item data, and text.

To retrieve information about a menu item, use the GetMenuItemInfo function. The lpmii parameter points to a MENUITEMINFO structure, which specifies the attributes to retrieve and receives their current values.

The DeleteMenu or RemoveMenu function is used to delete a menu item from a menu. If the item being deleted is a pop-up item, DeleteMenu deletes the associated pop-up menu, discarding the menu handle and freeing the memory used by the pop-up menu. RemoveMenu deletes a menu item, but if the item activates a pop-up menu, the function does not destroy the pop-up menu or its handle, allowing the menu to be reused.

After a menu bar has been modified, the DrawMenuBar function must be called to redraw the menu bar. Otherwise, the modifications will not appear until Windows redraws the owner window.

+$#A Superseded Functions

The following menu functions have been superseded. The new function should be used instead of the corresponding superseded function.

Superseded function | New function
AppendMenu | InsertMenuItem
ChangeMenu | SetMenuItemInfo
CheckMenuItem | SetMenuItemInfo
GetMenuState | GetMenuItemInfo
InsertMenu | InsertMenuItem
ModifyMenu | SetMenuItemInfo

+$#A Using Extended Menus

• Using a menu template resource.

• Creating a floating pop-up menu.

• Using menu item bitmaps.

• Creating owner-drawn menu items.

• Using custom check mark bitmaps.

+$#A Using a Menu Template Resource

{ewl msdncd, EWGraphic, group10809 26 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Using a Menu-Template Resource.

You typically include a menu in an application by creating a menu template resource and then loading the menu at run time. This section explains how to load a menu template resource and use it in your application. For information about creating a menu template resource, see the documentation included with your development tools.

+$#A Loading a Menu Template Resource

{ewl msdncd, EWGraphic, group10809 27 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Loading a Menu-Template Resource.

You can load a menu template resource by calling the LoadMenu function, specifying the handle of the module that contains the resource and the menu template's identifier. LoadMenu returns a menu handle that you can use to assign the menu to a window. This window becomes the menu's owner window, receiving all the messages generated by the menu.

You can assign a menu to a window by using the SetMenu function or by specifying the menu's handle in the hmenu parameter of the CreateWindowEx function when creating a window. Another way that you can assign a menu to a window is to specify a menu template when you register a window class; the template identifies the menu as the class menu for the given window class. Whenever you create a window of the given class, Windows automatically assigns the given menu to the window.

You can create a class menu by including the identifier of the menu template resource as the lpszMenuName member of a WNDCLASS structure and then passing the address of the structure to the RegisterClass function.

+$#A Creating a Class Menu

{ewl msdncd, EWGraphic, group10809 28 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Creating a Class Menu.

The following example shows how to create a class menu for an application, create a window that uses the class menu, and process menu commands in the window procedure.

HINSTANCE hinst;

int APIENTRY WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hinstance;

HINSTANCE hPrevInstance;

LPSTR lpCmdLine;

int nCmdShow;

{

MSG msg; // message

WNDCLASS wc; // windowclass data

HWND hwnd; // handle of main window

// Create the window class for the main window. Specify

// the identifier of the menu-template resource as the

// lpszMenuName member of the WNDCLASS structure to create

// the class menu.

wc.style = 0;

wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hinstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MyMenuResource";

wc.lpszClassName = "MainWClass";

if (!RegisterClass(&wc))

return FALSE;

hinst = hinstance;

// Create the main window. Set the hmenu parameter to NULL so

// that Windows uses the class menu for the window.

hwnd = CreateWindow("MainWClass", "Sample Application",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,

NULL);

if (hwnd == NULL)

return FALSE;

// Make the window visible and send a WM_PAINT message to the

// window procedure.

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

// Start the main message loop.

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

UNREFERENCED_PARAMETER(hPrevInstance);

}

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

switch (uMsg) {

.

. // Process other window messages.

.

case WM_COMMAND:

// Test for the identifier of a command item.

switch(LOWORD(wParam)) {

case IDM_FI_OPEN:

DoFileOpen(); // application-defined

break;

case IDM_FI_CLOSE:

DoFileClose(); // application-defined

break;

.

. // Process other menu commands.

.

default:

break;

}

return 0;

.

. // Process other window messages.

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return NULL;

}

+$#A Creating a Floating Pop-up Menu

{ewl msdncd, EWGraphic, group10809 29 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Creating a Floating Pop-up Menu.

To use a floating pop-up menu in an application, pass the handle of a pop-up menu to the TrackPopupMenu or TrackPopupMenuEx function. An application typically calls these functions in a window procedure when processing the WM_CONTEXTMENU message. For more information about this message, see Processing the WM_CONTEXTMENU Message.

In addition to the pop-up menu handle, TrackPopupMenu requires that you specify the handle of the owner window, the position of the floating pop-up menu (in screen coordinates), and the mouse button that the user can use to choose an item. TrackPopupMenuEx requires the same parameters as TrackPopupMenu, but TrackPopupMenuEx also enables you to specify a portion of the screen that the menu should not obscure.

You can specify the position of a menu by providing x- and y-coordinates along with the TPM_CENTERALIGN, TPM_LEFTALIGN, or TPM_RIGHTALIGN flag. The flag specifies the position of the menu relative to the x- and y-coordinates.

You should permit the user to choose an item from a pop-up menu by using the same mouse button used to display the menu. To do this, specify either TPM_LEFTBUTTON or TPM_RIGHTBUTTON flag to indicate which mouse button the user can use to choose a menu item.

+$#A Processing the WM_CONTEXTMENU Message

The WM_CONTEXTMENU message is generated when an application's window procedure passes the WM_RBUTTONUP or WM_NCRBUTTONUP message to the DefWindowProc function. The application can process this message to display a floating pop-up menu appropriate to a specific portion of its display. If the application does not display a menu, it should pass the message to DefWindowProc for default handling.

Following is an example of WM_CONTEXTMENU message processing as it might appear in an application's window procedure. The low-order and high-order words of the lParam parameter specify the screen coordinates of the mouse when the right mouse button is released. The application-defined OnContextMenu function returns TRUE if it displays a context menu or FALSE if it does not.

case WM_CONTEXTMENU:

if (!OnContextMenu(hwnd, LOWORD(lParam), HIWORD(lParam)))

return DefWindowProc(hwnd, uMsg, wParam, lParam);

break;

The following application-defined OnContextMenu function displays a floating pop-up menu if the specified mouse position is within the window's client area. A more sophisticated function might display one of several different menus depending on which portion of the client area is specified. To actually display the floating pop-up menu, this example calls an application-defined function called DisplayContextMenu. For a description of this function, see Displaying a Floating Pop-up Menu.

BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y)

{

RECT rc; // client area of window

POINT pt = { x, y }; // location of mouse click

// Get the bounding rectangle of the client area.

GetClientRect(hwnd, &rc);

// Convert the mouse position to client coordinates.

ScreenToClient(hwnd, &pt);

// If the position is in the client area, display a floating

// pop-up menu.

if (PtInRect(&rc, pt)) {

ClientToScreen(hwnd, &pt);

DisplayContextMenu(hwnd, pt);

return TRUE;

}

// Return FALSE if no menu is displayed.

return FALSE;

}

+$#A Displaying a Floating Pop-up Menu

The function shown in the following example displays a floating pop-up menu. This function might be called when an application's window procedure receives a WM_CONTEXTMENU message.

The application includes a menu resource identified by the string "PopupMenu". The top-level menu, or menu bar, simply contains a pop-up menu item. The application uses the TrackPopupMenu function to display the pop-up menu associated with this menu item. (The top-level menu itself is not displayed because TrackPopupMenu requires the handle of a pop-up menu.)

VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt)

{

HMENU hmenu; // top-level menu

HMENU hmenuTrackPopup; // pop-up menu

// Load the menu resource.

if ((hmenu = LoadMenu(hinst, "PopupMenu")) == NULL)

return;

// TrackPopupMenu cannot display the top-level menu, so get

// the handle of the first pop-up menu.

hmenuTrackPopup = GetSubMenu(hmenu, 0);

// Display the floating pop-up menu. Track the right mouse

// button on the assumption that this function is called

// during WM_CONTEXTMENU processing.

TrackPopupMenu(hmenuTrackPopup,

TPM_LEFTALIGN | TPM_RIGHTBUTTON,

pt.x, pt.y, 0, hwnd, NULL);

// Destroy the menu.

DestroyMenu(hmenu);

}

+$#A Using Menu Item Bitmaps

{ewl msdncd, EWGraphic, group10809 30 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview, Using Menu-Item Bitmaps.

Windows can use a bitmap instead of a text string to display a menu item. To use a bitmap, you must set the MF_BITMAP flag for the menu item and specify the handle of the bitmap that Windows should display for the menu item. This section describes how to set the MF_BITMAP flag and retrieve the handle of a bitmap.

+$#A Setting the MFT_BITMAP Type Flag

The MFT_BITMAP type flag tells Windows to use a bitmap rather than a text string to display a menu item. A menu item's MFT_BITMAP flag must be set at run time; it cannot be set in a menu-template resource.

You can use the SetMenuItemInfo or InsertMenuItem function to set the MFT_BITMAP type flag. To change a menu item from a text item to a bitmap item, use SetMenuItemInfo. To add a new bitmap item to a menu, use the InsertMenuItem function.

+$#A Creating the Bitmap

{ewl msdncd, EWGraphic, group10809 31 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Creating the Bitmap.

When you set the MFT_BITMAP type flag for a menu item, you must also specify the handle of the bitmap that Windows should display for the menu item. You can provide the bitmap as a bitmap resource or create the bitmap at run time. If you use a bitmap resource, you can use the LoadBitmap function to load the bitmap and obtain its handle.

To create the bitmap at run time, use graphics device interface (GDI) functions. GDI provides several ways to create a bitmap at run time, but developers typically use the following method:

1. Use the CreateCompatibleDC function to create a device context compatible with the device context used by the application's main window.

2. Use the CreateCompatibleBitmap function to create a bitmap compatible with the application's main window or use the CreateBitmap function to create a monochrome bitmap.

3. Use the SelectObject function to select the bitmap into the compatible device context.

4. Use GDI drawing functions, such as Ellipse and LineTo, to draw an image into the bitmap.

For more information about bitmaps, see Bitmaps.

+$#A Example of Menu Item Bitmaps

The example in this section creates two pop-up menus, each containing several bitmap menu items. For each pop-up menu, the application adds a corresponding pop-up item to the main window's menu bar.

The first pop-up menu contains menu items showing each of three chart types: pie, line, and bar. The bitmaps for these menu items are defined as resources and are loaded by using the LoadBitmap function. Associated with this menu is a "Chart" pop-up item on the menu bar.

The second pop-up menu contains menu items showing each of the five line styles used with the CreatePen function: PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, and PS_DASHDOTDOT. The application creates the bitmaps for these menu items at run time using GDI drawing functions. Associated with this menu is a "Lines" pop-up item on the menu bar.

Defined in the application's window procedure are two static arrays of bitmap handles. One array contains the handles of the three bitmaps used for the Chart menu. The other contains the handles of the five bitmaps used for the Lines menu. When processing the WM_CREATE message, the window procedure loads the chart bitmaps, creates the line bitmaps, and then adds the corresponding menu items. When processing the WM_DESTROY message, the window procedure deletes all of the bitmaps.

Following are the relevant portions of the application's header file.

// Menu item identifiers

#define IDM_PIE 1

#define IDM_LINE 2

#define IDM_BAR 3

#define IDM_SOLID 4

#define IDM_DASH 5

#define IDM_DASHDOT 6

#define IDM_DASHDOTDOT 7

// Number of items on the Chart and Lines menus

#define C_LINES 5

#define C_CHARTS 3

// Bitmap resource identifiers

#define IDB_PIE 1

#define IDB_LINE 2

#define IDB_BAR 3

// Dimensions of the line bitmaps

#define CX_LINEBMP 40

#define CY_LINEBMP 10

Following are the relevant portions of the window procedure. The window procedure performs most of its initialization by calling the application-defined LoadChartBitmaps, CreateLineBitmaps, and AddBitmapMenu functions, described later in this section.

LRESULT CALLBACK MainWindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

{

static HBITMAP aHbmLines[C_LINES];

static HBITMAP aHbmChart[C_CHARTS];

int i;

switch (uMsg) {

case WM_CREATE:

// Call application-defined functions to load the

// bitmaps for the chart menu and create those for

// the lines menu.

LoadChartBitmaps(aHbmChart);

CreateLineBitmaps(aHbmLines);

// Call an application-defined function to create pop-up

// menus containing the bitmap menu items. The function

// also adds a pop-up item to the window's menu bar.

AddBitmapMenu(

hwnd, // menu bar's owner window

"&Chart", // text of pop-up item on menu bar

IDM_PIE, // ID of first item on pop-up menu

aHbmChart, // array of bitmap handles

C_CHARTS // number of items on pop-up menu

);

AddBitmapMenu(hwnd, "&Lines", IDM_SOLID,

aHbmLines, C_LINES);

break;

case WM_DESTROY:

for (i = 0; i < C_LINES; i++)

DeleteObject(aHbmLines[i]);

for (i = 0; i < C_CHARTS; i++)

DeleteObject(aHbmChart[i]);

PostQuitMessage(0);

break;

.

. // Process additional messages here.

.

default:

return (DefWindowProc(hwnd, uMsg, wParam, lParam));

}

return 0;

}

The application-defined LoadChartBitmaps function loads the bitmap resources for the chart menu by calling the LoadBitmap function, as follows.

VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm)

{

paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE));

paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE));

paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR));

}

The application-defined CreateLineBitmaps function creates the bitmaps for the Lines menu by using GDI drawing functions. The function creates a memory DC with the same properties as the desktop window's DC. For each line style, the function creates a bitmap, selects it into the memory DC, and draws in it.

VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm)

{

HWND hwndDesktop = GetDesktopWindow();

HDC hdcDesktop = GetDC(hwndDesktop);

HDC hdcMem = CreateCompatibleDC(hdcDesktop);

COLORREF clrMenu = GetSysColor(COLOR_MENU);

HBRUSH hbrOld;

HPEN hpenOld;

HBITMAP hbmOld;

int fnDrawMode;

int i;

// Create a brush using the menu background color,

// and select it into the memory DC.

hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu));

// Create the bitmaps. Select each one into the memory

// DC that was created and draw in it.

for (i = 0; i < C_LINES; i++) {

// Create the bitmap and select it into the DC.

paHbm[i] = CreateCompatibleBitmap(hdcDesktop,

CX_LINEBMP, CY_LINEBMP);

hbmOld = SelectObject(hdcMem, paHbm[i]);

// Fill the background using the brush.

PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY);

// Create the pen and select it into the DC.

hpenOld = SelectObject(hdcMem,

CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0)));

// Draw the line. To preserve the background color where

// the pen is white use the R2_MASKPEN drawing mode.

fnDrawMode = SetROP2(hdcMem, R2_MASKPEN);

MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL);

LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2);

SetROP2(hdcMem, fnDrawMode);

// Delete the pen, and select the old pen and bitmap.

DeleteObject(SelectObject(hdcMem, hpenOld));

SelectObject(hdcMem, hbmOld);

}

// Delete the brush and select the original brush.

DeleteObject(SelectObject(hdcMem, hbrOld));

// Delete the memory DC and release the desktop DC.

DeleteDC(hdcMem);

ReleaseDC(hwndDesktop, hdcDesktop);

}

The application-defined AddBitmapMenu function creates a pop-up menu and adds the specified number of bitmap menu items to it. Then it adds a corresponding pop-up item to the specified window's menu bar.

VOID WINAPI AddBitmapMenu(

HWND hwnd, // window that owned the menu bar

LPSTR lpszText, // text of pop-up item on menu bar

UINT uID, // ID of first bitmap menu item

HBITMAP *paHbm, // bitmaps for the menu items

int cItems) // number bitmap menu items

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup = CreatePopupMenu();

MENUITEMINFO mii;

int i;

// Add the bitmap menu items to the pop-up menu.

for (i = 0; i < cItems; i++) {

mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;

mii.fType = MFT_BITMAP;

mii.wID = uID + i;

mii.dwTypeData = (LPSTR) (paHbm[i]);

InsertMenuItem(hmenuPopup, i, TRUE, &mii);

}

// Add a pop-up item to the menu bar.

mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_SUBMENU;

mii.fType = MFT_STRING;

mii.hSubMenu = hmenuPopup;

mii.dwTypeData = lpszText;

InsertMenuItem(hmenuBar,

GetMenuItemCount(hmenuBar), TRUE, &mii);

}

+$#A Creating Owner-Drawn Menu Items

If you need complete control over the appearance of a menu item, you can use an owner-drawn menu item in your application. This section describes the steps involved in creating and using an owner-drawn menu item.

+$#A Setting the MFT_OWNERDRAW Flag

You cannot define an owner-drawn menu item in your application's resource-definition file. Instead, you must create a new menu item or modify an existing one, so it has the MFT_OWNERDRAW type flag.

You can use the InsertMenuItem or SetMenuItemInfo function to specify an owner-drawn menu item. To place a new item on a menu, moving the other items down, use InsertMenuItem. To change the content of a menu, use SetMenuItemInfo.

When calling these two functions, you must specify the address of a MENUITEMINFO structure, giving the properties of the new menu item or the properties you want to change for an existing menu item. To make an item an owner-drawn item, specify the MIIM_TYPE value for the fMask member and the MFT_OWNERDRAW value for the fType member.

By setting the appropriate members of the MENUITEMINFO structure, you can associate an application-defined value, which is called item data, with each menu item. To do so, specify the MIIM_DATA value for the fMask member and the application-defined value for the dwItemData member.

You can use item data with any type of menu item, but it is particularly useful for owner-drawn items. For example, suppose a structure contains information used to draw a menu item. An application might use the item data for a menu item to store a pointer to the structure. The item data is sent to the menu's owner window with the WM_MEASUREITEM and WM_DRAWITEM messages. To retrieve the item data for a menu at any time, use the GetMenuItemInfo function.

+$#A Owner-Drawn Menus and the WM_MEASUREITEM Message

Before Windows displays an owner-drawn menu item for the first time, it sends the WM_MEASUREITEM message to the window procedure of the window that owns the item's menu. This message contains a pointer to a MEASUREITEMSTRUCT structure that identifies the item and contains the item data that an application may have assigned to it. The window procedure must fill the itemWidth and itemHeight members of the structure before returning from processing the message. Windows uses the information in these members when creating the bounding rectangle in which an application draws the menu item. It also uses the information to detect when the user chooses the item.

+$#A Owner-Drawn Menus and the WM_DRAWITEM Message

Whenever the item must be drawn (for example, when it is first displayed or when the user selects it), Windows sends the WM_DRAWITEM message to the window procedure of the menu's owner window. This message contains a pointer to a DRAWITEMSTRUCT structure, which contains information about the item, including the item data that an application may have assigned to it. In addition, DRAWITEMSTRUCT contains flags that indicate the state of the item (such as whether it is grayed or checked) as well as a bounding rectangle and a DC that the application uses to draw the item.

An application must do the following while processing the WM_DRAWITEM message:

1. Determine the type of drawing that is necessary. To do so, check the itemAction member of the DRAWITEMSTRUCT structure.

2. Draw the menu item appropriately, using the bounding rectangle and DC obtained from the DRAWITEMSTRUCT structure. The application must draw only within the bounding rectangle. For performance reasons, Windows does not clip portions of the image that are drawn outside the rectangle.

3. Restore all GDI objects selected for the menu item's DC.

If the user selects the menu item, Windows sets the itemAction member of the DRAWITEMSTRUCT structure to the ODA_SELECT value and sets the ODS_SELECTED value in the itemState member. This is an application's cue to redraw the menu item to indicate that it is selected.

+$#A Example of Owner-Drawn Menu Items

The example in this section uses owner-drawn menu items in a pop-up menu. The menu items select specific font attributes, and the application displays each menu item using a font that has the corresponding attribute. For example, the Italic menu item is displayed in an italic font. The Character pop-up item on the menu bar opens the pop-up menu.

The menu bar and pop-up menu are defined initially by an extended menu-template resource. Because a menu template cannot specify owner-drawn items, the pop-up menu initially contains four text menu items with the following strings: "Regular," "Bold," "Italic," and "Underline." The application's window procedure changes these to owner-drawn items when it processes the WM_CREATE message. When it receives the WM_CREATE message, the window procedure calls the application-defined OnCreate function, which performs the following steps for each menu item:

1. Allocates an application-defined MYITEM structure.

2. Gets the text of the menu item and saves it in the application-defined MYITEM structure.

3. Creates the font used to display the menu item and saves its handle in the application-defined MYITEM structure.

4. Changes the menu item type to MFT_OWNERDRAW and saves a pointer to the application-defined MYITEM structure as item data.

Because a pointer to each application-defined MYITEM structure is saved as item data, it is passed to the window procedure in conjunction with the WM_MEASUREITEM and WM_DRAWITEM messages for the corresponding menu item. The pointer is contained in the itemData member of both the MEASUREITEMSTRUCT and DRAWITEMSTRUCT structures.

A WM_MEASUREITEM message is sent for each owner-drawn menu item the first time that it is displayed. The application processes this message by selecting the font for the menu item into a DC and then determining the space required to display the menu item text in that font. The font and menu item text are both specified by the menu item's MYITEM structure (the structure defined by the application). The application determines the size of the text by using the GetTextExtentPoint function.

The window procedure processes the WM_DRAWITEM message by displaying the menu item text in the appropriate font. The font and menu item text are both specified by the menu item's MYITEM structure. The application selects text and background colors appropriate to the menu item's state.

The window procedure processes the WM_DESTROY message to destroy fonts and free memory. The application deletes the font and frees the application-defined MYITEM structure for each menu item.

Following are the relevant portions of the application's header file.

// Menu item identifiers for the Character menu

#define IDM_CHARACTER 10

#define IDM_REGULAR 11

#define IDM_BOLD 12

#define IDM_ITALIC 13

#define IDM_UNDERLINE 14

// Structure associated with menu items

typedef struct tagMYITEM {

HFONT hfont;

int cchItemText;

char szItemText[1];

} MYITEM;

#define CCH_MAXITEMTEXT 256

Following are the relevant portions of the application's window procedure and its associated functions.

LRESULT CALLBACK MainWindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

{

switch (uMsg) {

case WM_CREATE:

if (!OnCreate(hwnd))

return -1;

break;

case WM_DESTROY:

OnDestroy(hwnd);

PostQuitMessage(0);

break;

case WM_MEASUREITEM:

OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);

return TRUE;

case WM_DRAWITEM:

OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam);

return TRUE;

.

. // Additional message processing goes here.

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

BOOL WINAPI OnCreate(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

UINT uID;

MYITEM *pMyItem;

// Get the handle of the pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Modify each menu item. Assume that the IDs IDM_REGULAR

// through IDM_UNDERLINE are consecutive numbers.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Allocate an item structure, leaving space for a

// string of up to CCH_MAXITEMTEXT characters.

pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,

sizeof(MYITEM) + CCH_MAXITEMTEXT);

// Save the item text in the item structure.

mii.fMask = MIIM_TYPE;

mii.dwTypeData = pMyItem->szItemText;

mii.cch = CCH_MAXITEMTEXT;

GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

pMyItem->cchItemText = mii.cch;

// Reallocate the structure to the minimum required size.

pMyItem = (MYITEM *) LocalReAlloc(pMyItem,

sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);

// Create the font used to draw the item.

pMyItem->hfont = CreateMenuItemFont(uID);

// Change the item to an owner-drawn item, and save

// the address of the item structure as item data.

mii.fMask = MIIM_TYPE | MIIM_DATA;

mii.fType = MFT_OWNERDRAW;

mii.dwItemData = (DWORD) pMyItem;

SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

}

return TRUE;

}

HFONT CreateMenuItemFont(UINT uID)

{

LOGFONT lf;

ZeroMemory(&lf, sizeof(lf));

lf.lfHeight = 20;

lstrcpy(lf.lfFaceName, "Times New Roman");

switch (uID) {

case IDM_BOLD:

lf.lfWeight = FW_HEAVY;

break;

case IDM_ITALIC:

lf.lfItalic = TRUE;

break;

case IDM_UNDERLINE:

lf.lfUnderline = TRUE;

break;

}

return CreateFontIndirect(&lf);

}

VOID WINAPI OnDestroy(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

UINT uID;

MYITEM *pMyItem;

// Get the handle of the pop-up menu.

mii.fMask = MIIM_SUBMENU; /* information to get */

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Free resources associated with each menu item.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Get the item data.

mii.fMask = MIIM_DATA;

GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

pMyItem = (MYITEM *) mii.dwItemData;

// Destroy the font and free the item structure.

DeleteObject(pMyItem->hfont);

LocalFree(pMyItem);

}

}

VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)

{

MYITEM *pMyItem = (MYITEM *) lpmis->itemData;

HDC hdc = GetDC(hwnd);

HFONT hfntOld = SelectObject(hdc, pMyItem->hfont);

SIZE size;

GetTextExtentPoint(hdc, pMyItem->szItemText,

pMyItem->cchItemText, &size);

lpmis->itemWidth = size.cx;

lpmis->itemHeight = size.cy;

SelectObject(hdc, hfntOld);

ReleaseDC(hwnd, hdc);

}

VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis)

{

MYITEM *pMyItem = (MYITEM *) lpdis->itemData;

COLORREF clrPrevText, clrPrevBkgnd;

HFONT hfntPrev;

int x, y;

// Set the appropriate foreground and background colors.

if (lpdis->itemState & ODS_SELECTED) {

clrPrevText = SetTextColor(lpdis->hDC,

GetSysColor(COLOR_HIGHLIGHTTEXT));

clrPrevBkgnd = SetBkColor(lpdis->hDC,

GetSysColor(COLOR_HIGHLIGHT));

}

else {

clrPrevText = SetTextColor(lpdis->hDC,

GetSysColor(COLOR_MENUTEXT));

clrPrevBkgnd = SetBkColor(lpdis->hDC,

GetSysColor(COLOR_MENU));

}

// Determine where to draw and leave space for a check mark.

x = lpdis->rcItem.left;

y = lpdis->rcItem.top;

x += LOWORD(GetMenuCheckMarkDimensions());

// Select the font and draw the text.

hfntPrev = SelectObject(lpdis->hDC, pMyItem->hfont);

ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE,

&lpdis->rcItem, pMyItem->szItemText,

pMyItem->cchItemText, NULL);

// Restore the original font and colors.

SelectObject(lpdis->hDC, hfntPrev);

SetTextColor(lpdis->hDC, clrPrevText);

SetBkColor(lpdis->hDC, clrPrevBkgnd);

}

+$#A Using Custom Check Mark Bitmaps

{ewl msdncd, EWGraphic, group10809 32 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Using Custom Check Mark Bitmaps.

Windows provides a default check mark bitmap for displaying next to a menu item that is checked. You can customize an individual menu item by providing a pair of bitmaps to replace the default check mark bitmap. Windows displays one bitmap when the item is checked and the other when it is unchecked. This section describes the steps involved in creating and using custom check mark bitmaps.

+$#A Creating Custom Check Mark Bitmaps

{ewl msdncd, EWGraphic, group10809 33 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Creating Custom Check Mark Bitmaps.

A custom check mark bitmap must be the same size as the default check mark bitmap. You can retrieve the default check mark size of the bitmap by calling the GetMenuCheckMarkDimensions function. The low-order word of this function's return value specifies the width; the high-order word specifies the height.

You can use bitmap resources to provide check mark bitmaps. However, because the required bitmap size varies depending on the display type, you may need to resize the bitmap at run time by using the StretchBlt function. Depending on the bitmap, the distortion caused by sizing could produce unacceptable results.

Instead of using a bitmap resource, you can create a bitmap at run time by using GDI functions. For the typical method, you follow these steps:

1. Use the CreateCompatibleDC function to create a DC compatible with the DC used by the application's main window.

2. Use the CreateCompatibleBitmap function to create a bitmap compatible with the application's main window, or use the CreateBitmap function to create a monochrome bitmap.

3. Use the SelectObject function to select the bitmap into the compatible DC.

4. Use GDI drawing functions, such as Ellipse and LineTo, to draw an image into the bitmap.

+$#A Associating Bitmaps with a Menu Item

{ewl msdncd, EWGraphic, group10809 34 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Associating Bitmaps with a Menu Item.

You associate a pair of check mark bitmaps with a menu item by passing the handles of the bitmaps to the SetMenuItemBitmaps function. The hbmUnchecked parameter identifies the unchecked bitmap, and the hbmChecked parameter identifies the checked bitmap. If you want to remove one or both check marks from a menu item, you can set the hbmUnchecked or hbmChecked parameter, or both, to NULL.

+$#A Setting the Check Mark Attribute

{ewl msdncd, EWGraphic, group10809 35 /a "SDK.BMP"} Windows 95 Overview Setting the Check Mark Attribute
For more information, see the Windows NT overview Setting the Check Mark Attribute.

The CheckMenuItem function sets a menu item's check mark attribute to either checked or unchecked. You can specify the MF_CHECKED value to set the check mark attribute to checked and the MF_UNCHECKED value to set it to unchecked. You can also set the check state of a menu item by using the SetMenuItemInfo function.

Sometimes a group of menu items represents a set of mutually exclusive options. By using the CheckMenuRadioItem function, you can check one menu item while simultaneously removing the check mark from all other menu items in the group.

+$#A Example of Using Custom Check Mark Bitmaps

The example in this section assigns custom check mark bitmaps to menu items in two pop-up menus. The menu items in the first pop-up menu specify character attributes: bold, italic, and underline. Each menu item can be either checked or unchecked. For these menu items, the example uses check mark bitmaps that resemble the checked and unchecked states of a check box control.

The menu items in the second pop-up menu specify paragraph alignment settings: left, centered, and right. Only one of these menu items is checked at any given time. For these menu items, the example uses check mark bitmaps that resemble the checked and unchecked states of a radio button control.

The window procedure processes the WM_CREATE message by calling the application-defined OnCreate function. The application-defined OnCreate function creates the four check mark bitmaps and then assigns them to their appropriate menu items by using the SetMenuItemBitmaps function.

To create each bitmap, the application-defined OnCreate function calls the application-defined CreateMenuBitmaps function, specifying a pointer to a bitmap-specific drawing function. The application-defined CreateMenuBitmaps function creates a monochrome bitmap of the required size, selects it into a memory DC, and erases the background. Then it calls the specified drawing function to fill in the foreground.

The four application-defined drawing functions are DrawCheck, DrawUncheck, DrawRadioCheck, and DrawRadioUncheck. They draw a rectangle with an X, an empty rectangle, an ellipse containing a smaller filled ellipse, and an empty ellipse, respectively.

The window procedure processes the WM_DESTROY message by deleting the check mark bitmaps. It retrieves each bitmap handle by using the GetMenuItemInfo function and then passes the handle to the DeleteObject function.

When the user chooses a menu item, a WM_COMMAND message is sent to the owner window. For menu items on the Character menu, the window procedure calls the application-defined CheckCharacterItem function. For items on the Paragraph menu, the window procedure calls the application-defined CheckParagraphItem function.

Each item on the Character menu can be checked and unchecked independently. Therefore, the application-defined CheckCharacterItem function simply toggles the specified menu item's check state. First, the function calls the GetMenuItemInfo function to get the current menu item state. Then it toggles the MFS_CHECKED state flag and sets the new state by calling the SetMenuItemInfo function.

Unlike character attributes, only one paragraph alignment can be selected at a time. Therefore, the application-defined CheckParagraphItem function checks the specified menu item and removes the check mark from all other items on the menu. To do so, it calls the CheckMenuRadioItem function.

Following are the relevant portions of the application's header file.

// Menu item identifiers for the Character menu

#define IDM_CHARACTER 10

#define IDM_BOLD 11

#define IDM_ITALIC 12

#define IDM_UNDERLINE 13

// Menu item identifiers for the Paragraph menu

#define IDM_PARAGRAPH 20

#define IDM_LEFT 21

#define IDM_CENTER 22

#define IDM_RIGHT 23

// Function-pointer type for drawing functions

typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size);

Following are the relevant portions of the application's window procedure and related functions.

LRESULT CALLBACK MainWindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

{

switch (uMsg) {

case WM_CREATE:

if (!OnCreate(hwnd))

return -1;

break;

case WM_DESTROY:

OnDestroy(hwnd);

PostQuitMessage(0);

break;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDM_BOLD:

case IDM_ITALIC:

case IDM_UNDERLINE:

CheckCharacterItem(hwnd, LOWORD(wParam));

break;

case IDM_LEFT:

case IDM_CENTER:

case IDM_RIGHT:

CheckParagraphItem(hwnd, LOWORD(wParam));

break;

.

. // Process other commands here.

.

}

break;

.

. // Process other messages here.

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Get the state of the specified menu item.

mii.fMask = MIIM_STATE; // information to get

GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

// Toggle the checked state.

mii.fState ^= MFS_CHECKED;

SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

}

VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Check the specified item and uncheck all the others.

CheckMenuRadioItem(

hmenuPopup, // handle of pop-up menu

IDM_LEFT, // first item in range

IDM_RIGHT, // last item in range

uID, // item to check

MF_BYCOMMAND // IDs, not positions

);

}

BOOL WINAPI OnCreate(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

UINT uID;

HBITMAP hbmChecked;

HBITMAP hbmUnchecked;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Create the checked and unchecked bitmaps.

hbmChecked = CreateMenuBitmap(DrawCheck);

hbmUnchecked = CreateMenuBitmap(DrawUncheck);

// Set the checkmark bitmaps for each menu item.

for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++) {

SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,

hbmUnchecked, hbmChecked);

}

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Create the checked and unchecked bitmaps.

hbmChecked = CreateMenuBitmap(DrawRadioCheck);

hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck);

// Set the checkmark bitmaps for each menu item.

for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++) {

SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND,

hbmUnchecked, hbmChecked);

}

// Initially check the IDM_LEFT paragraph item.

CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT,

IDM_LEFT, MF_BYCOMMAND);

return TRUE;

}

HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw)

{

// Create a DC compatible with the desktop window's DC.

HWND hwndDesktop = GetDesktopWindow();

HDC hdcDesktop = GetDC(hwndDesktop);

HDC hdcMem = CreateCompatibleDC(hdcDesktop);

// Determine the required bitmap size.

DWORD dwExt = GetMenuCheckMarkDimensions();

SIZE size = { LOWORD(dwExt), HIWORD(dwExt) };

// Create a monochrome bitmap and select it.

HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL);

HBITMAP hbmOld = SelectObject(hdcMem, hbm);

// Erase the background and call the drawing function.

PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS);

(*lpfnDraw)(hdcMem, size);

// Clean up.

SelectObject(hdcMem, hbmOld);

DeleteDC(hdcMem);

ReleaseDC(hwndDesktop, hdcDesktop);

return hbm;

}

VOID WINAPI DrawCheck(HDC hdc, SIZE size)

{

HBRUSH hbrOld;

hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));

Rectangle(hdc, 0, 0, size.cx, size.cy);

MoveToEx(hdc, 0, 0, NULL);

LineTo(hdc, size.cx, size.cy);

MoveToEx(hdc, 0, size.cy - 1, NULL);

LineTo(hdc, size.cx - 1, 0);

SelectObject(hdc, hbrOld);

}

VOID WINAPI DrawUncheck(HDC hdc, SIZE size)

{

HBRUSH hbrOld;

hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));

Rectangle(hdc, 0, 0, size.cx, size.cy);

SelectObject(hdc, hbrOld);

}

VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size)

{

HBRUSH hbrOld;

hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));

Ellipse(hdc, 0, 0, size.cx, size.cy);

SelectObject(hdc, GetStockObject(BLACK_BRUSH));

Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2);

SelectObject(hdc, hbrOld);

}

VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size)

{

HBRUSH hbrOld;

hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH));

Ellipse(hdc, 0, 0, size.cx, size.cy);

SelectObject(hdc, hbrOld);

}

VOID WINAPI OnDestroy(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

// Get the handle of the Character pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Get the check mark bitmaps and delete them.

mii.fMask = MIIM_CHECKMARKS;

GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii);

DeleteObject(mii.hbmpChecked);

DeleteObject(mii.hbmpUnchecked);

// Get the handle of the Paragraph pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Get the check mark bitmaps and delete them.

mii.fMask = MIIM_CHECKMARKS;

GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii);

DeleteObject(mii.hbmpChecked);

DeleteObject(mii.hbmpUnchecked);

}

+$#A Extended Menu Reference

The functions, structures, and messages used with extended menus can be grouped as follows. Some of these application programming interface (API) elements are also used with menus.

Menu Handles

GetMenu
GetSubMenu
GetSystemMenu

Menu-Item Information

GetMenuItemID
GetMenuItemInfo
SetMenuItemInfo
MENUITEMINFO

Default Menu Items

GetMenuDefaultItem
SetMenuDefaultItem

Menu-Item States

CheckMenuRadioItem
EnableMenuItem
GetMenuCheckMarkDimensions
HiliteMenuItem
SetMenuItemBitmaps

Menu Templates

LoadMenu
LoadMenuIndirect
MENUITEMTEMPLATE
MENUITEMTEMPLATEHEADER
MENUEX_TEMPLATE_HEADER
MENUEX_TEMPLATE_ITEM

Menu Creation and Modification

CreateMenu
CreatePopupMenu
DeleteMenu
DestroyMenu
DrawMenuBar
InsertMenuItem
RemoveMenu

Menu Display

SetMenu
TrackPopupMenu
TrackPopupMenuEx
WM_CONTEXTMENU

Menu Messages

WM_COMMAND
WM_INITMENU
WM_INITMENUPOPUP
WM_MENUSELECT
WM_SYSCOMMAND

+$#A Extended Menu Functions

The following are the extended menu functions.

CheckMenuRadioItem
GetMenuDefaultItem
GetMenuItemInfo
GetMenuItemRect
InsertMenuItem
MenuItemFromPoint
SetMenuDefaultItem
SetMenuItemInfo
TrackPopupMenuEx

+$#A Extended Menu Structures

The following are the extended menu structures.

MENUEX_TEMPLATE_HEADER
MENUEX_TEMPLATE_ITEM
MENUITEMINFO
TPMPARAMS

+$#A Extended Menu Messages

The following are the extended menu messages.

WM_CONTEXTMENU

Créé avec HelpNDoc Personal Edition: Écrire des livres électronique Kindle


File Installation Library.rtf

+$#AK File Installation Library

The Microsoft® Win32® application programming interface (API) includes a file installation library that makes it easier for applications to install files properly and enables setup programs to analyze files currently installed.

+$#A About File Installation

The functions included in the file installation library determine where a file should be installed, identify conflicts with currently installed files, and perform the installation process. These functions enable installation programs to avoid the following problems:

• Installing older versions of components over newer versions

• Changing the language in a mixed-language system without notification

• Installing multiple copies of a library in different directories

• Copying files to network directories shared by multiple users

The file installation library also includes functions that enable applications to query a version resource for file information and present the information in a clear format. This information includes the file's purpose, author, version number, and so on.

Version information can be added to any Microsoft® Windows® file that can have Windows resources, such as a dynamic-link library (DLL), an executable file, or a font file. To add the information, a developer must create a version resource and add the resource to the file by using a resource compiler and specifying VS_FILE_INFO as the resource type.

+$#A Creating an Installation Program

An installation program typically has the following goals:

• To place files in the correct location.

• To notify the user if the installation program is replacing an existing file with a version that is significantly different ⎯ for example, replacing a German-language file with an English-language file, or replacing a newer file with an older file.

When writing the installation program, the developer must have the following information for each file on the installation disk(s):

• The name and location of the file (referred to as the source file).

• The name of the equivalent file on the user's hard disk (referred to as the destination file). This name is usually the same as the filename on the installation disk.

• The sharing status of the file ⎯ that is, whether the file is private to the application being installed or could be shared by multiple applications.

For each file on the installation disk(s), the installation program must, at a minimum, call the VerFindFile and VerInstallFile functions. These functions are described briefly in the following paragraphs.

The installation program can use VerFindFile with the destination-file name to determine where the file should be copied on the disk. This function can also be used to specify whether the file is private to the application or can be shared. If a problem occurs in finding the file, VerFindFile returns an error value. For example, if Windows is using the destination file, VerFindFile returns VFF_FILEINUSE. The installation program must notify the user of the problem and respond to the user's decision to continue or to end the installation.

VerInstallFile copies the source file to a temporary file in the directory specified by VerFindFile. If necessary, VerInstallFile expands the file by using the functions in the data decompression library.

VerInstallFile compares the version information of the temporary file to that of the destination file. If the two differ, VerInstallFile returns one or more error values. For example, it returns VIF_SRCOLD if the temporary file is older than the destination file and VIF_DIFFLANG if the files have different language identifiers or code-page values. The installation program must notify the user of the problem and respond to the user's decision to continue or to end the installation.

Some VerInstallFile errors are recoverable. That is, the installation program can call VerInstallFile again, specifying the VIFF_FORCEINSTALL option, to install the file regardless of the version conflict. If VerInstallFile returns VIF_TEMPFILE and the user chooses not to force the installation, the installation program should delete the temporary file.

VerInstallFile could encounter a nonrecoverable error when attempting to force installation, even though the error did not exist previously. For example, the file could be locked by another user before the installation program attempted to force installation. If an installation program attempts to force installation after a non-recoverable error, VerInstallFile fails. The installation program must contain routines to recover from this type of error.

The recommended solution is to display for all errors a common dialog box with the buttons Install, Skip, and Install All. (Another solution is a dialog box with the buttons Yes, Yes to All, Skip, and Cancel.) The Install All button should prevent the installation program from prompting the user about similar errors by including the VIFF_FORCEINSTALL option in all subsequent uses of VerInstallFile. For nonrecoverable errors, the Install and Install All buttons should be disabled.

To display a useful error message to the user, the installation program usually must retrieve information from the version resources of the conflicting files. The file installation library provides four functions the installation program can use for this purpose: GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue, and VerLanguageName. GetFileVersionInfoSize returns the size of the version information. GetFileVersionInfo uses information retrieved by GetFileVersionInfoSize to retrieve a structure that contains the version information. VerQueryValue retrieves a specific member from that structure.

For example, if VerInstallFile returns the VIF_DIFFTYPE error, the installation program should use the GetFileVersionInfoSize, GetFileVersionInfo, and VerQueryValue functions on the temporary and destination files to obtain the general type of each file. If the languages of the files conflict, the installation program should also use VerLanguageName to translate the binary language identifier into a text representation of the language. (For example, 0x040C translates to the string "French.")

If VerInstallFile returns a file error, such as VIF_ACCESSVIOLATION, the installation program should use the GetLastError function to retrieve the most recent error value. The program should translate this value into an informative message to display to the user. The program must not yield control between the calls to VerInstallFile and GetLastError.

+$#A File Installation Library Functions

Following are the functions used for file installation.

GetFileVersionInfo
GetFileVersionInfoSize
VerFindFile
VerInstallFile
VerLanguageName
VerQueryValue

Obsolete or Deleted Functions

GetFileResource
GetFileResourceSize

Créé avec HelpNDoc Personal Edition: Créer des aides HTML, DOC, PDF et des manuels depuis une même source


File Manager Extensions.rtf

+$#AK File Manager Extensions

In Microsoft® Windows®, a File Manager extension is a dynamic-link library (DLL) that adds a menu and buttons to File Manager. A File Manager extension can also display (in the status bar) Help text for the buttons. This guide describes how to create and install extensions for File Manager.

+$#A About File Manager Extensions

File Manager maintains a list of extensions in an initialization file and loads the extensions when starting. An extension DLL contains an entry point that processes menu commands and notification messages sent by File Manager. Up to five extension DLLs can be installed at any one time.

+$#A Creating a File Manager Extension

A File Manager extension must reside in a DLL that includes a standard entry point, the FMExtensionProc callback function. The extension must include the WFEXT.H header file that defines File Manager messages and structures. File Manager communicates with the extension DLL by sending the following messages to the DLL's FMExtensionProc function.

Message | Meaning
1−99 | User has selected an item from the extension-supplied menu. The value is the identifier of the selected menu item.
FMEVENT_HELPSTRING | User is selecting an extension menu or toolbar command item. File Manager wants the extension to supply a helpstring.
FMEVENT_HELPMENUITEM | User has pressed F1 while selecting an extension menu or toolbar command item. File Manager wants the extension to call WinHelp appropriately for the command item.
FMEVENT_INITMENU | User has selected the extension's menu. The extension should initialize items in the menu.
FMEVENT_LOAD | File Manager is loading the extension DLL and prompts the DLL for information about the menu that the DLL supplies.
FMEVENT_SELCHANGE | Selection in the File Manager directory window or Search Results window has changed.
FMEVENT_TOOLBARLOAD | File Manager is creating the toolbar and prompts the extension DLL for information about any buttons the DLL adds to the toolbar.
FMEVENT_UNLOAD | File Manager is unloading the extension DLL.
FMEVENT_USER_REFRESH | User has chosen the Refresh command from the Window menu. The extension should update items in the menu, if necessary.

+$#A Creating the Entry-Point Function

File Manager communicates with an extension DLL through the FMExtensionProc callback function. Make sure to export this function by listing it in the EXPORTS statement of your extension's module-definition (.DEF) file. The FMExtensionProc function handles the messages listed in the preceding section by performing the tasks listed in the following table.

Task | Action
Initializing the extension (FMEVENT_LOAD) | Provides File Manager with the name and handle of the menu and saves the menu-item delta value.
Initializing the menu (FMEVENT_INITMENU) | Initializes all top-level menu items and the items in any submenus.
Initializing the toolbar (FMEVENT_TOOLBARLOAD) | Provides File Manager with information about the buttons to add to the toolbar, including the command identifier, Help string identifier, styles, and bitmap identifier.
Processing menu selections | Carries out commands that the user chooses from the extension's menu.
Processing file selections (FMEVENT_SELCHANGE) | Queries File Manager for information about the file that the user has selected from the directory window or Search Results window.
Updating items in the menu (FMEVENT_USER_REFRESH) | Modifies the menu as appropriate when the user chooses File Manager's Refresh command from the Window menu.
Providing menu item help strings (FMEVENT_HELPSTRING) | Provides File Manager with a help string for a specified command in a specified menu. File Manager displays this string in the area at the bottom left of the window.
Passing menu item help requests on to WinHelp (FMEVENT_HELPMENUITEM) | Provides WinHelp with the window passed by File Manager and menu item information,
Quitting the extension DLL (FMEVENT_UNLOAD) | Frees any memory allocated and prepares to exit.

The FMExtensionProc function is defined as follows.

LONG WINAPI FMExtensionProc(hwnd, wMsg, lParam)

HWND hwnd;

WORD wMsg;

LONG lParam;

The hwnd parameter identifies the File Manager window. An extension should use this window handle to specify the parent window for any dialog box or message box it must display. It should also use this handle to send query messages to File Manager. The uMsg parameter contains one of the File Manager messages listed previously. The lParam parameter contains a message-specific value. The return value from the FMExtensionProc function depends on the value of the uMsg parameter.

The menu added to File Manager may be a hierarchical (cascaded) menu and may contain grayed, disabled, or checked menu items in addition to command items. Menu items should be text only; owner-drawn menus and bitmap menus are not supported. Changing the bitmap for check marks is not supported.

Whenever File Manager calls the FMExtensionProc function, it waits to refresh its directory windows (for changes in the file system) until after the function returns. This allows the extension to perform large numbers of file operations without excessive repainting on the part of File Manager. It is not necessary for the extension to send the FM_REFRESH_WINDOWS message to notify File Manager to repaint its directory windows.

+$#A Loading the Extension

File Manager first sends the FMEVENT_LOAD message to the FMExtensionProc function. The lParam parameter accompanying the FMEVENT_LOAD message points to an FMS_LOAD structure that File Manager uses to obtain information about the extension-supplied menu, including the menu name and menu handle.

File Manager also uses the FMS_LOAD structure to pass the menu-item delta value to the extension. To avoid conflicts with its own menu-item identifiers, File Manager renumbers the menu-item identifiers in an extension-supplied menu by adding the delta value to each identifier. An extension DLL that must modify its menu after File Manager has loaded it must use the delta value. For example, to delete a menu item, the extension DLL finds the sum of the delta value and the menu-item identifier and passes it as the idItem parameter to the DeleteMenu function.

If an extension returns FALSE in response to this message, File Manager calls the FreeLibrary function and ends any communication with the extension DLL. This is the only place where an extension can notify File Manager of initialization problems and thus prevent the extension from being loaded.

+$#A Adding Custom Buttons

A File Manager extension can add custom buttons to the File Manager toolbar by returning button information in response to the FMEVENT_TOOLBARLOAD message. File Manager sends this message to the FMExtensionProc function of the extension only if the extension successfully installed a custom menu during the processing of the FMEVENT_LOAD message. Use custom buttons as accelerator keys for menu commands specified in the custom menu.

When FMExtensionProc receives the FMEVENT_TOOLBARLOAD message, the lParam parameter points to an FMS_TOOLBARLOAD structure. The File Manager extension must copy information about the custom buttons to the structure, including the number of buttons and a specifier for the bitmap containing the button images (either the identifier for a bitmap resource or a handle to a memory bitmap). The extension must also include the address of an array of EXT_BUTTON structures, each of which must contain a command identifier for the corresponding button. If the button is an accelerator for a menu command, the button identifier and corresponding menu-command identifier must be equal.

The bitmap containing the button images must be a bitmap resource or a memory bitmap. The extension must copy either the resource identifier or the memory bitmap's handle to the FMS_TOOLBARLOAD structure; and, in the case of a bitmap resource, the resource must be in the extension's executable file.

The bitmap must contain one image for each specified button. The bitmap height is always 15 pixels; the width is always a multiple of 16 pixels because each button image must be 16 pixels wide. Button images are concatenated from left to right. For example, the image of the first button specified in the array of EXT_BUTTON structures must be the leftmost image in the bitmap.

An extension can provide a line of Help text for a button by specifying the identifier of a string resource in the idsHelp member of the EXT_BUTTON structure. When the user selects the button, File Manager retrieves the string resource and displays it on the toolbar.

+$#A Processing Menu Selections

The menu resource that you define for your extension's menu must use menu-item identifiers in the range 1 through 99. When the user selects an item, the extension receives a command notification that is the identifier of the selected item as defined in the resource-definition file (which has the .RC filename extension). The command notification is not the sum of the delta value and the identifier. An extension DLL's FMExtensionProc function carries out commands by processing command notifications.

+$#A Initializing the Extension Menu

Whenever the user selects the extension's main menu item from File Manager's menu bar, File Manager sends the FMEVENT_INITMENU message to the extension DLL. An extension can use this message to initialize its menu items. For example, an extension can add check marks, disable items, or gray items during this message.

When the user selects submenus within the extension's menu, File Manager does not send the FMEVENT_INITMENU message. An extension DLL must initialize all items at the same time, including those in submenus.

+$#A Updating the Extension Menu

When the user chooses the Refresh command from the Window menu, File Manager sends an FMEVENT_USER_REFRESH message to an extension DLL. The extension can use this opportunity to update its menu items.

+$#A Processing File Selections

When the user selects a filename in the directory window or in the Search Results window, File Manager sends the FMEVENT_SELCHANGE message to an extension DLL. The extension can use this opportunity to send a query message to File Manager to obtain more information about the user's selection.

Because the user can change the selection often, the extension should return promptly after processing the FMEVENT_SELCHANGE message to avoid slowing the user's selection process.

+$#A Quitting the Extension Dynamic-Link Library

When File Manager quits, it sends the FMEVENT_UNLOAD message to each extension DLL and then calls the FreeLibrary function to free the DLLs. Each DLL should free any memory that it has allocated.

+$#A Installing Extensions

File Manager installs extensions that have settings in the [AddOns] section of the WINFILE.INI initialization file. Each setting contains an entry and a value. An entry consists of a string that represents the name of an extension. The value assigned to the entry consists of a string that specifies the path to the extension DLL. An application can use the WritePrivateProfileString function to add a setting to WINFILE.INI. The following example shows a setting in WINFILE.INI.

[AddOns]

MyExtension=C:\NT\SYSTEM\MYEXT.DLL

File Manager does not display an error message if it cannot find an extension DLL, so an extension DLL can be deleted in order to uninstall it. Even so, an application that installs an extension DLL should provide an uninstall option to remove the extension's setting from the WINFILE.INI file.

+$#A Extension Messages

An extension can send the following window messages to retrieve relevant information from File Manager. File Manager is guaranteed to respond correctly only to messages sent from the FMExtensionProc function.

Message | Description
FM_GETDRIVEINFO | File Manager returns drive information from the active window. An extension provides a pointer to an FMS_GETDRIVEINFO structure; File Manager fills the structure with drive information.
FM_GETFILESEL | File Manager returns information about a selected file from the active File Manager window (either the directory window or the Search Results window). An extension provides a pointer to an FMS_GETFILESEL structure; File Manager fills the structure with file information.
FM_GETFILESELLFN | Same as the FM_GETFILESEL message except that the selected file may have a long filename.
FM_GETFOCUS | File Manager returns a value that identifies the type of window with input focus.
FM_GETSELCOUNT | File Manager returns the count of selected files in the directory and Search Results windows.
FM_GETSELCOUNTLFN | Same as the FM_GETSELCOUNT message except that the count includes files with long filenames.
FM_REFRESH_WINDOWS | File Manager repaints either its active window or all of its windows. This message is similar to File Manager's Refresh command on the Window menu.
FM_RELOAD_EXTENSIONS | File Manager reloads all extensions. First, File Manager unloads all extensions, sending an FMEVENT_UNLOAD message to each extension. Then, it reloads the extensions, sending an FMEVENT_LOAD message to each extension. The FM_RELOAD_EXTENSIONS message allows an extension to uninstall itself by removing its setting from the WINFILE.INI file; this action causes File Manager to reload the remaining extensions. Other applications and programs (for example, installation programs) can also post this message by calling the PostMessage function.

+$#A Processing Commands and Messages from File Manager

The following code shows the FMExtensionProc function for a sample extension DLL. It demonstrates how an extension processes the menu commands and notification messages sent by File Manager.

HINSTANCE hinst;

HMENU hmenu;

WORD wMenuDelta;

BOOL fMultiple = FALSE;

BOOL fLFN = FALSE;

LONG WINAPI FMExtensionProc(hwnd, wMsg, lParam)

HWND hwnd;

WORD wMsg;

LONG lParam;

{

CHAR szBuf[200];

INT count;

switch (wMsg) {

case FMEVENT_LOAD:

#define lpload ((LPFMS_LOAD) lParam)

/* Save the menu-item delta value. */

wMenuDelta = lpload->wMenuDelta;

/* Fill the FMS_LOAD structure. */

lpload->dwSize = sizeof(FMS_LOAD);

lstrcpy(lpload->szMenuName, "&Extension");

/* Return the handle of the menu. */

hinst = GetModuleHandle("ext.dll");

lpload->hMenu = GetSubMenu(LoadMenu(hinst,

MAKEINTRESOURCE(MYMENU)), 0);

return (LONG)TRUE;

case FMEVENT_UNLOAD:

/* Perform any cleanup procedures here. */

break;

case FMEVENT_INITMENU:

/* Copy the menu handle. */

hmenu = (HMENU) lParam;

/*

* Add check marks to menu items as appropriate.

* Add menu-item delta values to menu-item

* identifiers to specify the menu items to check.

*/

CheckMenuItem(hmenu,

wMenuDelta + IDM_MULTIPLE,

fMultiple ? MF_BYCOMMAND | MF_CHECKED :

MF_BYCOMMAND | MF_UNCHECKED);

CheckMenuItem(hmenu,

wMenuDelta + IDM_LFN,

fLFN ? MF_BYCOMMAND | MF_CHECKED :

MF_BYCOMMAND | MF_UNCHECKED);

break;

case FMEVENT_TOOLBARLOAD:

{

static EXT_BUTTON extbtn[] = {

{1, 0, 0},

};

/* Fill the FMS_TOOLBARLOAD structure. */

#define lptbld ((LPFMS_TOOLBARLOAD) lParam)

lptbld->dwSize = sizeof(FMS_TOOLBARLOAD);

lptbld->lpButtons = (LPEXT_BUTTON) &extbtn;

lptbld->cButtons = 1;

lptbld->cBitmaps = 1;

lptbld->idBitmap = ID_BUTTONBITMAP;

return (LONG)TRUE;

}

case FMEVENT_USER_REFRESH:

MessageBox(hwnd, "User refresh event",

"Hey!", MB_OK);

break;

case FMEVENT_SELCHANGE:

break;

/*

* The following messages are generated when the user

* chooses items from the extension menu.

*/

case FMEVENT_HELPSTRING:

#define lphs ((LPFMS_HELPSTRING)lParam)

if (lphs->idCommand == -1)

lstrcpy(lphs->szHelp, "Help for extension menu");

else

wsprintf(lphs->szHelp, "Help for item %d", lphs->idCommand);

break;

case FMEVENT_HELPMENUITEM:

wsprintf(szBuf, "Help for %d", lParam);

MessageBox(hwnd, szBuf, "WinHelp call", MB_OK);

/*

* Use: WinHelp(hwnd, "ExtHelp.hlp", HELP_CONTEXT, lParam);

*/

break;

case IDM_GETFOCUS:

wsprintf(szBuf, "Focus %d",

(INT) SendMessage(hwnd, FM_GETFOCUS, 0, 0));

MessageBox(hwnd, szBuf, "Focus", MB_OK);

break;

case IDM_GETCOUNT:

count = (INT) SendMessage(hwnd,

fLFN ? FM_GETSELCOUNTLFN : FM_GETSELCOUNT,

0, 0);

wsprintf(szBuf, "%d files selected", count);

MessageBox(hwnd, szBuf, "Selection Count", MB_OK);

break;

case IDM_GETFILE:

{

FMS_GETFILESEL file;

count = (INT) SendMessage(hwnd,

fLFN ? FM_GETSELCOUNTLFN : FM_GETSELCOUNT,

FMFOCUS_DIR,

0);

while (count >= 1) {

/*

* Selection indices are zero-based (0 is

* first).

*/

count--;

SendMessage(hwnd, FM_GETFILESEL, count,

(LONG) (LPFMS_GETFILESEL) &file);

wsprintf(szBuf, "file %s\nSize %ld",

(LPSTR) file.szName, file.dwSize);

MessageBox(hwnd, szBuf, "File Information",

MB_OK);

if (!fMultiple)

break;

}

break;

}

case IDM_GETDRIVE:

{

FMS_GETDRIVEINFO drive;

SendMessage(hwnd, FM_GETDRIVEINFO, 0,

(LONG) (LPFMS_GETDRIVEINFO)&drive);

wsprintf(szBuf,

"%s\nFree %ld\nTotal %ld\nVolume %s\nShare %s",

(LPSTR) drive.szPath, drive.dwFreeSpace,

drive.dwTotalSpace, (LPSTR) drive.szVolume,

(LPSTR) drive.szShare);

MessageBox(hwnd, szBuf, "Drive Info", MB_OK);

break;

}

case IDM_LFN:

MessageBox(hwnd, "IDM_LFN", "Hi", MB_OK);

fLFN = !fLFN;

break;

case IDM_MULTIPLE:

MessageBox(hwnd, "IDM_MULTIPLE", "Hi", MB_OK);

fMultiple = !fMultiple;

break;

case IDM_REFRESH:

case IDM_REFRESHALL:

SendMessage(hwnd, FM_REFRESH_WINDOWS,

wMsg == IDM_REFRESHALL, 0);

break;

case IDM_RELOAD:

PostMessage(hwnd, FM_RELOAD_EXTENSIONS, 0, 0);

break;

}

return 0L;

}

+$#A Adding the Undelete Command

File Manager supports a hook for adding an Undelete command to the File menu (below the Delete command). If an "undelete" dynamic-link library is specified in the WINFILE.INI file, File Manager adds the Undelete command to the File menu when starting. When the user chooses the Undelete command, File Manager calls the DLL.

The [settings] section of the WINFILE.INI file should include a reference to the undelete DLL, as follows.

[settings]

UNDELETE.DLL=C:\MYDIR\OTHER.DLL

An undelete DLL must include a standard entry point, the UndeleteFile function. This function must be exported by specifying the name of the function in the EXPORTS statement of the DLL's module-definition (.DEF) file.

The UndeleteFile function is defined as follows:

DWORD APIENTRY UndeleteFile(hwndParent, lpszDir)

HWND hwndParent;

LPSTR lpszDir;

The hwndParent parameter identifies the parent window for any dialog boxes the DLL creates. The lpszDir parameter specifies the initial directory to be used (for example, C:\TEMP).

+$#A File Manager Extension Functions

Following are the functions used with File Manager extensions.

FMExtensionProc
UndeleteFile

Créé avec HelpNDoc Personal Edition: Produire des livres électroniques facilement


File Mapping.rtf

+$#AK File Mapping

This Windows 95 overview discusses the file-mapping functions in the Microsoft® Win32® application programming interface (API). These functions allow a process to create file-mapping objects and file views to easily access and share data.

+$#A File Mapping Platform Differences

The overviews for file mapping in Windows 95 and Windows NT are substantially different. To accommodate these differences, the Windows 95 overview is first presented in its entirety, followed by the Windows NT version.

+$#A About File Mapping

{ewl msdncd, EWGraphic, group10747 0 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview About File Mapping.

File mapping is the association of a file's contents to a portion of a process's virtual address space. The operating system creates a file-mapping object to maintain this association. A file view is the portion of virtual address space that is used to access the file's contents. Processes read and write to the file view using pointers just as they would with dynamically allocated memory. Also, the VirtualProtect function operates on file views just like any other virtual memory.

Windows reads data from the file into the file view and writes it back out to the file as needed. In effect, file mapping is a way to tell the system to back a specified part of a process's virtual address space with a specified file or with the system pagefile.

Mapping a file allows a process to access files on disk in the same way as dynamically allocated memory or to share memory between two or more processes. The file-mapping functions in the Microsoft® Win32® application programming interface (API) allow a process to create file-mapping objects and file views to easily access and share data.

+$#A Accessing Files Using the CreateFile Function

The first step in mapping a file is to open the file by calling the CreateFile function. The file should be opened with exclusive access to ensure that other processes cannot write to the portion of the file that is mapped. An easy way to obtain exclusive access is to specify zero in the fdwShareMode parameter of CreateFile.

The CreateFileMapping function uses the handle returned by CreateFile to create a file-mapping object. CreateFileMapping enables you to specify an object name, the number of bytes to be mapped from the file, and the read-write access to the mapped memory. CreateFileMapping fails if the access flags conflict with those specified when the file was opened by using CreateFile. For example, to read and write to the file, specify the GENERIC_READ and GENERIC_WRITE values in the fdwAccess parameter of CreateFile and specify the PAGE_READWRITE value in the fdwProtect parameter of CreateFileMapping.

A file mapping can map a file as large as the file system allows. The dwMaximumSizeHigh and dwMaximumSizeLow parameters of CreateFileMapping allow you to specify the number of bytes to be mapped from the file. The size of the file mapping is independent of the size of the file being mapped.

However, if the mapping is larger than the file, the system grows the file before CreateFileMapping returns. If the file mapping is smaller than the file, only the specified number of bytes from the file is mapped.

When you do not want the size of the file to change (for example, when mapping read-only files), call CreateFileMapping and specify zero for both dwMaximumSizeHigh and dwMaximumSizeLow. Doing this creates a file-mapping object exactly the same size as the file. Otherwise, you must calculate or estimate the size of the finished file because file-mapping objects are static in size; once created they cannot grow or shrink.

To actually map the data from the file to memory, you must create a view of the file. The MapViewOfFile and MapViewOfFileEx functions use the file-mapping object handle returned by CreateFileMapping to create a view (or a copy) of the file or a portion of the file in the process's virtual address space. These functions fail if the access flags conflict with those specified when the file-mapping object was created by CreateFileMapping. MapViewOfFileEx works exactly like MapViewOfFile except that it allows a process to specify the base address of the view of the file in the process's virtual address space.

MapViewOfFile returns a pointer to the file view. An application uses this pointer to read and write data to the file mapping in the same way as it would access dynamic memory. By dereferencing a pointer in the range of addresses specified in MapViewOfFile, an application can read and write data to the file. The actual reading and writing to the file on disk is handled by the system. Data is not actually transferred at the time the file-mapping object is written to. Instead, much of the file input and output (I/O) is cached to improve general system performance. Applications may override this behavior by calling the FlushViewOfFile function to force the system to perform disk transactions immediately.

An application may create multiple file views from the same file-mapping object. A file view can be a different size than the file-mapping object from which it is derived, but it must fit within the file-mapping object. The offset specified by the dwOffsetHigh and dwOffsetLow parameters of MapViewOfFile must be a multiple of the allocation granularity of the system. To obtain the system's memory allocation granularity, use the GetSystemInfo function, which fills in the members of a SYSTEM_INFO structure.

When done with the file-mapping object, the process should destroy all file views in its address space by using the UnmapViewOfFile function for each file view. This function invalidates the pointer to the process's virtual address space. If any of the pages of the file view have changed since the view was mapped, the system writes the changed pages of the file "lazily" to disk. To commit the data to disk immediately, call FlushViewOfFile before unmapping the view.

Both the file-mapping object and the file must be closed by calling CloseHandle to first close the file-mapping object and then the file.

After closing a file-mapping object and unmapping all views, if necessary, a process can adjust the size of the file by using the SetFilePointer and SetEndOfFile functions. For example, if the mapping is larger than the desired size of the file, a process can set the file pointer to the desired size of the file.

+$#A Sharing Files and Memory

File mapping can be used to share a file or memory between two or more processes. To do so, all of the processes must use the same file-mapping object to create identical views of the file or memory. Each process's view resides in the process's own virtual address space. If one process updates its view, the other process sees the update immediately.

To share a file the first process creates or opens a file by using CreateFile. Next, it creates a file-mapping object by using CreateFileMapping and specifying a name for the file-mapping object. To share memory that is not associated with a file, a process must use the CreateFileMapping function and specify (HANDLE)0xFFFFFFFF instead of an existing file handle. The corresponding file-mapping object accesses memory backed by the system paging file. You must specify a size greater than zero when you specify (HANDLE)0xFFFFFFFF in a call to CreateFileMapping.

The easiest way for other processes to obtain a handle of the file-mapping object created by the first process is to use the OpenFileMapping function and specify the object's name. If the file-mapping object does not have a name, the process must obtain a handle to it through inheritance or duplication. For more information on inheritance and duplication, see Processes and Threads, and Handles and Objects.

Processes that share files or memory must create file views by using the MapViewOfFile or MapViewOfFileEx function just as they would when accessing a file. They must coordinate their access using semaphores, mutexes, events, or some other mutual exclusion technique. For more information on synchronization, see Synchronization.

A shared file-mapping object will not be destroyed until all processes that use it close their handles to it by using CloseHandle and call UnMapViewOfFile on all file views.

+$#A Coherence

Coherence guarantees that the data accessible in a file view is an identical copy of the file's contents on disk. Although the CreateFileMapping function works with remote files, it does not keep them coherent. For example, if two computers both map a file as writable, and both change the same page, each computer will see only its own write operations to the page. When the data gets updated on the disk, it is not merged.

+$#A Using File Mapping

{ewl msdncd, EWGraphic, group10747 1 /a "SDK.BMP"} Windows 95 Overview
For more information, see the Windows NT overview Using File Mapping.

The code samples in this guide illustrate how two processes might access an existing file as named shared memory.

As discussed earlier, the processes must synchronize their access to the memory. For more information on synchronization, see Synchronization.

In the following code, the first process calls CreateFileMapping function to create a file-mapping object. When a corresponding file view is created, the process has read-write access to the memory.

hMapFile = CreateFileMapping(hFile, // current file handle

NULL, // default security

PAGE_READWRITE, // read-write access

0, // max. object size

0, // size of hFile

"MyFileMappingObject"); // name of map. object

if (hMapFile == NULL) {

ErrorHandler("Could not create file-mapping object.");

}

The following code uses the file-mapping object handle returned by CreateFileMapping to create a view of the file in the process's address space.

lpMapAddress = MapViewOfFile(hMapFile, // handle to map. object

FILE_MAP_ALL_ACCESS, // read-write access

0, // max. object size

0, // size of hFile

0); // map entire file

if (lpMapAddress == NULL) {

ErrorHandler("Could not map view of file.");

}

The MapViewOfFile function returns a pointer to the file view. The process can use the pointer to access the memory for whatever it needs to do.

In the next example, a second process uses the OpenFileMapping function to share the same memory. As in the original process, the MapViewOfFile function returns a pointer to the file view.

hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, // r-w access

FALSE, // do not inherit

"MyFileMappingObject"); // name of mapping obj.

if (hMapFile == NULL) {

ErrorHandler("Could not open file-mapping object.");

}

lpMapAddress = MapViewOfFile(hMapFile, // handle to map. obj.

FILE_MAP_ALL_ACCESS, // read-write access

0, // max. object size

0, // size of hFile

0); // map entire file

if (lpMapAddress == NULL) {

ErrorHandler("Could not map view of file.");

}

Note that MapViewOfFile may return different addresses in different processes. To specify the location of the file view within a process's address space, use the MapViewOfFileEx function rather than MapViewOfFile.

The following example uses the UnmapViewOfFile function to invalidate the pointer to the mapped memory. This destroys the file view in the process's address space. If any pages of the file view have changed since the file view was mapped, UnmapViewOfFile also copies the changed pages to the file on disk.

if (!UnmapViewOfFile(lpMapAddress)) {

ErrorHandler("Could not unmap view of file.");

}

When a process is finished using a file mapping and has umapped all views, it must close the file mapping object's handle by calling CloseHandle.

CloseHande(hMapFile);

The FlushViewOfFile function copies the specified number of bytes of the file view to the physical file but does not destroy the file view:

if (!FlushViewOfFile(lpMapAddress, dwBytesToFlush)) {

ErrorHandler("Could not flush memory to disk.");

}

+$#A File Mapping Functions

The following functions are used with file mapping.

CreateFileMapping
FlushViewOfFile
MapViewOfFile
MapViewOfFileEx
OpenFileMapping
UnmapViewOfFile

+$#AK File Mapping (Windows NT)

This Windows NT™ overview discusses the file-mapping functions in the Microsoft® Win32® application programming interface (API). These functions allow a process to create file-mapping objects and file views to easily access and share data.

+$#A About File Mapping

{ewl msdncd, EWGraphic, group10748 0 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview About File Mapping.

File mapping is the copying of a file's contents to a process's virtual address space. The copy of the file's contents is called the file view, and the internal structure the operating system uses to maintain the copy is called the file-mapping object.

Another process can create an identical file view in its own virtual address space by using the first process's file-mapping object to create the view. This enables the processes to share data. Any process that has the name or a handle of a file-mapping object can create a file view.

Microsoft® Windows NT™ file-mapping functions create file-mapping objects and file views necessary for this method of data sharing.

+$#A Sharing a File

If two (or more) processes need to share a file, one process should open the file for exclusive access by using the CreateFile function. The CreateFileMapping function uses the handle returned by CreateFile to create a file-mapping object. Processes that have the name or a handle of the file-mapping object can share the file's data. The file handle should remain open until the processes are no longer sharing the file; this prevents nonsharing processes from reading from or writing to the file.

CreateFileMapping enables you to specify an object name, the number of bytes to be mapped from the file, and the read-and-write access to the mapped memory. CreateFileMapping fails if the access flags conflict with those specified when the file was opened by using CreateFile. CreateFileMapping returns a handle of the file-mapping object. This handle is used to create a "view of the file," or "file view," in the process's virtual address space. A file view is a copy of the entire file or a portion of the file, depending on the number of bytes requested.

The process that created the file-mapping object uses the MapViewOfFile function with the file-mapping object handle to create a file view, or copy of the file, in the process's virtual address space. MapViewOfFile returns a pointer to the file view. This function fails if the access flags conflict with those specified when the mapping object was created with CreateFileMapping.

To share the data in the file view, another process must create an identical file view in its own virtual address space. To do this, the process must first obtain a handle of the same file-mapping object and then create its own file view by using MapViewOfFile. The easiest way to obtain a handle of the file-mapping object is to use the OpenFileMapping function and specify the object's name. If the file-mapping object does not have a name, the process must obtain a handle of it through inheritance or duplication. For more information on inheritance and duplication, see Processes and Threads and Handles and Objects.

MapViewOfFile returns a pointer to the file view. To ensure that multiple processes are sharing the same data through their own pointers, all views of a file must be derived from the same file-mapping object. If this is the case, the file views are said to be coherent, or identical. Typically, file views are not coherent if they are derived from different file-mapping objects, even if the mapping objects refer to the same file.

Because the file view is basically a block of memory and not a file, processes read from and write to the file view by using pointers just as though the block were dynamically allocated. You do not need to use the ReadProcessMemory and WriteProcessMemory functions.

If one or more of the processes has write access to the shared data, processes must synchronize their access to the shared data by using a semaphore. This ensures that the file views remain coherent in all processes. For more information on synchronization, see Synchronization.

Some applications require that a file be mapped to the same address in multiple processes. To place the file view at a specific location within the process's address space, you should use the MapViewOfFileEx function and specify the desired address. If there is not enough space at the specified address, the function fails.

Although CreateFileMapping works with remote files, it does not keep them coherent. For example, if two machines both map a file as writable, and both change the same page, each machine will only see its own writes to the page. When the data gets updated on the disk, it is not merged.

+$#A Sharing a Memory Block

If two (or more) processes need to share a memory block not associated with a file, one process must use the CreateFileMapping function and specify (HANDLE)0xFFFFFFFF instead of an existing file handle. The corresponding file-mapping object accesses memory from the operating-system paging file. Processes sharing the operating-system paging file must create file views by using the MapViewOfFile or MapViewOfFileEx function just as when sharing a named file.

+$#A Closing a File View

When a process has finished using the shared memory, it should destroy the file view in its address space by using the UnmapViewOfFile function. This invalidates the pointer to the shared memory. If any of the pages of the file view changed since the view was mapped, this function copies the changed pages to the physical file.

To copy a specified number of bytes from the file view to the physical file, you should use the FlushViewOfFile function. FlushViewOfFile does not destroy the file view.

For information on other ways to share data among applications, see Dynamic Data Exchange Management Library.

+$#A Using File Mapping

{ewl msdncd, EWGraphic, group10748 1 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Using File Mapping.

The code samples in this guide illustrate how two processes might access an existing file as named shared memory.

If one or both processes write to the shared memory, the processes must use a semaphore to synchronize their access to the memory. For more information on synchronization, see Synchronization.

In the following code, the CreateFileMapping function creates a file-mapping object. When a corresponding file view is created, the process has read-and-write access to the memory.

hMapFile = CreateFileMapping(hFile, /* current file handle */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

PAGE_READWRITE, /* read-write access */

0, /* max. object size is */

0, /* size of hFile */

"MyFileMappingObject"); /* name of map. object */

if (hMapFile == NULL) {

ErrorHandler("Could not create file-mapping object.");

}

The following code uses the file-mapping object handle returned by CreateFileMapping to create a view of the file in the process's address space.

lpMapAddress = MapViewOfFile(hMapFile, /* handle to map. object */

FILE_MAP_ALL_ACCESS, /* read-write access */

0, /* max. object size is */

0, /* size of hFile */

0); /* map entire file */

if (lpMapAddress == NULL) {

ErrorHandler("Could not map view of file.");

}

The MapViewOfFile function returns a pointer to the file view. The process can use the pointer to access the memory for whatever it needs to do.

In the next example, a second process uses the OpenFileMapping function to share the same memory. As in the original process, the MapViewOfFile function returns a pointer to the file view.

hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, /* r-w access */

FALSE, /* do not inherit */

"MyFileMappingObject"); /* name of mapping obj. */

if (hMapFile == NULL) {

ErrorHandler("Could not open file-mapping object.");

}

lpMapAddress = MapViewOfFile(hMapFile, /* hndl. to map. obj. */

FILE_MAP_ALL_ACCESS, /* read-write access */

0, /* max. object size is */

0, /* size of hFile */

0); /* map entire file */

if (lpMapAddress == NULL) {

ErrorHandler("Could not map view of file.");

}

Note that MapViewOfFile returns different addresses in different processes. To specify the location of the file view within a process's address space, use the MapViewOfFileEx function, rather than MapViewOfFile.

The following example uses the UnmapViewOfFile function to invalidate the pointer to the mapped memory. This destroys the file view in the process's address space. If any pages of the file view changed since the view was mapped, UnmapViewOfFile also copies the changed pages to the physical file.

if (!UnmapViewOfFile(lpMapAddress)) {

ErrorHandler("Could not unmap view of file.");

}

The FlushViewOfFile function copies the specified number of bytes of the file view to the physical file but does not destroy the file view:

if (!FlushViewOfFile(lpMapAddress, dwBytesToFlush)) {

ErrorHandler("Could not flush memory to disk.");

}

+$#A File Mapping Functions

The following functions are used with file mapping.

CreateFileMapping
FlushViewOfFile
MapViewOfFile
MapViewOfFileEx
OpenFileMapping
UnmapViewOfFile

Créé avec HelpNDoc Personal Edition: Générateur complet de livres électroniques Kindle


File Systems.rtf

+$#AK File Systems

Applications based on the Microsoft® Win32® application programming interface (API) rely on file systems to store and retrieve information on mass storage devices. File systems provide the underlying support that applications need to create and access files and directories on the individual volumes associated with the devices.

+$#A About File Systems

Depending on the configuration of a computer, a Win32-based application may have access to volumes managed by any of the following file systems:

• New Technology file system (NTFS)

• File allocation table (FAT) file system

• Protected-mode FAT file system

• High-performance file system (HPFS)

For example, applications that connect to network drives may encounter file systems that range from FAT to NTFS. Because different volumes may be managed by different file systems, it is important to understand the differences between file systems and to prepare applications to work effectively with all available file systems. The following sections describe these differences and present guidelines for handling them within applications.

+$#A Guidelines

Each file system consists of one or more drivers and supporting dynamic-link libraries that define the data formats and features of the file system. These determine the conventions used for filenames, the level of security and recoverability available, and the general performance of input and output (I/O) operations. Before you access files and directories on a given volume, you should determine the capabilities of the file system by using the GetVolumeInformation function. The function returns values, such as the maximum length of filenames, that you can use to adapt your application to work effectively with the file system.

In general, you should always avoid using static buffers for filenames and paths. Instead, use the values returned by GetVolumeInformation to allocate buffers as you need them. If you must use static buffers, reserve 256 characters for filenames and 260 characters for paths. These are the maximums currently recommended for Win32-based applications.

+$#A The New Technology File System

The New Technology file system (NTFS) organizes data on fixed disks. This file system supports object-oriented applications by treating all files as objects with user- and system-defined attributes. NTFS provides all the capabilities of the FAT and HPFS file systems without many of their limitations. Accessing files under NTFS is often faster than accessing similar files under the FAT or HPFS file systems.

NTFS is a fully recoverable file system. It is designed to restore consistency to a disk after a CPU failure, system crash, or I/O error. NTFS allows the operating system to recover without your having to use disk-checking utilities. However, NTFS provides these utilities in case recovery fails or corruption occurs outside the control of the file system. NTFS also includes features not present in HPFS or FAT, such as security, Unicode filenames, automatic creation of MS-DOS® aliases, multiple data streams, and unique functionality specific to the POSIX subsystem. For more information about security, see Security.

NTFS follows the filename conventions described in the HPFS section, but it also supports Unicode filenames.

NTFS cannot manipulate a file's extended attributes if the file was created on HPFS.

+$#A The File Allocation Table File System

The FAT file system organizes data on fixed disks and floppy disks. The distinguishing feature of the FAT file system is its filename convention. The filename convention consists of a filename (up to eight characters), a separating period (.), and a filename extension (up to three characters).

The main advantage of FAT volumes is that they are accessible by MS-DOS, Microsoft® Windows®, and OS/2 systems. FAT is also the only file system currently supported on floppy disks and other removable media.

Valid FAT filenames have the following form:

[[drive:]][[directory\]]filename[[.extension]]

The drive parameter must name an existing drive and can be any letter from A through Z. The drive letter must be followed by a colon (:).

The directory parameter specifies the directory that contains the file's directory entry. This value must be followed by a backslash (\) to separate it from the filename. If the specified directory is not in the current directory, directory must include the names of all directories in the file's path, separated by backslashes. The root directory is specified by using a backslash at the beginning of the name. For example, if the directory ABCD is in the directory SAMPLE and SAMPLE is in the root directory, the correct directory specification is \SAMPLE\ABCD. A directory name consists of any combination of up to eight letters, digits, or the following special characters:

$ % ' − _ @ { } ~ ` ! # ( )

A directory name can also have an extension that is any combination of up to three letters, digits, or special characters, preceded by a period (.).

The filename and extension parameters specify the file. Filename can be any combination of up to eight letters, digits, or the special characters previously listed; extension can be any combination of up to three letters, digits, or special characters, all preceded by a period. Filename can also include embedded (preceded and followed by one or more letters, digits, or special characters just noted) spaces. For example, the string "disk 1" is a legal value for filename.

FAT volumes do not distinguish between uppercase and lowercase letters.

+$#A The Protected-Mode FAT File System

The protected-mode FAT file system organizes data on fixed and floppy disks. Protected-mode FAT is compatible with the FAT file system, using file allocation tables and directory entries to store information about the contents of a disk. Protected-mode FAT also supports long filenames, storing these names and other information such as the date and time the file was last accessed in the FAT structures.

Protected-mode FAT allows filenames of up to 255 characters, including the terminating null character. This is similar to the New Technology file system (NTFS) which allows filenames of up to 256 characters.

Protected-mode FAT allows paths of up to 260 characters, including the terminating null character.

+$#A The High-Performance File System

HPFS organizes data on fixed disks. Filenames under HPFS can be any practical length (up to 254 characters) and can contain characters that are not valid for the FAT file system, such as spaces and periods. In many cases, accessing files under HPFS is faster than accessing similar files under the FAT file system.

Blank spaces can be used anywhere in an HPFS filename or directory name, but blank spaces and periods at the end of a filename are ignored. There is no requirement that HPFS filenames have extensions; however, many applications still create and use them.

The following special characters can also be used in HPFS filenames:

, + = [ ] ; _

An HPFS filename can be all uppercase, all lowercase, or mixed case. The case is preserved for directory listings but is ignored in file searches and all other system operations. Therefore, in a given directory, there cannot be more than one file with the same name when the only difference is case.

Créé avec HelpNDoc Personal Edition: Générateur d'aide complet


Files.rtf

+$#AK Files

A file is the basic unit of storage that enables a computer to distinguish one set of information from another.

+$#A About Files

In Microsoft® Windows®, files are stored on storage media, such as disks or tapes, and can be organized into groups called directories. The Windows file input and output (I/O) functions enable applications to create, open, modify, and delete files and directories. They also enable applications to obtain system information, such as what disk drives are present.

+$#A System Organization

A volume is a storage device, such as a fixed disk or floppy disk, formatted to store directories and files. Each volume has a root directory. Directories and files on the volume are organized in a tree structure that starts at the root directory. Each directory entry identifies the name, attributes, location, and size of the file or subdirectory.

A large volume can be divided into more than one logical volume, also called a partition. To the user and to the operating system, each partition appears to be a separate volume.

A file system is operating system software that manages the low-level organization of files on a volume. Windows supports the following file systems:

• File allocation table (FAT)

• High-performance file system (HPFS)

• New Technology file system (NTFS)

The type of file system defines the filename conventions on a volume and may also provide specific file system features, such as security, recoverability, and high I/O performance. Each volume can use a different file system.

For more information on the FAT, HPFS, and NTFS file systems, see File Systems.

+$#A Accessing Files

The first time a file function accesses a volume and whenever a volume is placed in a floppy-disk drive, Windows examines the volume to determine the appropriate file system. Thereafter, Windows manages all I/O to that volume through the device driver supporting the file system. If an error occurs, the driver returns it to Windows, which then passes it back to the application.

The Windows file functions enable applications to access files regardless of the underlying file system. However, capabilities may vary depending on the file system and/or operating system in use. For example, the CreateFile function includes a security parameter that provides no security benefits for an application written for Windows running with current versions of MS-DOS.

The Windows file functions that create, open, and delete files and directories identify them by their names. These functions store or search for the file or directory in the current directory on the current disk drive, unless the name explicitly specifies a path to a different directory, disk drive, or both.

+$#A Filename Conventions

Although each file system can have specific rules about the formation of individual components in a directory or filename, all file systems follow the same general conventions for combining components. For example, the FAT file system requires file and directory names to have the 8.3-character filename format, the HPFS file system allows names to be up to 254 characters, and the NTFS file system allows names to be up to 255 characters. All three file systems use the backslash (\) character to separate directory names and the filename when forming a path.

General rules for applications creating names for directories and files or processing names supplied by the user include the following:

• Use any character in the current code page for a name, but do not use a path separator, a character in the range 0 through 31, or any character explicitly disallowed by the file system. A name can contain characters in the extended character set (128−255).

• Use the backslash (\), the forward slash (/), or both to separate components in a path. No other character is acceptable as a path separator.

• Use a period (.) as a directory component in a path to represent the current directory.

• Use two consecutive periods (..) as a directory component in a path to represent the parent of the current directory.

• Use a period (.) to separate components in a directory name or filename.

• Do not use the following characters in directory names or filenames, because they are reserved for Windows:

< > : " / \ |

• Do not use reserved words, such as aux, con, and prn, as filenames or directory names.

• Process a path as a null-terminated string. The maximum length for a path is given by MAX_PATH.

The wide (Unicode) versions of the CreateDirectory, FindFirstFile, GetFileAttributes, and SetFileAttributes functions permit paths that exceed the MAX_PATH length if the path has the "\\?\" or "\\?\UNC\" prefix. These prefixes direct the functions to turn off path parsing. Use the "\\?\" prefix with paths for local storage devices and the "\\?\UNC\" prefix with paths having the Universal Naming Convention format.

• Do not assume case sensitivity. Consider names such as OSCAR, Oscar, and oscar to be the same.

By following the rules listed in this section, an application can create valid names for files and directories regardless of the file system in use.

Long Filenames on Windows NT FAT Partitions

You can create or open long filenames on Windows NT FAT partitions.

The operating system stores the long filenames on disk in Unicode. This means that the original long file name is always preserved, even if it contains extended characters, and regardless of the code page that is active during a disk read or write operation.

The legal character set for these long filenames is the NTFS legal character set, less one character: the colon (':') that NTFS uses for opening alternate file streams. This means that you can freely copy files between NTFS and FAT partitions without losing any file name information.

The operating system stores the long filenames on disk masquerading as extra volume label directory entries. These entries are not available from the DOS dir command, but they are available from the Windows NT dir command. When you create a long filename on a Windows NT FAT partition, the operating system creates a corresponding short 8.3 form of the name, just as it does on an NTFS partition.

+$#A File Operations

This section describes the file I/O operations and operating system information provided by the file functions in the Microsoft® Win32® application programming interface (API).

+$#A Creating and Opening Files with the CreateFile Function

The CreateFile function can create a new file or open an existing file. When an application uses this function, it must specify whether it will read from the file, write to the file, or both. The application must also specify what action to take whether or not the file exists. For example, an application can specify that CreateFile always be used to create the file. As a result, the function creates the file if it does not exist and overwrites the file if it does exist.

CreateFile also enables an application to specify whether it wants to share the file for reading, writing, both, or neither. A file that is not shared cannot be opened more than once by the first application nor by another application until the first application has closed.

Windows assigns a unique identifier, called a file handle, to each file that is opened or created. An application can use the file handle in functions that read from, write to, and describe the file. It is valid until the file is closed. When an application starts, it inherits all open file handles from the process that started it, if the handles are inheritable. For more information about processes, see Processes and Threads.

For information about the standard input, standard output, and standard error file handles, see Consoles and Character-Mode Support.

An application should check the return value of CreateFile before attempting to use the handle to access the file. If an error occurs, the application can use the GetLastError function to get extended error information.

+$#A Closing and Deleting Files

To use operating system resources efficiently, an application should close files when they are no longer needed by using the CloseHandle function. If a file is open when an application terminates, Windows closes it automatically.

The DeleteFile function can be used to delete a file. The file must, however, be closed before any attempt to delete it.

+$#A Copying and Moving Files

Before a file can be copied, it must be closed or opened only for reading. No thread can have the file opened for writing. To copy an existing file to a new one, use the CopyFile function. Applications can specify whether CopyFile fails if the new file already exists.

A file must also be closed before an application can move it. The MoveFile function copies an existing file to a new location and deletes the original. Applications frequently use this function to rename files.

+$#A Reading from and Writing to a File

Every open file has a file pointer that specifies the next byte to be read or the location to receive the next byte written. When a file is opened for the first time, Windows places the file pointer at the beginning of the file. As each byte is read or written, Windows advances the file pointer. An application can also move the file pointer by using the SetFilePointer function.

An application reads from and writes to a file by using the ReadFile and WriteFile functions. These functions require a handle of a file to be opened for reading and writing, respectively. ReadFile and WriteFile read and write a specified number of bytes at the location indicated by the file pointer. The data is read and written exactly as given; the functions do not format the data.

When the file pointer reaches the end of a file and the application attempts to read from the file, no error occurs, but no bytes are read. Therefore, reading zero bytes without an error means the program has reached the end of the file.

An application can truncate or extend a file by using the SetEndOfFile function. This function sets the end of file to the current position of the file pointer. A WriteFile of zero bytes does nothing.

When an application writes to a file, Windows usually collects the data being written in an internal buffer and writes the data to the disk on a regular basis.

An application can force the operating system to write the contents of the buffer to the disk by using the FlushFileBuffers function. Alternatively, an application can specify that write operations are to bypass the internal buffer and write directly to the disk by setting a flag when the file is created or opened by using the CreateFile function.

If there is data in the internal buffer when the file is closed, Windows does not automatically write the contents of the buffer to the disk before closing the file. If the application does not force the operating system to write the buffer to disk before closing the file, the caching algorithm determines when the buffer is written.

Locking and Unlocking Files

Although Windows allows more than one application to open a file and write to it, applications must not write over each other's work. An application can prevent this problem by temporarily locking a region in a file. The LockFile function locks a specified range of bytes in a file. The range may extend beyond the current end of the file. Locking part of a file prevents all other processes from reading or writing anywhere in the specified area. Attempts to read from or write to a region locked by another process always fail. The application unlocks the region by using the UnlockFile function. An application should unlock all locked areas before closing a file.

Synchronizing Input and Output

Asynchronous input and output is a type of I/O that allows some I/O functions to return immediately, even though an I/O request is still pending. Asynchronous I/O enables an application to continue with other processing and wait for the I/O to be completed at a later time. Asynchronous I/O is also called overlapped I/O.

The ReadFile and WriteFile functions enable an application to specify an OVERLAPPED structure that indicates where to position the file pointer before the read or write operation. The handle of the file being read from or written to must have been opened with the FILE_FLAG_OVERLAPPED flag. The OVERLAPPED structure can also contain an event handle on which the WaitForSingleObject or WaitForMultipleObjects function can wait.

An application can also wait on the file handle to synchronize the completion of an I/O operation, but doing so requires extreme caution. Each time an I/O operation is started, the operating system sets the file handle to the nonsignaled state. Each time an I/O operation is completed, the operating system sets the file handle to the signaled state. Therefore, if an application starts two I/O operations and waits on the file handle, there is no way to determine which operation is finished when the handle is set to the signaled state. If an application must perform multiple asynchronous I/O operations on a single file, it should wait on the event handle in the OVERLAPPED structure for each I/O operation, rather than on the file handle.

The ReadFileEx and WriteFileEx functions enable an application to specify a routine to execute when the asynchronous I/O request is completed.

For more information about synchronization, see Synchronization.

+$#A Searching for Files

An application can search the current directory for all filenames that match a given pattern by using the FindFirstFile, FindNextFile, and FindClose functions. The pattern must be a valid filename and can include wildcard characters.

FindFirstFile creates a handle that FindNextFile uses to search for other files with the same pattern. Both functions also return information about the file that was found in a WIN32_FIND_DATA structure. This information includes the filename, size, attributes, and time. The FindClose function destroys the handle created by FindFirstFile.

An application can search for a single file on a specific path by using the SearchPath function.

+$#A Creating Temporary Files

Applications can create temporary filenames with the GetTempFileName function. The GetTempPath function retrieves the path to the directory where temporary files are to be created.

+$#A Getting Information About Files

Nine characteristics called attributes may be associated with a file. The attributes can be one or more of the following values.

Value | Meaning
FILE_ATTRIBUTE_ARCHIVE | The file is an archive file. Applications use this value to mark files for backup or removal.
FILE_ATTRIBUTE_DIRECTORY | The file is a directory.
FILE_ATTRIBUTE_HIDDEN | The file is hidden. It is not included in an ordinary directory listing.
FILE_ATTRIBUTE_NORMAL | The file has no other attributes. This value is valid only if used alone.
FILE_ATTRIBUTE_READONLY | The file is read-only. Applications can read from the file but cannot write to it or delete it.
FILE_ATTRIBUTE_SYSTEM | The file is part of the operating system or is used exclusively by it.
FILE_ATTRIBUTE_TEMPORARY | The file is being used for temporary storage. Applications can write to the file only if absolutely necessary. Most of the file's data remains in memory without being flushed to the media, because the file will soon be deleted.
FILE_ATTRIBUTE_ATOMIC_WRITE | Reserved; do not use.
FILE_ATTRIBUTE_XACTION_WRITE | Reserved; do not use.

An application can retrieve and set a file's attributes by using the GetFileAttributes and SetFileAttributes functions. Applications cannot, however, use SetFileAttributes to set the directory attribute.

The GetFileType function returns the type of a file: disk, character (such as a console), pipe, or unknown. The GetFileSize function returns the size of a file. Applications can retrieve the full path of a file by using the GetFullPathName function.

Applications can retrieve and set the date and time a file was created, last modified, or last accessed by using the GetFileTime and SetFileTime functions. For more information about file times, see Time.

+$#A Directory Operations

When an application creates a new file, the operating system adds it to the specified directory. Each directory can have any number of files, up to the physical limit of the disk. An application can create new directories and delete existing directories by using the CreateDirectory and RemoveDirectory functions. An application cannot delete a directory unless it is empty.

The directory at the end of the active path is called the current directory; it is the directory in which the active application started, unless explicitly changed. An application can determine which directory is current by using the GetCurrentDirectory function. An application can change the current directory by using the SetCurrentDirectory function.

On the Windows NT platform, you can obtain a handle to a directory by calling the CreateFile function with the FILE_FLAG_BACKUP_SEMANTICS flag set. You can pass a directory handle to some functions in place of a file handle.

An application can monitor the contents of a directory and its subdirectories by using the FindFirstChangeNotification, FindNextChangeNotification, and FindCloseChangeNotification functions. Waiting for a change notification is similar to having a read operation pending against a directory and, if necessary, its subdirectories. When something changes within the directory being watched, the read operation is completed. For example, an application can use these functions to update a directory listing whenever a filename within the monitored directory changes.

An application can specify a set of conditions that trigger a change notification by using the FindFirstChangeNotification function. The conditions include changes to filenames, directory names, attributes, file size, time of last write, and security. This function also returns a handle that can be waited on by using the WaitForSingleObject or WaitForMultipleObjects function. If the wait condition is satisfied, FindNextChangeNotification can be used to provide a notification handle to wait on subsequent changes.

The FindCloseChangeNotification function closes the notification handle.

+$#A Getting System Information

Windows provides several functions that applications can use to obtain information about the file system, operating system, and hardware.

The GetVolumeInformation function retrieves information about the file system on a given volume. This information includes the volume name, volume serial number, file system name, file system flags, maximum length of a filename, and so on.

The GetSystemDirectory and GetWindowsDirectory functions retrieve the paths to the Windows system directory and the Windows directory, respectively.

The GetDiskFreeSpace function retrieves organizational information about a disk, including the number of bytes per sector, the number of sectors per cluster, the number of free clusters, and the total number of clusters.

The GetDriveType function indicates whether the disk referenced by the specified disk drive is a removable, fixed, CD-ROM, RAM, or network drive.

The GetLogicalDrives function identifies the drives currently present on the operating system. The GetLogicalDriveStrings function retrieves a null-terminated string for each drive present on the operating system. Use these strings whenever a root directory is required.

+$#A File Compression

Windows NT release 3.51 supports file compression on an individual file basis for volumes that use the NTFS file system.

+$#A Compression Attribute

On volumes that use the NTFS file system, each file and directory has a compression attribute. Other file systems may also implement a compression attribute for individual files and directories.

You can determine whether a file system supports a compression attribute for files and directories by calling the GetVolumeInformation function and examining the FS_FILE_COMPRESSION bit flag.

If a file's compression attribute is set, all of the data in the file is compressed. If the attribute is clear, none of the data in the file is compressed. There is no partially compressed state. The compression attribute is a simple Boolean indicator of compression state.

A directory's compression attribute provides a default compression attribute for newly created files and subdirectories. When you call CreateFile or CreateDirectory to create a new file or directory, the new file or directory inherits the compression attribute of its parent directory.

+$#A Compression State

Each file and directory on a volume that supports compression for individual files and directories has a compression state. Windows NT release 3.51 provides such support for volumes that use the NTFS file system.

Whereas the compression attribute of a file or directory indicates simply whether the file or directory is compressed or not compressed, the compression state also specifies the format of any compressed data.

Compression state is encoded as a 16-bit value. A compression state value of zero indicates that a file is not compressed. A value of one indicates that a file is compressed, using the default compression format for its parent directory. Any other value indicates that a file is compressed, using the compression format encoded by the compression state value.

For directories, the compression state provides a default compression state for newly created files and subdirectories. In other words, new files and directories inherit the compression state of their parent directory.

+$#A Determining Compression Attribute and State

Use the GetFileAttributes function to determine the compression attribute of a file or directory.

Use the FSCTL_GET_COMPRESSION DeviceIoControl operation to determine the compression state of a file or directory.

+$#A Setting Compression Attribute and State

On volumes whose file system supports the compression of files or directories, such as NTFS volumes under Windows NT release 3.51, new files and subdirectories inherit the compression attribute and compression state of their parent directory.

Use the FSCTL_SET_COMPRESSION DeviceIoControl operation to set the compression state of a file or directory. This operation also sets the compression attribute of the file or directory.

Setting the compression state of a file to a nonzero value compresses the file, using the compression format encoded by the compression state value. Setting a file's compression state to zero decompresses the file. These are synchronous operations. The file is compressed or decompressed immediately when you set its compression state.

Setting a directory's compression state does not cause any immediate compression or decompression. Instead, setting a directory's compression state sets a default compression state that will be given to all newly created files and subdirectories.

+$#A Obtaining the Size of a Compressed File

Use the GetCompressedFileSize function to obtain the compressed size of a file. If the file is compressed, its compressed size will be less than its uncompressed size. Use the GetFileSize).function to determine the uncompressed size of a file.

+$#A Using Files

The Windows file functions enable applications to access files regardless of the underlying file system. However, their functionality may vary depending on the operating system in use. If you have questions about a specific function, consult the reference information for that function.

For functions that manipulate files, the filenames may be relative to the current directory. A filename is relative to the current directory if it does not begin with a disk designator or directory name separator, such as a backslash (\). If the filename begins with a disk designator, it is a fully qualified path.

The examples in this section illustrate how to perform the following tasks:

• Creating and opening files

• Reading, writing, and locking files

• Creating and using a temporary file

• Searching for files and changing file attributes

• Monitoring changes in a directory or directory tree

• Testing for the end of the file

• Retrieving a file pointer

+$#A Creating and Opening Files

You can use the CreateFile function to create a new file or open an existing file. You must specify the filename, preferred access, share mode, creation instructions, and attributes.

The following example returns a handle of the current console input buffer.

HANDLE hReadWriteInput;

hReadWriteInput = CreateFile("CONIN$",

GENERIC_READ | GENERIC_WRITE,

FILE_SHARE_READ | FILE_SHARE_WRITE, /* share with cmd. */

NULL,

OPEN_EXISTING, /* open already created console */

0, /* normal file open */

NULL

);

For more information about consoles, see Consoles and Character-Mode Support.

In the following example, CreateFile opens an existing file for reading.

HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", /* open MYFILE.TXT */

GENERIC_READ, /* open for reading */

FILE_SHARE_READ, /* share for reading */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

OPEN_EXISTING, /* existing file only */

FILE_ATTRIBUTE_NORMAL, /* normal file */

(HANDLE) NULL); /* no attr. template */

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open file."); /* process error */

}

In this example, CreateFile succeeds only if a file named MYFILE.TXT already exists in the current directory. An application should check the return value of CreateFile before attempting to use the handle to access the file. If an error occurs, the application should use the GetLastError function to get extended error information and respond accordingly.

A file must be closed before it can be deleted. The following lines close and delete the MYFILE.TXT file.

CloseHandle(hFile);

DeleteFile("MYFILE.TXT");

In the following example, CreateFile creates a new file and opens it for writing.

HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", /* create MYFILE.TXT */

GENERIC_WRITE, /* open for writing */

0, /* do not share */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

CREATE_ALWAYS, /* overwrite existing */

FILE_ATTRIBUTE_NORMAL | /* normal file */

FILE_FLAG_OVERLAPPED, /* asynchronous I/O */

(HANDLE) NULL); /* no attr. template */

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open file."); /* process error */

}

In addition to the standard attributes (read only, hidden, system, and so on), you can also specify security attributes by including a pointer to a SECURITY_ATTRIBUTES structure as the fourth parameter. However, the underlying operating system must support security for these attributes for them to have any effect. For more information about security, see Security.

+$#A Reading, Writing, and Locking Files

The ReadFile function requires a file handle that is open for reading, or reading and writing. ReadFile copies a specified number of bytes, up to the end of the file, to a specified buffer. It returns the actual number of bytes read in a variable specified by its fourth parameter.

The WriteFile function requires a file handle that is open for writing, or writing and reading. WriteFile copies a specified number of bytes, up to the end of the buffer, to a specified file. It returns the actual number of bytes written in a variable specified by its fourth parameter.

The following example illustrates a possible flow for using callback completion asynchronous I/O.

Completion Routine:

VOID IoCompletionRoutine(DWORD dwErrorCode,

DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)

{

/* If an I/O error occurred, display the error and then exit. */

if (dwErrorCode) {

printf("FATAL I/O Error %ld I/O Context %lx.%lx\n",

dwErrorCode, lpOverlapped, lpOverlapped->hEvent);

ExitProcess(dwErrorCode);

}

LocalFree(lpOverlapped);

}

Main Thread:

VOID IoWorkerThread(VOID)

{

HANDLE HandleVector[2];

DWORD CompletionStatus;

PIOREQUEST IoRequestPacket;

LPOVERLAPPED Overlapped;

BOOL IoOperationStatus;

HandleVector[0] = IoWorkerListLock;

HandleVector[1] = IoWorkerListSemaphore;

for(;;) {

/* Do an alertable wait on the handle vector. Both objects */

/* being signaled at the same time means that there is an */

/* I/O request in the queue and the caller has exclusive */

/* access to the queue. */

CompletionStatus = WaitForMultipleObjectsEx(2, HandleVector,

TRUE, INFINITE, TRUE);

/* If the wait failed, error out. */

if (CompletionStatus == 0xFFFFFFFF) {

printf("FATAL WAIT ERROR %ld\n", GetLastError());

ExitProcess(1);

}

/* If an I/O completion occurred, wait for another */

/* I/O request or I/O completion. */

if (CompletionStatus != WAIT_IO_COMPLETION) {

/* The wait was satisfied. Ownership of the I/O */

/* request queue is exclusive, and there is something in */

/* the queue. To insert something in the queue, the */

/* inserter gets the list lock (mutex), inserts an entry, */

/* signals the list semaphore, and finally releases the */

/* list lock. */

IoRequestPacket = RemoveHeadList(&IoRequestList);

ReleaseMutex(IoWorkerListLock);

/* Allocate an overlapped structure. */

Overlapped = LocalAlloc(LMEM_ZEROINIT,

sizeof(OVERLAPPED));

if (!Overlapped) {

printf("FATAL allocation error\n");

ExitProcess(1);

}

Overlapped->Offset = IoRequestPacket->Offset;

Overlapped->OffsetHigh = IoRequestPacket->OffsetHigh;

Overlapped->hEvent =

IoRequestPacket->dwAdditionalIoContext;

if (IoRequestPacket->bRead) {

IoOperationStatus =

ReadFileEx(IoRequestPacket->hFile,

IoRequestPacket->lpBuffer,

IoRequestPacket->dwTransferCount,

Overlapped, IoCompletionRoutine);

}

else {

IoOperationStatus =

WriteFileEx(IoRequestPacket->hFile,

IoRequestPacket->lpBuffer,

IoRequestPacket->dwTransferCount,

Overlapped,

IoCompletionRoutine);

}

/* Test to see if the I/O was queued successfully. */

if (!IoOperationStatus) {

printf("FATAL I/O Error %ld I/O Context %lx.%lx\n",

GetLastError(), Overlapped, Overlapped->hEvent);

ExitProcess(1);

}

/* The I/O queued successfully. Go back into the */

/* alertable wait for I/O completion or for */

/* more I/O requests. */

}

}

}

The SetFilePointer function moves the file pointer a specified number of bytes, relative to the beginning or end of the file, or the file pointer's current position. If a positive number of bytes is specified, SetFilePointer moves the file pointer toward the beginning of the file; a negative value moves the pointer toward the end of the file.

The following example appends one file to the end of another file. The application opens two files by using CreateFile: ONE.TXT is opened for reading, and TWO.TXT is opened for writing. Then the application appends the contents of ONE.TXT to the end of TWO.TXT by reading and writing 4K blocks by using ReadFile and WriteFile. Before writing to the second file, the application sets the second file's pointer to the end of the file by using SetFilePointer and locks the area to be written by using LockFile. This prevents another process from accessing the area while the write is in progress. After each write operation, UnlockFile unlocks the locked area.

HANDLE hFile;

HANDLE hAppend;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char buff[4096];

/* Open the existing file. */

hFile = CreateFile("ONE.TXT", /* open ONE.TXT */

GENERIC_READ, /* open for reading */

0, /* do not share */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

OPEN_EXISTING, /* existing file only */

FILE_ATTRIBUTE_NORMAL, /* normal file */

(HANDLE) NULL); /* no attr. template */

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open ONE."); /* process error */

}

/*

* Open the existing file, or if the file does not exist,

* create a new file.

*/

hAppend = CreateFile("TWO.TXT", /* open TWO.TXT */

GENERIC_WRITE, /* open for writing */

0, /* do not share */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

OPEN_ALWAYS, /* open or create */

FILE_ATTRIBUTE_NORMAL, /* normal file */

(HANDLE) NULL); /* no attr. template */

if (hAppend == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open TWO."); /* process error */

}

/*

* Append the first file to the end of the second file.

* Lock the second file to prevent another process from

* accessing it while writing to it. Unlock the

* file when writing is finished.

*/

do {

if (ReadFile(hFile, (LPSTR) buff, 4096, &dwBytesRead, NULL)) {

dwPos = SetFilePointer(hAppend, 0, (LPLONG) NULL, FILE_END);

LockFile(hAppend, dwPos, 0, dwPos + dwBytesRead, 0);

WriteFile(hAppend, (LPSTR) buff, dwBytesRead,

&dwBytesWritten, NULL);

UnlockFile(hAppend, dwPos, 0, dwPos + dwBytesRead, 0);

}

} while (dwBytesRead == 4096);

/* Close both files. */

CloseHandle(hFile);

CloseHandle(hAppend);

+$#A Creating and Using a Temporary File

The following example copies one file to another. The second file is an uppercase version of the first file.

The application opens the ORIGINAL.TXT file by using CreateFile. The application then obtains a temporary filename with the GetTempFileName function and uses CreateFile to create the temporary file. The application reads 4K blocks into a buffer, converts the buffer contents to uppercase, and writes the converted buffer to the temporary file. When all of ORIGINAL.TXT has been written to the temporary file, the application closes both files and renames the temporary file to ALLCAPS.TXT by using the MoveFile function.

HANDLE hFile;

HANDLE hTempFile;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char szTempName[MAX_PATH];

char buffer[4096];

/* Open the existing file. */

hFile = CreateFile("ORIGINAL.TXT", /* filename */

GENERIC_READ, /* open for reading */

0, /* do not share */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

OPEN_EXISTING, /* existing file only */

FILE_ATTRIBUTE_NORMAL, /* normal file */

(HANDLE) NULL); /* no attr. template */

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open file."); /* process err. on fail*/

}

/* Create a temporary file. */

GetTempFileName("\\TEMP", /* dir. for temp. files */

"NEW", /* temp. filename prefix */

0, /* create unique name w/ sys. time */

(LPTSTR) szTempName); /* buffer for name */

hTempFile = CreateFile((LPTSTR) szTempName, /* filename */

GENERIC_READ | GENERIC_WRITE, /* open for read-write */

0, /* do not share */

(LPSECURITY_ATTRIBUTES) NULL, /* no security */

CREATE_ALWAYS, /* overwrite existing */

FILE_ATTRIBUTE_NORMAL, /* normal file */

(HANDLE) NULL); /* no attr. template */

if (hTempFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not create temporary file.");

}

/*

* Read 4K blocks to the buffer.

* Change all characters in the buffer to uppercase.

* Write the buffer to the temporary file.

*/

do {

if (ReadFile(hFile, (LPSTR) buffer, 4096,

&dwBytesRead, NULL)) {

CharUpperBuff((LPTSTR) buffer, dwBytesRead);

WriteFile(hTempFile, (LPSTR) buffer, dwBytesRead,

&dwBytesWritten, NULL);

}

} while (dwBytesRead == 4096);

/* Close both files. */

CloseHandle(hFile);

CloseHandle(hTempFile);

/* Move the temporary file to the new text file. */

if (!MoveFile(szTempName, "ALLCAPS.TXT")) {

ErrorHandler("Could not move temp. file.");

}

+$#A Searching for Files and Changing File Attributes

The following example copies all text files in the current directory to a new directory of read-only files named \TEXTRO. Files in the new directory are changed to read only, if necessary.

The application uses the GetCurrentDirectory function to retrieve the current directory path. This function is also used to return to the current directory after changing to the \TEXTRO directory.

The application then creates the \TEXTRO directory by using the CreateDirectory function.

The application searches the current directory for all .TXT files by using the FindFirstFile and FindNextFile functions. Each .TXT file is copied to the \TEXTRO directory. After a file is copied, the GetFileAttributes function determines whether the file is read only. If the file is not read only, the application changes directories to \TEXTRO and converts the copied file to read only by using the SetFileAttributes function.

After all .TXT files in the current directory have been copied, the application closes the search handle by using the FindClose function.

WIN32_FIND_DATA FileData;

HANDLE hSearch;

DWORD dwAttrs;

char szDirPath[] = "c:\\TEXTRO\\";

char szNewPath[MAX_PATH];

char szHome[MAX_PATH];

BOOL fFinished = FALSE;

/* Create a new directory. */

if (!CreateDirectory(szDirPath, NULL)) {

ErrorHandler("Couldn't create new directory.");

}

/* Start searching for .TXT files in the current directory. */

hSearch = FindFirstFile("*.txt", &FileData);

if (hSearch == INVALID_HANDLE_VALUE) {

ErrorHandler("No .TXT files found.");

}

/*

* Copy each .TXT file to the new directory

* and change it to read only, if not already.

*/

while (!fFinished) {

lstrcpy(szNewPath, szDirPath);

lstrcat(szNewPath, FileData.cFileName);

if (CopyFile(FileData.cFileName, szNewPath, FALSE)) {

dwAttrs = GetFileAttributes(FileData.cFileName);

if (!(dwAttrs & FILE_ATTRIBUTE_READONLY)) {

SetFileAttributes(szNewPath,

dwAttrs | FILE_ATTRIBUTE_READONLY);

}

}

else {

ErrorHandler("Couldn't copy file.");

}

if (!FindNextFile(hSearch, &FileData))

if (GetLastError() == ERROR_NO_MORE_FILES) {

MessageBox(hwnd, "No more .TXT files.",

"Search completed.", MB_OK);

fFinished = TRUE;

}

else {

ErrorHandler("Couldn't find next file.");

}

}

/* Close the search handle. */

if (!FindClose(hSearch)) {

ErrorHandler("Couldn't close search handle.");

}

+$#A Monitoring Changes in a Directory or Directory Tree

The following example monitors the directory tree starting at C:\ for directory name changes. It also monitors the C:\WINDOWS directory for filename changes.

The example uses the FindFirstChangeNotification function to create two notification handles and the WaitForMultipleObjects function to wait on the handles. Whenever a directory is created or deleted in the tree starting at C:\ , the example updates the entire directory tree. Whenever a file is created or deleted in the C:\WINDOWS directory, the example refreshes the WINDOWS directory. The FindNextChangeNotification function restarts the change notification each time the example processes a change.

DWORD dwWaitStatus;

HANDLE dwChangeHandles[2];

/*

* Watch the C:\WINDOWS directory for file creation and

* deletion.

*/

dwChangeHandles[0] = FindFirstChangeNotification(

"C:\\WINDOWS", /* directory to watch */

FALSE, /* do not watch the subtree */

FILE_NOTIFY_CHANGE_FILE_NAME); /* watch filename changes */

if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)

ExitProcess(GetLastError());

/*

* Watch the C:\ subtree for directory creation and

* deletion.

*/

dwChangeHandles[1] = FindFirstChangeNotification(

"C:\\", /* directory to watch */

TRUE, /* watch the subtree */

FILE_NOTIFY_CHANGE_DIR_NAME); /* watch dir. name changes */

if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)

ExitProcess(GetLastError());

/*

* Change notification is set. Now wait on both notification

* handles and refresh accordingly.

*/

while (TRUE) {

/* Wait for notification. */

dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,

FALSE, INFINITE);

switch (dwWaitStatus) {

case WAIT_OBJECT_0:

/*

* A file was created or deleted in C:\WINDOWS.

* Refresh this directory and restart the

* change notification. RefreshDirectory is an

* application-defined function.

*/

RefreshDirectory("C:\\WINDOWS")

if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)

ExitProcess(GetLastError());

break;

case WAIT_OBJECT_0 + 1:

/*

* A directory was created or deleted in C:\.

* Refresh the directory tree and restart the

* change notification. RefreshTree is an

* application-defined function.

*/

RefreshTree("C:\\");

if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)

ExitProcess(GetLastError());

break;

default:

ExitProcess(GetLastError());

}

}

+$#A Testing for the End of the File

The ReadFile function checks for the end-of-file condition (eof) differently for synchronous and asynchronous read operations. When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets the variable pointed to by lpNumberOfBytesRead to zero. An asynchronous read operation can encounter the end of a file during the initiating call to ReadFile or during subsequent asynchronous operation.

The test for end-of-file during a synchronous read operation is simple, as shown in the following example:

// attempt a synchronous read operation
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);
// check for eof
if (bResult && nBytesRead == 0, ) {
// we're at the end of the file
}

The test for end-of-file during an asynchronous read operation is more difficult. There are three end-of-file indicators for asynchronous read operations:

• ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF.

• ReadFile returns FALSE and GetLastError returns ERROR_IO_PENDING.

• GetOverLappedResult returns FALSE and GetLastError returns ERROR_HANDLE_EOF.

The following example shows how to test for an end-of-file during an asynchronous read operation:

// attempt to initiate an asynchronous read operation
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);
// if there was a problem ...
if (!bResult) {
switch (dwError = GetLastError()) {
case ERROR_HANDLE_EOF: {
// we're at the end of the file
}
case ERROR_IO_PENDING: {
// we're at the end of the file
}
// other error cases
}
}
.
.
.
// check on an asynchronous read operation
bResult = GetOverlappedResult(hFile, &gOverlapped, &nBytesRead,
&gOverlapped) ;
// if there was a problem ...
if (!bResult) {
switch (dwError = GetLastError()) {
case ERROR_HANDLE_EOF: {
// we're at the end of the file
}
// other error cases
}
}

+$#A Retrieving a File Pointer

The following example retrieves the current position of the file pointer in the file specified by the hFile value.

HANDLE hFile;

DWORD dwCurrentFilePosition;

dwCurrentFilePosition = SetFilePointer(

hFile, /* must have GENERIC_READ and/or GENERIC_WRITE */

0, /* do not move pointer */

NULL, /* hFile is not large enough to need this pointer */

FILE_CURRENT); /* provides offset from current position */

+$#A File Functions

Following are the functions used with files.

_hread
_hwrite
_lclose
_lcreat
_llseek
_lopen
_lread
_lwrite
AreFileApisANSI
CopyFile
CreateDirectory
CreateDirectoryEx
CreateFile
CreateIoCompletionPort
DefineDosDevice
DeleteFile
FileIOCompletionRoutine
FindClose
FindCloseChangeNotification
FindFirstChangeNotification
FindFirstFile
FindNextChangeNotification
FindNextFile
FlushFileBuffers
GetBinaryType
GetCurrentDirectory
GetDiskFreeSpace
GetDriveType
GetFileAttributes
GetFileInformationByHandle
GetFileSize
GetFileType
GetFullPathName
GetLogicalDrives
GetLogicalDriveStrings
GetQueuedCompletionStatus
GetShortPathName
GetTempDrive
GetTempFileName
GetTempPath
GetVolumeInformation
LockFile
LockFileEx
MoveFile
MoveFileEx
OpenFile
QueryDosDevice
ReadFile
ReadFileEx
RemoveDirectory
SearchPath
SetCurrentDirectory
SetEndOfFile
SetFileApisToANSI
SetFileApisToOEM
SetFileAttributes
SetFilePointer
SetHandleCount
SetVolumeLabel
UnlockFile
UnlockFileEx
WriteFile
WriteFileEx

Créé avec HelpNDoc Personal Edition: Créer des documentations web iPhone


Filled Shapes.rtf

+$#AK Filled Shapes

Filled shapes are geometric shapes that are outlined by using the current pen and filled by using the current brush. There are five filled shapes: ellipse, chord, pie, polygon, and rectangle.

+$#A About Filled Shapes

An application written for Microsoft® Windows® uses filled shapes in a variety of ways. Spreadsheet applications, for example, use filled shapes to construct charts and graphs, and drawing and painting applications use filled shapes to allow the user to draw figures and illustrations.

+$#A About Ellipses

An ellipse is a closed curve defined by two fixed points (f1 and f2) such that the sum of the distances (d1 + d2) from any point on the curve to the two fixed points is constant. The following illustration shows an ellipse drawn by using the Ellipse function.

{ewc msdncd, EWGraphic, group10658 0 /a "SDK_01.bmp"}

When calling Ellipse, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle. A bounding rectangle is the smallest rectangle completely surrounding the ellipse. When Windows draws the ellipse, it excludes the right and lower sides if no world transformations are set. Therefore, for any rectangle measuring x units wide by y units high, the associated ellipse measures x−1 units wide by y−1 units high. If the application sets a world transformation by calling the SetWorldTransform or ModifyWorldTransform function, Windows includes the right and lower sides.

+$#A About Chords

A chord is a region bounded by the intersection of an ellipse and a line segment called a secant. The following illustration shows a chord drawn by using the Chord function.

{ewc msdncd, EWGraphic, group10658 1 /a "SDK_02.bmp"}

When calling Chord, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle, as well as the coordinates of two points defining two radials. A radial is a line drawn from the center of an ellipse's bounding rectangle to a point on the ellipse.

When Windows draws the curved part of the chord, it does so by using the current arc direction for the specified device context. The default arc direction is counterclockwise. You can have your application reset the arc direction by calling the SetArcDirection function.

+$#A About Pies

A pie is a region bounded by the intersection of an ellipse curve and two radials. The following illustration shows a pie drawn by using the Pie function.

{ewc msdncd, EWGraphic, group10658 2 /a "SDK_03.bmp"}

When calling Pie, an application supplies the coordinates of the upper left and lower right corners of the ellipse's bounding rectangle, as well as the coordinates of two points defining two radials.

When Windows draws the curved part of the pie, it uses the current arc direction for the given device context. The default arc direction is counterclockwise. An application can reset the arc direction by calling the SetArcDirection function.

+$#A About Polygons

A polygon is a filled shape with straight sides. The sides of a polygon are drawn by using the current pen. When Windows fills a polygon, it uses the current brush and the current polygon fill mode. The two fill modes ⎯ alternate (the default) and winding ⎯ determine whether regions within a complex polygon are filled or left unpainted. An application can select either mode by calling the SetPolyFillMode function. For more information about polygon fill modes, see Regions.

The following illustration shows a polygon drawn by using Polygon.

{ewc msdncd, EWGraphic, group10658 3 /a "SDK_04.bmp"}

In addition to drawing a single polygon by using Polygon, an application can draw multiple polygons by using the PolyPolygon function.

+$#A Drawing Rectangles

A rectangle is a four-sided polygon whose opposing sides are parallel and equal in length. Although an application can draw a rectangle by calling the Polygon function, supplying the coordinates of each corner, Windows provides a simpler method, use of the Rectangle function. This function requires only the coordinates for the upper left and the lower right corners. When an application calls the Rectangle function, Windows draws the rectangle, excluding the right and lower sides if no world transformation is set for the given device context.

If a world transformation has been set by using the SetWorldTransform or ModifyWorldTransform function, Windows includes the right and lower edges.

In addition to drawing a normal rectangle, the Microsoft® Win32® application programming interface (API) provides a function that applications can use to draw rectangles with rounded corners. This function, RoundRect, requires that the application supply the coordinates of the lower left and upper right corners, as well as the width and height of the ellipse used to round each corner.

The Win32 API also provides three functions that applications can use to manipulate rectangles, described as follows.

Function | Description
FillRect | Repaints the interior of a rectangle.
FrameRect | Redraws the sides of a rectangle.
InvertRect | Inverts the colors that appear within the interior of a rectangle.

+$#A Using Filled Shapes

This section illustrates how to use filled shape functions. The example uses the main window procedure from an application that enables the user to draw ellipses, rectangles, and rectangles with rounded corners.

The user draws a filled shape by selecting a particular shape from the menu, positioning the cursor at the upper left corner of the shape (or the shape's bounding rectangle in the case of an ellipse), and then dragging the mouse until the desired dimensions are obtained.

The following illustration shows three filled shapes drawn using the sample code in this section.

{ewc msdncd, EWGraphic, group10658 4 /a "SDK_05.bmp"}

To enable the user to draw filled shapes, include the following code in your application.

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd; /* handle of window */

UINT uMsg; /* message */

WPARAM wParam; /* first message parameter */

LPARAM lParam; /* second message parameter */

{

HDC hdc; /* handle of device context (DC) */

PAINTSTRUCT ps; /* paint data for Begin/EndPaint */

POINT ptClientUL; /* client area upper left corner */

POINT ptClientLR; /* client area lower right corner */

static HDC hdcCompat;/* handle of DC for bitmap */

static POINT pt; /* x- and y-coordinates of cursor */

static RECT rcTarget; /* rect to receive filled shape */

static RECT rcClient; /* client area rectangle */

static BOOL fSizeEllipse; /* TRUE if ellipse is sized */

static BOOL fDrawEllipse; /* TRUE if ellipse is drawn */

static BOOL fDrawRectangle; /* TRUE if rectangle is drawn */

static BOOL fSizeRectangle; /* TRUE if rectangle is sized */

static BOOL fSizeRoundRect; /* TRUE if rounded rect is sized */

static BOOL fDrawRoundRect; /* TRUE if rounded rect is drawn */

static int nEllipseWidth; /* width for round corners */

static int nEllipseHeight; /* height for round corners */

switch (uMsg) {

case WM_COMMAND:

switch (wParam) {

/*

* Set the appropriate flag to indicate which

* filled shape the user is drawing.

*/

case IDM_ELLIPSE:

fSizeEllipse = TRUE;

break;

case IDM_RECTANGLE:

fSizeRectangle = TRUE;

break;

case IDM_ROUNDRECT:

fSizeRoundRect = TRUE;

break;

default:

return DefWindowProc(hwnd, uMsg, wParam,

lParam);

}

break;

case WM_CREATE:

nEllipseWidth = 20;

nEllipseHeight = 20;

return 0;

case WM_PAINT:

BeginPaint(hwnd, &ps);

/*

* Because the default brush is white, select

* a different brush into the device context

* to demonstrate the painting of filled shapes.

*/

SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH));

/*

* If one of the filled shape "draw" flags is TRUE,

* draw the corresponding shape.

*/

if (fDrawEllipse) { /* draws ellipse */

Ellipse(ps.hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

fDrawEllipse = FALSE;

rcTarget.left = rcTarget.right = 0;

rcTarget.top = rcTarget.bottom = 0;

}

if (fDrawRectangle) { /* Draws rectangle */

Rectangle(ps.hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

fDrawRectangle = FALSE;

rcTarget.left = rcTarget.right = 0;

rcTarget.top = rcTarget.bottom = 0;

}

if (fDrawRoundRect) { /* Draws rounded rectangle */

RoundRect(ps.hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom,

nEllipseWidth, nEllipseHeight);

fDrawRectangle = FALSE;

rcTarget.left = rcTarget.right = 0;

rcTarget.top = rcTarget.bottom = 0;

}

EndPaint(hwnd, &ps);

break;

case WM_SIZE:

/*

* Convert the client coordinates of the client area

* rectangle to screen coordinates and save them in a

* rectangle. The rectangle is passed to the ClipCursor

* function during WM_LBUTTONDOWN processing.

*/

GetClientRect(hwnd, &rcClient);

ptClientUL.x = rcClient.left;

ptClientUL.y = rcClient.top;

ptClientLR.x = rcClient.right;

ptClientLR.y = rcClient.bottom;

ClientToScreen(hwnd, &ptClientUL);

ClientToScreen(hwnd, &ptClientLR);

SetRect(&rcClient, ptClientUL.x, ptClientUL.y,

ptClientLR.x, ptClientLR.y);

return 0;

case WM_LBUTTONDOWN:

/*

* Restrict the cursor to the client area.

* This ensures that the window receives a matching

* WM_LBUTTONUP message.

*/

ClipCursor(&rcClient);

/* Save the coordinates of the cursor. */

pt.x = (LONG) LOWORD(lParam);

pt.y = (LONG) HIWORD(lParam);

/*

* If the user chooses one of the filled shapes,

* set the appropriate flag to indicate that the

* shape is being sized.

*/

if (fDrawEllipse)

fSizeEllipse = TRUE;

return 0;

case WM_MOUSEMOVE:

/*

* If one of the "size" flags is set, draw

* the target rectangle as the user drags

* the mouse.

*/

if ((wParam && MK_LBUTTON)

&& (fSizeEllipse || fSizeRectangle

|| fSizeRoundRect)) { /* draws target rect. */

/*

* Set the mixing mode so that the pen color is the

* inverse of the background color. The previous

* rectangle can then be erased by drawing

* another rectangle on top of it.

*/

hdc = GetDC(hwnd);

SetROP2(hdc, R2_NOTXORPEN);

/*

* If a previous target rectangle exists, erase

* it by drawing another rectangle on top.

*/

if (!IsRectEmpty(&rcTarget))

Rectangle(hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

/*

* Save the coordinates of the target rectangle.

* Avoid invalid rectangles by ensuring that the

* value of the left coordinate is greater than

* that of the right, and that the value of the

* bottom coordinate is greater than that of

* the top.

*/

if ((pt.x < (LONG) LOWORD(lParam)) &&

(pt.y > (LONG) HIWORD(lParam)))

SetRect(&rcTarget, pt.x, HIWORD(lParam),

LOWORD(lParam), pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&

(pt.y > (LONG) HIWORD(lParam)))

SetRect(&rcTarget, LOWORD(lParam),

HIWORD(lParam), pt.x, pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&

(pt.y < (LONG) HIWORD(lParam)))

SetRect(&rcTarget, LOWORD(lParam), pt.y,

pt.x, HIWORD(lParam));

else

SetRect(&rcTarget, pt.x, pt.y, LOWORD(lParam),

HIWORD(lParam));

/* Draw the new target rectangle. */

Rectangle(hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

ReleaseDC(hwnd, hdc);

}

return 0;

case WM_LBUTTONUP:

/*

* If one of the "size" flags is TRUE, reset

* it to FALSE, and then set the corresponding

* "draw" flag.

* Invalidate the appropriate rectangle and issue

* a WM_PAINT message.

*/

if (fSizeEllipse) {

fSizeEllipse = FALSE;

fDrawEllipse = TRUE;

}

if (fSizeRectangle) {

fSizeRectangle = FALSE;

fDrawRectangle = TRUE;

}

if (fSizeRoundRect) {

fSizeRoundRect = FALSE;

fDrawRoundRect = TRUE;

}

if (fDrawEllipse || fDrawRectangle || fDrawRoundRect)

{

InvalidateRect(hwnd, &rcTarget, TRUE);

UpdateWindow(hwnd);

}

/* Release the cursor. */

ClipCursor((LPRECT) NULL);

return 0;

case WM_DESTROY:

/*

* Destroy the background brush, compatible bitmap,

* and bitmap.

*/

DeleteDC(hdcCompat);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return (LRESULT) NULL;

}

+$#A Filled Shape Functions

The following functions are used with filled shapes.

Chord
Ellipse
FillRect
FrameRect
InvertRect
Pie
Polygon
PolyPolygon
Rectangle
RoundRect

Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub facile


Fonts and Text.rtf

+$#AK Fonts and Text

Fonts are used to draw text on video displays and other output devices. The Microsoft® Windows® operating system provides a set of functions that developers can use to install, select, and query different fonts.

+$#A About Fonts

To a person trained in the mechanics of manuscript composition or familiar with standard typography, some of the typographic terms used in Windows may be unusual. Most of the differences between standard typography and Windows reflect changes in technology. The original typographic terms were based on hot-metal composition, whereas the terms used in Windows, which appear as member names for the font and text output structures, reflect a new technology based on laser-printer output and composition performed on a personal computer using desktop publishing software.

+$#A Fonts Overview

In Windows, a font is a collection of characters and symbols that share a common design. The three major elements of this design are referred to as typeface, style, and size.

Typeface

The term typeface refers to specific characteristics of characters and symbols in the font, such as the width of the thick and thin strokes that compose the characters and the presence or absence of serifs. A serif is the short cross line at the ends of an unconnected stroke. A font or typeface without serifs is usually called a sans-serif font.

Style

The term style refers to the weight and slant of a font. Font weights can range from thin to black. The following list identifies possible weights for Windows fonts (beginning with the lightest and ending with the heaviest).

Thin
Extralight
Light
Normal
Medium
Semibold
Bold
Extrabold
Heavy

Three terms categorize the slant of a Windows font: roman, oblique, and italic.

The characters in a roman font are upright. The characters in an oblique font are artificially slanted. The slant is achieved by performing a shear transformation on the characters from a roman font. The characters in an italic font are truly slanted and appear as they were designed. For more information on shearing, see Coordinate Spaces and Transformations.

Size

In Windows, the size of a font is an imprecise value. It can generally be determined by measuring the distance from the bottom of a lowercase "g" to the top of an adjacent uppercase "M," as shown in the following illustration.

{ewc msdncd, EWGraphic, group10659 0 /a "SDK_01.bmp"}

A font's size is specified in units called points. A point is .013837 of an inch. Following the point system devised by Pierre Simon Fournier, it is common practice to approximate a point as 1/72 inch.

+$#A Font Families

Windows organizes fonts by family; a family is a set of fonts having common stroke width and serif characteristics. Windows categorizes families with five family names. A sixth name ("Dontcare") allows an application to use the default font. The following table describes the font-family names.

Font-family name | Description
Decorative | Specifies a novelty font. An example is Old English.
Dontcare | Specifies a generic family name. This name is used when information about a font does not exist or does not matter.
Modern | Specifies a monospace font with or without serifs. Monospace fonts are usually modern; examples include Pica, Elite, and Courier New®.
Roman | Specifies a proportional font with serifs. An example is Times New Roman.
Script | Specifies a font that is designed to look like handwriting; examples include Script and Cursive.
Swiss | Specifies a proportional font without serifs. An example is Arial.

These family names correspond to constants found in the WINGDI.H file: FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN, FF_SCRIPT, and FF_SWISS. An application uses these constants when it creates a font, selects a font, or retrieves information about a font.

Fonts within a family are distinguished by size (10 point, 24 point, etc.) and style (regular, italic, etc.).

+$#A Raster, Vector, and TrueType® Fonts

Windows-based applications can use three different kinds of font technologies to display and print text: raster, vector, and TrueType. The differences between these fonts reflect the way that the glyph for each character or symbol is stored in the respective font-resource file. In raster fonts, a glyph is a bitmap that Windows uses to draw a single character or symbol in the font. In vector fonts, a glyph is a collection of line endpoints that define the line segments Windows uses to draw a character or symbol in the font. In TrueType fonts, a glyph is a collection of line and curve commands as well as a collection of hints. Windows uses the line and curve commands to define the outline of the bitmap for a character or symbol in the TrueType font. Windows uses the hints to adjust the length of the lines and shapes of the curves used to draw the character or symbol. These hints and the respective adjustments are based on the amount of scaling used to reduce or increase the size of the bitmap.

Because the bitmaps for each glyph in a raster font are designed for a specific resolution of device, raster fonts are generally considered to be device dependent. Vector fonts, on the other hand, are not device dependent, because each glyph is stored as a collection of scalable lines. However, vector fonts are generally drawn more slowly than raster or TrueType fonts. TrueType fonts provide both relatively fast drawing speed and true device independence. By using the hints associated with a glyph, a developer can scale the characters from a TrueType font up or down and still maintain their original shape.

As previously mentioned, the glyphs for a font are stored in a font-resource file. A font-resource file is actually a Windows library that contains only data ⎯ there is no code. For raster and vector fonts, this data is divided into two parts: a header describing the font's metrics and the glyph data. A font-resource file for a raster or vector font is identified by the .FON filename extension. For TrueType fonts, there are two files for each font: the first file contains a relatively short header and the second contains the actual font data. The first file is identified by a .FOT extension and the second is identified by a .TTF extension.

+$#A Character Sets Used by Fonts

All fonts use a character set. A character set contains punctuation marks, numerals, uppercase and lowercase letters, and all other printable characters. Each element of a character set is identified by a number.

Most character sets used in Windows are supersets of the U.S. ASCII character set, which defines characters for the 96 numeric values from 32 through 127. There are five major groups of character sets:

• Windows

• Unicode™

• OEM (original equipment manufacturer)

• Symbol

• Vendor-specific

Windows Character Set

The Windows character set is the most commonly used character set in Windows programming. It is essentially equivalent to the ANSI character set. The blank character is the first character in the Windows character set. It has a hexadecimal value of 0x20 (decimal 32). The last character in the Windows character set has a hexadecimal value of 0xFF (decimal 255).

Many fonts specify a default character. Whenever a request is made for a character that is not in the font, Windows provides this default character. Many fonts using the Windows character set specify the period (.) as the default character. TrueType fonts typically use an open box as the default character.

Fonts use a break character called a quad to separate words and justify text. Most fonts using the Windows character set specify that the blank character will serve as the break character.

Windows version 3.1 added 24 characters to the Windows code page, as shown in the following table.

Character | Name | Windows character code
‚ | base line single quote | 130
ƒ | florin | 131
„ | base line double quote | 132
… | ellipsis | 133
† | dagger | 134
‡ | double dagger | 135
ˆ | circumflex | 136
‰ | permille | 137
Š | S Hacek | 138
‹ | left single guillemet | 139
Œ | OE ligature | 140
' | left single quote | 145
' | right single quote | 146
" | left double quote | 147
" | right double quote | 148
• | bullet | 149
− | en dash | 150
⎯ | em dash | 151
˜ | tilde | 152
™ | trademark ligature | 153
š | s Hacek | 154
› | right single guillemet | 155
œ | oe ligature | 156
Ÿ | Y Dieresis | 159

It should be noted that the characters for left and right single quote were first added to the character set for the release of Windows version 3.0.

Unicode™ Character Set

The Windows ANSI character uses 8 bits to represent each character; therefore, the maximum number of characters that can be expressed using 8 bits is 256 (2^8). This is usually sufficient for Western languages, including the diacritical marks used in French, German, Spanish, and other languages. However, Eastern languages employ thousands of separate characters, which cannot be encoded by using a single-byte coding scheme. With the proliferation of computer commerce, double-byte coding schemes were developed so that characters could be represented in 8-bit, 16-bit, 24-bit, or 32-bit sequences. This requires complicated passing algorithms; even so, using different code sets could yield entirely different results on two different computers.

To address the problem of multiple coding schemes, the Unicode standard for data representation was developed. A 16-bit character coding scheme, Unicode can represent 65,536 (2^16) characters, which is enough to include all languages in computer commerce today, as well as punctuation marks, mathematical symbols, and room for future expansion. Unicode establishes a unique code for every character to ensure that character translation is always accurate.

OEM Character Set

The OEM character set is typically used in full-screen MS-DOS® sessions for screen display. Characters 32 through 127 are usually the same in the OEM, U.S. ASCII, and Windows character sets. The other characters in the OEM character set (0 through 31 and 128 through 255) correspond to the characters that can be displayed in a full-screen MS-DOS session. These characters are generally different from the Windows characters.

Symbol Character Set

The Symbol character set contains special characters typically used to represent mathematical and scientific formulas.

Vendor-Specific Character Sets

Many printers and other output devices provide fonts based on character sets that differ from the Windows and OEM sets ⎯ for example, the Extended Binary Coded Decimal Interchange Code (EBCDIC) character set. To use one of these character sets, the printer driver translates from the Windows character set to the vendor-specific character set.

+$#A Font Installation and Deletion

A font must already be resident on a given device or installed in the Windows font table in order for an application to draw text using glyphs from that font. The Windows font table is an internal array that identifies all nondevice fonts that are available to a Windows-based application. An application can retrieve the names of fonts currently installed on a device and stored in the internal font table by calling the EnumFontFamilies or ChooseFont function.

An application can install a font by calling the font-installation function AddFontResource. The AddFontResource function loads a font that is stored in a font-resource file.

In the case of TrueType fonts, an additional step is sometimes necessary before the font can be installed in the font table. Some font manufacturers ship only the TrueType font-data files (identified by the .TTF extension). Before Windows can load these fonts, it requires a corresponding header file (identified by the .FOT extension). To create this header file, an application must call the CreateScalableFontResource function and pass the name of the font-data file as the third parameter. When this header file is created, an application can install the font by calling the AddFontResource function and passing the name of the new header file.

When an application finishes using an installed font, it must remove that font by calling the RemoveFontResource function.

Whenever an application calls the functions that add and delete font resources, it should also call the SendMessage function and send a WM_FONTCHANGE message to all top-level windows in the system. This message notifies other applications that the internal font table has been altered by an application that added or removed a font.

+$#A Font Creation and Selection

Previous versions of Windows required that the application developer provide source code and a dialog box template to support an interface that displayed the available fonts from which the user could choose. The new Font common dialog box simplifies the creation and selection process. By initializing the CHOOSEFONT structure and calling the ChooseFont function, an application can support the same font-selection interface that previously required many lines of code. (For more information about the Font common dialog box, see Common Dialog Box Library.)

Selection by the User

Most font creation and selection operations involve the user. For example, word processing applications let the user select unique fonts for headings, footnotes, and body text. After the user selects a font by using the Font dialog box and presses the OK button, the ChooseFont function initializes the members of a LOGFONT structure with the attributes of the requested font. To use this font for text-output operations, an application must first create a logical font and then select that font into its device context. A logical font is an application-supplied description of an ideal font. A developer can create a logical font by calling the CreateFont or the CreateFontIndirect functions. In this case, the application would call CreateFontIndirect and supply a pointer to the LOGFONT structure initialized by ChooseFont. In general, it is more efficient to call CreateFontIndirect because CreateFont requires several parameters while CreateFontIndirect requires only one ⎯ a pointer to LOGFONT.

Before an application can actually begin drawing text with a logical font, it must find the closest match from the fonts stored internally on the device and the fonts whose resources have been loaded into the operating system. The fonts stored on the device or in the operating system are called physical fonts. The process of finding the physical font that most closely matches a specified logical font is called font mapping. This process occurs when an application calls the SelectObject function and supplies a handle identifying a logical font. Font mapping is performed by using an internal algorithm that compares the attributes of the requested logical font against the attributes of available physical fonts. When the font mapper algorithm completes its search and determines the closest possible match, the SelectObject function returns and the application can begin drawing text with the new font.

The SetMapperFlags function specifies whether or not the font mapper algorithm searches only for physical fonts with aspect ratios that match the physical device. The aspect ratio for a device is the ratio formed by the width and the height of a pixel on that device.

+$#A Special Font Selection Considerations

Although most font selection operations involve the user, there are some instances where this is not true. For example, a developer may want to use a unique font in an application to draw text in a control window. To select an appropriate font, the application must be able to determine what fonts are available, create a logical font that describes one of these available fonts, and then select that font into the appropriate device context.

An application can enumerate the available fonts by using the EnumFontFamilies function. This can be useful when an application must determine which fonts are available from a given family.

Once an application has enumerated the available fonts and located an appropriate match, it should use the values returned by the font enumeration function to initialize the members of a LOGFONT structure. Then it can call the CreateFontIndirect function, passing to it a pointer to the initialized LOGFONT structure. If the CreateFontIndirect function is successful, the application can then select the logical font by calling the SelectObject function.

When initializing the members of the LOGFONT structure, be sure to specify a specific character set in the lfCharSet member. This member is important in the font mapping process and the results will be inconsistent if this member is not initialized correctly. If you specify a typeface name in the lfFaceName member of the LOGFONT structure, make sure that the lfCharSet value matches the character set of the typeface specified in lfFaceName. For example, if you want to select a font such as "MS Mincho", lfCharSet must be set to the predefined value SHIFTJIS_CHARSET.

+$#A Embedded Fonts

Embedding a font is the technique of bundling a document and the fonts it contains into a file for transmission to another computer. Embedding a font guarantees that a font specified in a transmitted file will be present on the computer receiving the file. Not all fonts can be moved from computer to computer, however, since most fonts are licensed to only one computer at a time. In Windows, only TrueType fonts can be embedded.

Applications should embed a font in a document only when requested by a user. An application cannot be distributed along with documents that contain embedded fonts, nor can an application itself contain an embedded font. Whenever an application distributes a font, in any format, the proprietary rights of the owner of the font must be acknowledged.

It may be a violation of a font vendor's proprietary rights or user license agreement to embed any fonts where embedding is not permitted or to fail to observe the following guidelines on embedding fonts. A font's license may give only read-write permission for a font to be installed and used on the destination computer. Or the license may give read-only permission. Read-only permission allows a document to be viewed and printed (but not modified) by the destination computer; documents with read-only embedded fonts are themselves read-only. Read-only embedded fonts may not be unbundled from the document and installed on the destination computer.

An application can determine the license status by calling the GetOutlineTextMetrics function and examining the otmfsType member of the OUTLINETEXTMETRIC structure. If bit 1 of otmfsType is set, embedding is not permitted for the font. If bit 1 is clear, the font can be embedded. If bit 2 is set, the embedding is read-only.

To embed a font, an application can use the GetFontData function to read the font file. Setting the dwTable and dwOffset parameters of GetFontData to 0L and the cbData parameter to −1L ensures that the application reads the entire font file from the beginning.

After an application retrieves the font data, it can store the data with the document by using any applicable format. Most applications build a font directory in the document, listing the embedded fonts and whether the embedding is read-write or read-only. An application can use the otmpStyleName and otmFamilyName members of the OUTLINETEXTMETRIC structure to identify the font.

If the read-only bit is set for the embedded font, applications must encrypt the font data before storing it with the document. The encryption method need not be complicated; for example, using the XOR operator to combine the font data with an application-defined constant is adequate and fast.

+$#A About Text Output

Text output is the most common type of graphic output found within the client area of Windows-based applications. It is used by Windows-based applications in different ways. Word processing and desktop publishing applications create documents with formatted text; spreadsheet applications use text, numbers, and symbols to specify formulas, label columns, and list values; database applications create records and display queries with text, and CAD applications use text to label objects and display dimensions.

The Win32® application programming interface (API) provides a complete set of functions to format and draw text in an application's client area and on a page of printer paper. These functions can be divided into two categories: those that format the text (or prepare it for output) and those that actually draw the text. The formatting functions align text, set the intercharacter spacing, set the text and text-background colors, and justify text. The drawing functions draw individual characters (or symbols) or entire strings of text.

+$#A Formatting Text

The formatting functions can be divided into three categories: those that retrieve or set the text-formatting attributes for a device context, those that retrieve character widths, and those that retrieve string widths and heights.

+$#A Text-Formatting Attributes

An application can use six functions to set the text-formatting attributes for a device context: SetBkColor, SetBkMode, SetTextAlign, SetTextCharacterExtra, SetTextColor, and SetTextJustification. These functions affect the text alignment, the intercharacter spacing, the text justification, and text and background colors. In addition, six other functions can be used to retrieve the current text formatting attributes for any device context: GetBkColor, GetBkMode, GetTextAlign, GetTextCharacterExtra, GetTextColor, and GetTextExtentPoint32.

Text Alignment

Applications can use the SetTextAlign function to specify how Windows should position the characters in a string of text when they call one of the drawing functions. This function can be used to position headings, page numbers, callouts, and so on. Windows positions a string of text by aligning a reference point on an imaginary rectangle that surrounds the string, with the current cursor position or with a point passed as an argument to one of the text drawing functions. The SetTextAlign function lets the application specify the location of this reference point. The list below identifies the possible reference point locations.

Location | Description
left/bottom | The reference point is located at the bottom left corner of the rectangle.
left/base line | The reference point is located at the intersection of the character-cell base line and the left edge of the rectangle.
left/top | The reference point is located at the top left corner of the rectangle.
center/bottom | The reference point is located at the center of the bottom of the rectangle.
center/base line | The reference point is located at the intersection of the character-cell base line and the center of the rectangle.
center/top | The reference point is located at the center of the top of the rectangle.
right/bottom | The reference point is located at the bottom right corner of the rectangle.
right/base line | The reference point is located at the intersection of the character-cell base line and the right edge of the rectangle.

right/top

The reference point is located at the top right corner of the rectangle.

The following illustration shows a string of text drawn by calling the TextOut function. Before drawing the text, the SetTextAlign function was called to relocate the reference point at each one of the nine possible locations.

{ewc msdncd, EWGraphic, group10659 1 /a "SDK_04.bmp"}

The default text alignment for a device context is the upper left corner of the imaginary rectangle that surrounds the text. An application can retrieve the current text-alignment setting for any device context by calling the GetTextAlign function.

Intercharacter Spacing

Applications can use the SetTextCharacterExtra function to alter the intercharacter spacing for all text output operations in a given device context. The following illustration shows a string of text drawn twice by calling the TextOut function. Before drawing the text the second time, the SetTextCharacterExtra function was called to increment the intercharacter spacing.

{ewc msdncd, EWGraphic, group10659 2 /a "SDK_06.bmp"}

The default intercharacter spacing value for any device context is zero. An application can retrieve the current intercharacter spacing value for a device context by calling the GetTextCharacterExtra function.

Text Justification

Applications can use the GetTextExtentPoint32 and SetTextJustification functions to justify a line of text. Text justification is a common operation in any desktop publishing and in most word processing applications. The GetTextExtentPoint32 function computes the width and height of a string of text. After the width is computed, the application can call the SetTextJustification function to distribute extra spacing between each of the words in a line of text. The following illustration shows a paragraph of text printed twice: in the first paragraph, the text was not justified; in the second paragraph, the text was justified by calling the GetTextExtentPoint32 and SetTextJustification functions.

{ewc msdncd, EWGraphic, group10659 3 /a "SDK_05.bmp"}

Text and Background Color

Applications can use the SetTextColor function to set the color of text drawn in the client-area of their windows, as well as the color of text drawn on a color printer. An application can use the SetBkColor function to set the color that appears behind each character and the SetBkMode function to specify how Windows should blend the selected background color with the current color or colors on the video display.

The default text color for a display device context is black; the default background color is white; and the default background mode is OPAQUE. An application can retrieve the current text color for a device context by calling the GetTextColor function. An application can retrieve the current background color for a device context by calling the GetBkColor function and the current background mode by calling the GetBkMode function.

+$#A Character Widths

Applications need to retrieve character-width data when they perform such tasks as fitting strings of text to page or column widths. There are four functions that an application can use to retrieve character-width data. Two of these functions retrieve the character-advance width and two of these functions retrieve actual character-width data.

An application can use the GetCharWidth32 and GetCharWidthFloat functions to retrieve the advance width for individual characters or symbols in a string of text. The advance width is the distance that the cursor on a video display or the print-head on a printer must advance before printing the next character in a string of text. The GetCharWidth32 function returns the advance width as an integer value. If greater precision is required, an application can use the GetCharWidthFloat function to retrieve fractional advance-width values.

An application can retrieve actual character-width data by using the GetCharABCWidths and GetCharABCWidthsFloat functions. To retrieve character widths for characters in a string of text that will be printed using a TrueType font, an application can call the GetCharABCWidths function; however, for any other font, the application should call the GetCharABCWidthsFloat function. (For more information about TrueType fonts, see Raster, Vector, and TrueType® Fonts). The following illustration shows the three components of the ABC width:

{ewc msdncd, EWGraphic, group10659 4 /a "SDK_02.bmp"}

The "A" spacing is the width to add to the current position before placing the character. The "B" spacing is the width of the character itself. The "C" spacing is the white space to the right of the character. The total advance width is determined by calculating the sum of A+B+C. The character cell is an imaginary rectangle that surrounds each character or symbol in a Windows font. Because characters can overhang or underhang the character cell, either or both of the A and C increments can be a negative number.

+$#A String Widths and Heights

In addition to retrieving character-width data for individual characters, applications also need to compute the width and height of entire strings. Two functions retrieve string-width and height measurements: GetTextExtentPoint32, and GetTabbedTextExtent. If the string does not contain tab characters, an application can use the GetTextExtentPoint32 function to retrieve the width and height of a specified string. If the string contains tab characters, an application should call the GetTabbedTextExtent function.

Windows also supports a special function, called GetTextExtentExPoint, that applications can use for word-wrapping operations. This function returns the number of characters from a specified string that fit within a given space.

Font Ascenders and Descenders

Some applications determine the line spacing between text lines of different sizes by using a font's maximum ascender and descender. An application can retrieve these values by calling the GetTextMetrics function and then checking the tmAscent and tmDescent members of the TEXTMETRIC.

The maximum ascent and descent are different from the typographic ascent and descent. In TrueType fonts, the typographic ascent and descent are typically the top of the "f" glyph and bottom of the "g" glyph. An application can retrieve the typographic ascender and descender for a TrueType font by calling the GetOutlineTextMetrics function and checking the values in the otmMacAscent and otmMacDescent members of the OUTLINETEXTMETRIC structure. It is important to note that TrueType font metrics do not correspond exactly to the metrics for Windows raster fonts, because TrueType font metrics have been designed by Apple Computer, Inc. for consistency across a variety of display and output devices.

The following figure shows the difference between the vertical text metric values returned in the NEWTEXTMETRIC and OUTLINETEXTMETRIC structures. (The names beginning with otm are members of the OUTLINETEXTMETRIC structure.)

{ewc msdncd, EWGraphic, group10659 5 /a "SDK_03.bmp"}

Font Dimensions

An application can retrieve the physical dimensions of a TrueType font by calling the GetOutlineTextMetrics function. An application can retrieve the physical dimensions of any other font by calling the GetTextMetrics function. To determine the dimensions of an output device, an application can call the GetDeviceCaps function. GetDeviceCaps returns both physical and logical dimensions.

A logical inch is a measure Windows uses to present legible fonts on the screen and is approximately 30 to 40 percent larger than a physical inch. The use of logical inches precludes an exact match between the output of the screen and printer. Developers should be aware that the text on a screen is not simply a scaled version of the text that will appear on the page, particularly if graphics are incorporated into the text.

+$#A Drawing Text

After an application selects the appropriate font, sets the required text-formatting options, and computes the necessary character width and height values for a string of text, it can begin drawing characters and symbols by calling any of the four text-output functions. Two of these functions, DrawText and TabbedTextOut, are part of Window Manager and found in the library USER.DLL; the remaining two functions are part of GDI and found in the library GDI.DLL. When an application calls one of these functions, the operating system passes the call to the graphics engine, which in turn passes the call to the appropriate device driver. At the device driver level, all of these calls are supported by one or more calls to the driver's own ExtTextOut or TextOut function. An application will achieve the fastest execution by calling the ExtTextOut function, which is quickly converted into an ExtTextOut call for the device. However, there are instances when an application should call one of the other three functions; for example, to draw multiple lines of text within the borders of a specified rectangular region, it is more efficient to call the DrawText function. To create a multicolumn table with justified columns of text, it is more efficient to call the TabbedTextOut function.

+$#A Using the Font and Text-Output Functions

This section describes how you can use the font and text-output functions to draw normal text, draw text from different fonts on the same line, rotate lines of text, display the font-selection common dialog-box, enumerate fonts, and so on.

• Using a stock font to draw text

• Creating a logical font

• Enumerating the installed fonts

• Checking the text capabilities of a device

• Setting the text alignment

• Drawing text from different fonts on the same line

• Rotating lines of text

• Retrieving character outlines

• Using portable truetype metrics

• Using PANOSE numbers

• Creating customized fonts

+$#A Using a Stock Font to Draw Text

Windows provides six stock fonts. A stock font is a logical font that an application can obtain by calling the GetStockObject function and passing a value that identifies the requested font. The following list contains the six values that you can specify to obtain a stock font.

Value | Meaning
ANSI_FIXED_FONT | Specifies a monospace font based on the Windows character set. A Courier font is typically used.
ANSI_VAR_FONT | Specifies a proportional font based on the Windows character set. MS Sans Serif is typically used.
DEVICE_DEFAULT_FONT | Specifies the preferred font for the given device. This is typically the System font for display devices; however, for some dot-matrix printers this is a font that is resident on the device. (Printing with this font is usually faster than printing with a downloaded, bitmapped font).
OEM_FIXED_FONT | Specifies a monospace font based on an OEM character set. For IBM® computers and compatibles, the OEM font is based on the IBM PC character set.
SYSTEM_FONT | Specifies the System font. This is a proportional font based on the Windows character set, and is used by the operating system to display window titles, menu names, and text in dialog boxes. The System font is always available. Other fonts are available only if they have been installed.
SYSTEM_FIXED_FONT | Specifies a monospace font compatible with the System font in Windows versions earlier than 3.0.

The following example retrieves a handle of the Windows variable stock font, selects it into a device context, and then writes a string using that font:

HFONT hfnt, hOldFont;

hfnt = GetStockObject(ANSI_VAR_FONT);

if (hOldFont = SelectObject(hdc, hfnt)) {

TextOut(hdc, 10, 50, "Sample ANSI_VAR_FONT text.", 26);

SelectObject(hdc, hOldFont);

}

If other stock fonts are not available, GetStockObject returns a handle to the System font (SYSTEM_FONT). You should use stock fonts only if the mapping mode for your application's device context is MM_TEXT.

+$#A Creating a Logical Font

You can use the Font common dialog box to display available fonts. The ChooseFont dialog box is displayed after an application initializes the members of a CHOOSEFONT structure and calls the ChooseFont function. After the user chooses one of the available fonts and presses the OK button, the ChooseFont function initializes a LOGFONT structure with the relevant data. Your application can then call the CreateFontIndirect function and create a logical font based on the user's request. The following example demonstrates how this is done.

HFONT FAR PASCAL MyCreateFont( void )

{

CHOOSEFONT cf;

LOGFONT lf;

HFONT hfont;

/* Initialize members of the CHOOSEFONT structure. */

cf.lStructSize = sizeof(CHOOSEFONT);

cf.hwndOwner = (HWND)NULL;

cf.hDC = (HDC)NULL;

cf.lpLogFont = &lf;

cf.iPointSize = 0;

cf.Flags = CF_SCREENFONTS;

cf.rgbColors = RGB(0,0,0);

cf.lCustData = 0L;

cf.lpfnHook = (LPCFHOOKPROC)NULL;

cf.lpTemplateName = (LPSTR)NULL;

cf.hInstance = (HINSTANCE) NULL;

cf.lpszStyle = (LPSTR)NULL;

cf.nFontType = SCREEN_FONTTYPE;

cf.nSizeMin = 0;

cf.nSizeMax = 0;

/* Display the CHOOSEFONT common-dialog box. */

ChooseFont(&cf);

/* Create a logical font based on the user's */

/* selection and return a handle identifying */

/* that font. */

hfont = CreateFontIndirect(cf.lpLogFont);

return (hfont);

}

+$#A Enumerating the Installed Fonts

In some instances, an application must be able to enumerate the available fonts and select the one most appropriate for a particular operation. An application can enumerate the available fonts by calling the EnumFonts or EnumFontFamilies function. These functions send information about the available fonts to a callback function that the application supplies. The callback function receives information in LOGFONT and NEWTEXTMETRIC structures. (The NEWTEXTMETRIC structure contains information about a TrueType font. When the callback function receives information about a non-TrueType font, the information is contained in a TEXTMETRIC structure.) By using this information, an application can limit the user's choices to only those fonts that are available.

The EnumFontFamilies function is similar to the EnumFonts function but includes some extra functionality. EnumFontFamilies allows an application to take advantage of styles available with TrueType fonts. New and upgraded applications should use EnumFontFamilies instead of EnumFonts.

In previous versions of Windows, the only style attributes were weight and italic; any other styles were specified in the family name for the font. For example, when an application used the EnumFonts function to query the available Courier fonts, EnumFonts might return information for Courier, Courier Bold, Courier Bold Italic, and Courier Italic. It would not return information about any other Courier fonts that might be installed, because any other Courier fonts would typically have a different family name.

TrueType fonts are organized around a typeface name (for example, Courier New) and style names (for example, italic, bold, and extra-bold). The EnumFontFamilies function enumerates all the styles associated with a given family name, not simply the bold and italic attributes. For example, when the system includes a TrueType font called Courier New Extra-Bold, EnumFontFamilies lists it with the other Courier New fonts. The capabilities of EnumFontFamilies are helpful for fonts with many or unusual styles and for fonts that cross international borders.

If an application does not supply a typeface name, the EnumFonts and EnumFontFamilies functions supply information about one font in each available family. To enumerate all the fonts in a device context, the application can specify NULL for the typeface name, compile a list of the available typefaces, and then enumerate each font in each typeface.

The following example uses the EnumFontFamilies function to retrieve the number of available raster, vector, and TrueType font families.

UINT uAlignPrev;

int aFontCount[] = { 0, 0, 0 };

char szCount[8];

EnumFontFamilies(hdc, (LPCTSTR) NULL,

(FONTENUMPROC) EnumFamCallBack, (LPARAM) aFontCount);

uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);

MoveToEx(hdc, 10, 50, (LPPOINT)NULL);

TextOut(hdc, 0, 0, "Number of raster fonts: ", 24);

itoa(aFontCount[0], szCount, 10);

TextOut(hdc, 0, 0, szCount, strlen(szCount));

MoveToEx(hdc, 10, 75, (LPPOINT)NULL);

TextOut(hdc, 0, 0, "Number of vector fonts: ", 24);

itoa(aFontCount[1], szCount, 10);

TextOut(hdc, 0, 0, szCount, strlen(szCount));

MoveToEx(hdc, 10, 100, (LPPOINT)NULL);

TextOut(hdc, 0, 0, "Number of TrueType fonts: ", 26);

itoa(aFontCount[2], szCount, 10);

TextOut(hdc, 0, 0, szCount, strlen(szCount));

SetTextAlign(hdc, uAlignPrev);

.

.

.

BOOL FAR PASCAL EnumFamCallBack(lplf, lpntm, FontType, aFontCount)

LPLOGFONT lplf;

LPNEWTEXTMETRIC lpntm;

DWORD FontType;

LPVOID aFontCount;

{

int far * aiFontCount = (int far *) aFontCount;

/*

* Record the number of raster, TrueType, and vector

* fonts in the font-count array.

*/

if (FontType & RASTER_FONTTYPE)

aiFontCount[0]++;

else if (FontType & TRUETYPE_FONTTYPE)

aiFontCount[2]++;

else

aiFontCount[1]++;

if (aiFontCount[0] || aiFontCount[1] || aiFontCount[2])

return TRUE;

else

return FALSE;

UNREFERENCED_PARAMETER( lplf );

UNREFERENCED_PARAMETER( lpntm );

}

This example uses two masks, RASTER_FONTTYPE and TRUETYPE_FONTTYPE, to determine the type of font being enumerated. If the RASTER_FONTTYPE bit is set, the font is a raster font. If the TRUETYPE_FONTTYPE bit is set, the font is a TrueType font. If neither bit is set, the font is a vector font. A third mask, DEVICE_FONTTYPE, is set when a device (for example, a laser printer) supports downloading TrueType fonts; it is zero if the device is a display adapter, dot-matrix printer, or other raster device. An application can also use the DEVICE_FONTTYPE mask to distinguish GDI-supplied raster fonts from device-supplied fonts. Windows can simulate bold, italic, underline, and strikeout attributes for GDI-supplied raster fonts, but not for device-supplied fonts.

An application can also check bits 1 and 2 in the tmPitchAndFamily member of the NEWTEXTMETRIC structure to identify a TrueType font. If bit 1 is 0 and bit 2 is 1, the font is a TrueType font.

Vector fonts are categorized as OEM_CHARSET instead of ANSI_CHARSET. Some applications identify vector fonts by using this information, checking the tmCharSet member of the NEWTEXTMETRIC structure. This categorization usually prevents the font mapper from choosing vector fonts unless they are specifically requested. (Most applications no longer use vector fonts because their strokes are single lines and they take longer to draw than TrueType fonts, which offer many of the same scaling and rotation features that required vector fonts in earlier versions of Windows.)

+$#A Checking the Text Capabilities of a Device

You can use the EnumFonts and EnumFontFamilies functions to enumerate the fonts that are available in a printer-compatible memory device context. You can also use the GetDeviceCaps function to retrieve information about the text capabilities of a device. By calling the GetDeviceCaps function with the NUMFONTS index, you can determine the minimum number of fonts supported by a printer. (An individual printer may support more fonts than specified in the return value from GetDeviceCaps with the NUMFONTS index.) By using the TEXTCAPS index, you can identify many of the text capabilities of the specified device.

+$#A Setting the Text Alignment

You can query and set the text alignment for a device context by using the GetTextAlign and SetTextAlign functions. The text-alignment settings determine how text is positioned relative to a given location. Text can be aligned to the right or left of the position or centered over it; it can also be aligned above or below the point.

The following example shows a method for determining which horizontal alignment flag is set:

switch ((TA_LEFT | TA_RIGHT | TA_CENTER) & GetTextAlign(hdc))

{

case TA_LEFT:

.

.

.

case TA_RIGHT:

.

.

.

case TA_CENTER:

.

.

.

}

You can also use the SetTextAlign function to update the current position when a text-output function is called. For instance, the following example uses the SetTextAlign function to update the current position when the TextOut function is called. In this example, the cArial parameter is an integer that specifies the number of Arial fonts.

UINT uAlignPrev;

char szCount[8];

uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);

MoveToEx(hdc, 10, 50, (LPPOINT) NULL);

TextOut(hdc, 0, 0, "Number of Arial fonts: ", 23);

itoa(cArial, szCount, 10);

TextOut(hdc, 0, 0, (LPSTR) szCount, strlen(szCount));

SetTextAlign(hdc, uAlignPrev);

+$#A Drawing Text From Different Fonts on the Same Line

Different type styles within a font family can have different widths. For example, bold and italic styles of a family are always wider than the roman style for a given point size. When you display or print several type styles on a single line, you must keep track of the width of the line to avoid having characters displayed or printed on top of one another.

You can use two functions to retrieve the width (or extent) of text in the current font. The GetTabbedTextExtent function computes the width and height of a character string. If the string contains one or more tab characters, the width of the string is based upon a specified array of tab-stop positions. The GetTextExtentPoint32 function computes the width and height of a line of text.

When necessary, Windows synthesizes a font by changing the character bitmaps. To synthesize a character in a bold font, Windows draws the character twice: once at the starting point, and again one pixel to the right of the starting point. To synthesize a character in an italic font, Windows draws two rows of pixels at the bottom of the character cell, moves the starting point one pixel to the right, draws the next two rows, and continues until the character has been drawn. By shifting pixels, each character appears to be sheared to the right. The amount of shear is a function of the height of the character.

One way to write a line of text that contains multiple fonts is to use the GetTextExtentPoint32 function after each call to TextOut and add the length to a current position. The following example writes the line "This is a sample string." using bold characters for "This is a", switches to italic characters for "sample", then returns to bold characters for "string." After printing all the strings, it restores the system default characters.

int XIncrement;

int YStart;

TEXTMETRIC tm;

HFONT hfntDefault, hfntItalic, hfntBold;

SIZE sz;

LPSTR lpszString1 = "This is a ";

LPSTR lpszString2 = "sample ";

LPSTR lpszString3 = "string.";

/* Create a bold and an italic logical font. */

hfntItalic = MyCreateFont();

hfntBold = MyCreateFont();

/* Select the bold font and draw the first string */

/* beginning at the specified point (XIncrement, YStart). */

XIncrement = 10;

YStart = 50;

hfntDefault = SelectObject(hdc, hfntBold);

TextOut(hdc, XIncrement, YStart, lpszString1,

lstrlen(lpszString1));

/*

* Compute the length of the first string and add

* this value to the x-increment that is used for the

* text-output operation.

*/

GetTextExtentPoint32(hdc, lpszString1,

lstrlen(lpszString1), &sz);

XIncrement += sz.cx;

/*

* Retrieve the overhang value from the TEXTMETRIC

* structure and subtract it from the x-increment.

* (This is only necessary for non-TrueType raster

* fonts.)

*/

GetTextMetrics(hdc, &tm);

XIncrement -= tm.tmOverhang;

/*

* Select an italic font and draw the second string

* beginning at the point (XIncrement, YStart).

*/

hfntBold = SelectObject(hdc, hfntItalic);

GetTextMetrics(hdc, &tm);

XIncrement -= tm.tmOverhang;

TextOut(hdc, XIncrement, YStart, lpszString2,

lstrlen(lpszString2));

/*

* Compute the length of the second string and add

* this value to the x-increment that is used for the

* text-output operation.

*/

GetTextExtentPoint32(hdc, lpszString2, lstrlen(lpszString2), &sz);

XIncrement += sz.cx;

/*

* Reselect the bold font and draw the third string

* beginning at the point (XIncrement, YStart).

*/

SelectObject(hdc, hfntBold);

TextOut(hdc, XIncrement - tm.tmOverhang, YStart, lpszString3,

lstrlen(lpszString3));

/* Reselect the original font. */

SelectObject(hdc, hfntDefault);

/* Delete the bold and italic fonts. */

DeleteObject(hfntItalic);

DeleteObject(hfntBold);

In this example, the GetTextExtentPoint32 function initializes the members of a SIZE structure with the length and height of the specified string. The GetTextMetrics function retrieves the overhang for the current font. Because the overhang is zero if the font is a TrueType font, the overhang value does not change the string placement. For raster fonts, however, it is important to use the overhang value.

The overhang is subtracted from the bold string once, to bring subsequent characters closer to the end of the string if the font is a raster font. Because overhang affects both the beginning and end of the italic string in a raster font, the glyphs start at the right of the specified location and end at the left of the endpoint of the last character cell. (The GetTextExtentPoint32 function retrieves the extent of the character cells, not the extent of the glyphs.) To account for the overhang in the raster italic string, the example subtracts the overhang before placing the string and subtracts it again before placing subsequent characters.

The SetTextJustification function adds extra space to the break characters in a line of text. You can use the GetTextExtentPoint function to determine the extent of a string, then subtract that extent from the total amount of space the line should occupy, and use the SetTextJustification function to distribute the extra space among the break characters in the string. The SetTextCharacterExtra function adds extra space to every character cell in the selected font, including the break character. (You can use the GetTextCharacterExtra function to determine the current amount of extra space being added to the character cells; the default setting is zero.)

You can place characters with greater precision by using the GetCharWidth32 or GetCharABCWidths function to retrieve the widths of individual characters in a font. The GetCharABCWidths function is more accurate than the GetCharWidth32 function, but only when it is used with TrueType fonts; when you use GetCharABCWidths with non-TrueType fonts, it retrieves the same information as GetCharWidth32.

ABC spacing also allows an application to perform very accurate text alignment. For example, when the application right aligns a raster roman font without using ABC spacing, the advance width is calculated as the character width. This means the white space to the right of the glyph in the bitmap is aligned, not the glyph itself. By using ABC widths, applications have more flexibility in the placement and removal of white space when aligning text, because they have information that allows them to finely control intercharacter spacing.

+$#A Rotating Lines of Text

You can rotate TrueType fonts at any angle. This is useful for labeling charts and other illustrations. The following example rotates a string in 10-degree increments around the center of the client area by changing the value of the lfEscapement and lfOrientation members of the LOGFONT structure used to create the font.

RECT rc;

int angle;

HFONT hfnt, hfntPrev;

LPSTR lpszRotate = "String to be rotated.";

/* Allocate memory for a LOGFONT structure. */

PLOGFONT plf = (PLOGFONT) LocalAlloc(LPTR, sizeof(LOGFONT));

/* Specify a font typeface name and weight. */

lstrcpy(plf->lfFaceName, "Arial");

plf->lfWeight = FW_NORMAL;

/* Retrieve the client-rectangle dimensions. */

GetClientRect(hwnd, &rc);

/*

* Set the background mode to transparent for the

* text-output operation.

*/

SetBkMode(hdc, TRANSPARENT);

/*

* Draw the string 36 times, rotating 10 degrees

* counter-clockwise each time.

*/

for (angle = 0; angle < 3600; angle += 100) {

plf->lfEscapement = angle;

hfnt = CreateFontIndirect(plf);

hfntPrev = SelectObject(hdc, hfnt);

TextOut(hdc, rc.right / 2, rc.bottom / 2,

lpszRotate, lstrlen(lpszRotate));

SelectObject(hdc, hfntPrev);

DeleteObject(hfnt);

}

/* Reset the background mode to its default. */

SetBkMode(hdc, OPAQUE);

/* Free the memory allocated for the LOGFONT structure. */

LocalFree((LOCALHANDLE) plf);

+$#A Retrieving Character Outlines

You can use the GetGlyphOutline function to retrieve the outline of a glyph from a TrueType font. The glyph outline returned by the GetGlyphOutline function is for a grid-fitted glyph. (A grid-fitted glyph has been modified so that its bitmap image conforms as closely as possible to the original design of the glyph.) If your application requires an unmodified glyph outline, request the glyph outline for a character in a font whose size is equal to the font's em units. (To create a font with this size, set the lfHeight member of the LOGFONT structure to the negative of the value of the ntmSizeEM member of the NEWTEXTMETRIC structure.)

GetGlyphOutline returns the outline as a bitmap or as a series of polylines and splines. When an application retrieves a glyph outline as a series of polylines and splines, the information is returned in a TTPOLYGONHEADER structure followed by as many TTPOLYCURVE structures as required to describe the glyph. All points are returned as POINTFX structures and represent absolute positions, not relative moves. The starting point given by the pfxStart member of the TTPOLYGONHEADER structure is the point where the outline for a contour begins. The TTPOLYCURVE structures that follow can be either polyline records or spline records.

To render a TrueType character outline in Windows, you must use both the polyline and the spline records. Windows can render both polylines and splines easily. Each polyline and spline record contains as many sequential points as possible, to minimize the number of records returned.

The starting point given in the TTPOLYGONHEADER structure is always on the outline of the glyph. The specified point serves as both the starting and ending points for the contour.

+$#A Polyline Records

Polyline records are a series of points; lines drawn between the points describe the outline of the character. A polyline record begins with the last point in the previous record (or, for the first record in the contour, the starting point). Each point in the record is on the glyph outline and can be connected simply by using straight lines.

+$#A Spline Records

Spline records represent the quadratic curves (that is, quadratic b-splines) used by TrueType. A spline record begins with the last point in the previous record (or for the first record in the contour, with the starting point). For the first spline record, the starting point and the last point in the record are on the glyph outline. For all other spline records, only the last point is on the glyph outline. All other points in the spline records are off the glyph outline and must be rendered as the control points of b-splines.

The last spline or polyline record in a contour always ends with the contour's starting point. This arrangement ensures that every contour is closed.

Because b-splines require three points (one point off the glyph outline between two points that are on the outline), you must perform some calculations when a spline record contains more than one off-curve point.

For example, if a spline record contains three points (A, B, and C) and it is not the first record, points A and B are off the glyph outline. To interpret point A, use the current position (which is always on the glyph outline) and the point on the glyph outline between points A and B. To find the midpoint (M) between A and B, you can perform the following calculation.

M = A + (B − A)/2

The midpoint between consecutive off-outline points in a spline record is a point on the glyph outline, according to the definition of the spline format used in TrueType fonts.

If the current position is designated by P, the two quadratic splines defined by this spline record are (P, A, M) and (M, B, C).

+$#A Using Portable TrueType Metrics

Applications that use the TrueType text metrics can achieve a high degree of printer and document portability; even applications that must maintain compatibility with earlier versions of Windows can use the TrueType metrics.

Design widths overcome most of the problems of device-dependent text introduced by physical devices. Design widths are a kind of logical width. Independent of any rasterization problems or scaling transformations, each glyph has a logical width and height. Composed to a logical page, each character in a string has a place that is independent of the physical device widths. Although a logical width implies that widths can be scaled linearly at all point sizes, this is not necessarily true for either nonportable or most TrueType fonts. At smaller point sizes, some glyphs are made wider relative to their height for better readability.

The characters in TrueType core fonts are designed against a 2048 by 2048 grid. The design width is the width of a character in these grid units. (TrueType supports any integer grid size up to 16,384 by 16,384; grid sizes that are integer powers of 2 scale faster than other grid sizes.)

The font outline is designed in notional units. The em square is the notional grid against which the font outline is fitted. (You can use the otmEMSquare member of OUTLINETEXTMETRIC and the ntmSizeEM member of NEWTEXTMETRIC to retrieve the size of the em square in notional units.) When a font is created that has a point size (in device units) equal to the size of its em square, the ABC widths for this font are the desired design widths. For example, assume the size of an em square is 1000 and the ABC widths of a character in the font are 150, 400, and 150. A character in this font that is 10 device units high would have ABC widths of 1.5, 4, and 1.5, respectively. Since the MM_TEXT mapping mode is most commonly used with fonts (and MM_TEXT is equivalent to device units), this is a simple calculation.

Because of the high resolution of TrueType design widths, applications that use them must take into account the large numeric values that can be created.

+$#A Device vs. Design Units

An application can retrieve font metrics for a physical font only after the font has been selected into a device context. When a font is selected into a device context, it is scaled for the device. The font metrics specific to the device are known as device units.

Portable metrics in fonts are known as design units. To apply to a given device, design units must be converted to device units. Use the following formula to convert design units to device units.

DeviceUnits = (DesignUnits/unitsPerEm) * (PointSize/72) * DeviceResolution

The variables in this formula have the following meanings.

Variable | Description
DeviceUnits | Specifies the DesignUnits font metric converted to device units. This value is in the same units as the value given for DeviceResolution.
DesignUnits | Specifies the font metric to be converted to device units. This value can be any font metric, including the width of a character or the ascender value for an entire font.
unitsPerEm | Specifies the em square size for the font.
PointSize | Specifies size of the font, in points. (One point equals 1/72 of an inch.)
DeviceResolution | Specifies number of device units (pixels) per inch. Typical values might be 300 for a laser printer or 96 for a VGA screen.

This formula should not be used to convert device units back to design units. Device units are always rounded to the nearest pixel. The propagated round-off error can become very large, especially when an application is working with screen sizes.

To request design units, create a logical font whose height is specified as −unitsPerEm. Applications can retrieve the value for unitsPerEm by calling the EnumFontFamilies function and checking the ntmSizeEM member of the NEWTEXTMETRIC structure.

+$#A Metrics for Portable Documents

The following table specifies the most important font metrics for applications that require portable documents and the functions that allow an application to retrieve them.

Function | Metric | Use
EnumFontFamilies | ntmSizeEM | Retrieval of design metrics; conversion to device metrics
GetCharABCWidths | ABCWidths | Accurate placement of characters at the start and end of margins, picture boundaries, and other text breaks
GetCharWidth32 | AdvanceWidths | Placement of characters on a line
GetOutlineTextMetrics | otmfsType | Font-embedding bits
 | otmsCharSlopeRise | Y-component for slope of cursor for italic fonts
 | otmsCharSlopeRun | X-component for slope of cursor for italic fonts
 | otmAscent | Line spacing
 | otmDescent | Line spacing
 | otmLineGap | Line spacing
 | otmpFamilyName | Font identification
 | otmpStyleName | Font identification
 | otmpFullName | Font identification (typically, family and style name)

The otmsCharSlopeRise, otmsCharSlopeRun, otmAscent, otmDescent, and otmLineGap members of the OUTLINETEXTMETRIC structure are scaled or transformed to correspond to the current device mode and physical height (as given in the tmHeight member of the NEWTEXTMETRIC structure).

Font identification is important in those instances when an application must select the same font ⎯ for example, when a document is reopened or moved to a different operating system. The font mapper always selects the correct font when an application requests a font by full name. The family and style names provide input to the standard font dialog box, which ensures that the selection bars are properly placed.

The otmsCharSlopeRise and otmsCharSlopeRun values are used to produce a close approximation of the main italic angle of the font. For typical roman fonts, otmsCharSlopeRise is 1 and otmsCharSlopeRun is 0. For italic fonts, the values attempt to approximate the sine and cosine of the main italic angle of the font (in counterclockwise degrees past vertical); note that the italic angle for upright fonts is 0. Because these values are not expressed in design units, they should not be converted into device units.

The character placement and line spacing metrics enable an application to compute device-independent line breaks that are portable across screens, printers, typesetters, and even platforms.

Device-independent page layout requires the following seven basic steps:

1. Normalize all design metrics to a common ultra-high resolution (UHR) value (for example, 65,536 DPI); this prevents round-off errors.

2. Compute line breaks based on UHR metrics and physical page width; this yields a starting point and an ending point of a line within the text stream.

3. Compute the device page width in device units (for example, pixels).

4. Fit each line of text into the device page width, using the line breaks computed in step 2.

5. Compute page breaks by using UHR metrics and the physical page length; this yields the number of lines per page.

6. Compute the line heights in device units.

7. Fit the lines of text onto the page, using the lines per page from step 5 and the line heights from step 6.

If all applications adopt these techniques, developers can virtually guarantee that documents moved from one application to another will retain their original appearance and format.

+$#A Using PANOSE Numbers

TrueType font files include PANOSE numbers, which applications can use to choose a font that closely matches their specifications. The PANOSE system classifies faces by 10 different attributes. For more information about these attributes, see the PANOSE structure. A PANOSE structure is part of the OUTLINETEXTMETRIC structure (whose values are filled in by calling the GetOutlineTextMetrics function).

The PANOSE attributes are rated individually on a scale. The resulting values are concatenated to produce a number. Given this number for a font and a mathematical metric to measure distances in the PANOSE space, an application can determine the nearest neighbors.

+$#A Creating Customized Fonts

Windows keeps a font table containing all the fonts that applications can use. Windows chooses a font from this table when an application calls the CreateFont or CreateFontIndirect function. There can be up to 253 entries in the table.

A font resource is a group of individual fonts representing characters in a given character set that have various combinations of heights, widths, and pitches. You can load font resources and add the fonts in each resource to the operating system font table by using the AddFontResource function. To remove a font resource from the font table, you can use the RemoveFontResource function.

After you add a font resource to the font table, you can use the individual fonts in the resource. In other words, the CreateFont function takes the fonts into account when it tries to match a physical font to the specified logical font. (Fonts in the table are never directly accessible to an application. They are available only through the CreateFontIndirect and CreateFont functions, which return the handles of the fonts, not their memory addresses.)

Whenever your application adds or removes a font resource, it should inform all other applications of the change by sending a WM_FONTCHANGE message to them. Use the following call to the SendMessage function to send the message to all windows.

SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

You can use the GetProfileString function to search the [Fonts] section of the WIN.INI file for the list of fonts that the user has used Control Panel to install.

+$#A Creating Font Resources

A font resource file is an empty Windows dynamic-link library; it contains no code or data, but it does contain resources. You can create font resources by creating font files and adding them as resources to a font resource file. To create a font resource file, follow these steps:

1. Create the font files.

2. Create a resource-definition file for the font.

3. Create a dummy code module.

4. Create a module-definition file that describes the fonts and the devices that use the fonts.

5. Compile and link the source files.

You can add a font file to an empty library, along with such resources as icons, cursors, and menus, by using resource compiler.

Note A complete explanation of how to create a font resource file is beyond the scope of this book. For more information on this topic, see the documentation provided with your application development tools.

+$#A Installing and Using an Embedded Font

You must separate an embedded font from the containing document and install it in the user's operating system before Windows can use it. Although the exact procedure for separating the font from the document depends on the method used to embed it, your application should always perform these steps:

1. Resolve name conflicts before installing the font.

2. Write the font data to a file, decoding read-only fonts as necessary.

3. Use the CreateScalableFontResource function to create a font resource file for the unembedded font.

In Windows, only a TrueType font can be embedded.

Your application should avoid installing a font with the same name as a font that is already on the system. To determine whether there are duplicate style names, an application can compare the information returned by EnumFontFamilies against the family name and style name stored with the embedded font.

Read-Write Permission

Embedded fonts that have read-write permission (and that therefore can be permanently installed on the user's system) should be written to a file that has the .TTF filename extension.

Most applications put the files for embedded fonts that have read-write permission into either the SYSTEM subdirectory of the user's Windows directory or into the application's working directory. Files for read-only embedded fonts are typically put into a temporary directory.

Before installing an embedded font, you must use the CreateScalableFontResource function to create a font resource file. Because Windows cannot directly interpret the native TrueType font file format, it requires a file that mimics the standard .FON file (called a .FOT file) to make internal bookkeeping and enumeration easier. The CreateScalableFontResource function produces a .FOT file that points to the TrueType font file. Once this .FOT file is produced, Windows applications can use TrueType fonts transparently by using the AddFontResource and RemoveFontResource functions. You can also use the CreateScalableFontResource function to install special fonts for logos, icons, and other graphics. Font resource files for read-only fonts should use a different extension (for example, .FOR) and should be hidden from other applications in the system by specifying 1 for the first parameter of CreateScalableFontResource.

Your application should offer users the option of permanently installing embedded fonts that have read-write permission. To permanently install a font, applications should concatenate the typeface and style names and then use the WriteProfileString function to insert this string along with the .FOT file name in the [Fonts] section of the WIN.INI file. A typical font entry in the [Fonts] section looks like the following example.

Times New Roman Bold (TrueType)=TIMESBD.FOT

Read-Only Permission

Embedded fonts with read-only permission should not use the .TTF extension and should avoid the .FOT and .FON extensions. A typical filename extension for read-only embedded fonts is .TTR. Files for read-only embedded fonts must be removed from the operating system and from both physical and logical memory as soon as the containing document is closed, so their names do not need to be meaningful except to the application.

If a document contains one or more read-only embedded fonts, the user must not be permitted to edit the document. If the user is allowed to edit the document in any way, your application must first strip away and delete the read-only embedded fonts. As mentioned earlier, read-only embedded fonts must be removed from the operating system and memory immediately when the document in which they are bundled is closed.

To delete read-only embedded fonts, your application should follow these steps:

1. Call the RemoveFontResource function for each font to be deleted.

2. Delete the font resource file for each font.

3. Delete each TrueType font file for each font.

When an application creates a file for a read-only embedded font and specifies 1 for the first parameter of the CreateScalableFontResource function, the EnumFonts and EnumFontFamilies functions will not enumerate this font. Hiding read-only embedded fonts in this manner makes it unlikely that another application can use them, even though Windows resources are theoretically available to all processes in Windows. The RemoveFontResource function does not delete a font currently in use. If your application uses a read-only embedded font installed by another application, it can be difficult for the installing application to delete the font. In this case, your application should delete the resource file and the TrueType font file when the user closes the document containing the read-only fonts.

It is very important that applications delete the TrueType font file for read-only embedded fonts. If the delete operation fails when the user closes the document, the application should periodically attempt to delete the file as the application runs, when it closes, and the next time it starts.

In some cases, an application is unable to delete a TrueType font file for a read-only embedded font because of external events (such as a system failure). There is no legal liability for events that are out of the control of the application.

+$#A Font and Text Functions

Following are the functions used with fonts.

AddFontResource
CreateFont
CreateFontIndirect
CreateScalableFontResource
DrawText
DrawTextEx
EnumFontFamilies
EnumFontFamProc
EnumFonts
EnumFontsProc
ExtTextOut
GetAspectRatioFilterEx
GetCharABCWidths
GetCharABCWidthsFloat
GetCharacterPlacement
GetCharWidth
GetCharWidth32
GetCharWidthFloat
GetFontData
GetGlyphOutline
GetKerningPairs
GetOutlineTextMetrics
GetRasterizerCaps
GetTabbedTextExtent
GetTextAlign
GetTextCharacterExtra
GetTextColor
GetTextExtentExPoint
GetTextExtentPoint
GetTextExtentPoint32
GetTextFace
GetTextMetrics
PolyTextOut
RemoveFontResource
SetMapperFlags
SetTextAlign
SetTextCharacterExtra
SetTextColor
SetTextJustification
TabbedTextOut
TextOut

Obsolete or Deleted Functions

GetAspectRatioFilter
GetTextExtent

Créé avec HelpNDoc Personal Edition: Générateur complet d'aides multi-formats


Handles and Objects.rtf

+$#AK Handles and Objects

An object is an internal structure that represents a system resource, such as a file, a thread, or a graphic image. An application cannot directly access the internal structure of an object or the system resource that an object represents. Instead, an application must obtain an object handle and use this handle to examine or modify the system resource (or both). In the Microsoft® Win32® application programming interface (API), handles are usually implemented as indirect pointers, but this is not always the case.

+$#A About Handles and Objects

Windows uses objects and handles to regulate access to system resources for two main reasons. First, the use of objects ensures that developers are not writing code specifically to low-level, internal structures. This enables Microsoft to add or change functionality of the operating system, as long as the original calling conventions are maintained. When subsequent versions of the operating system are released, applications will gain this new functionality with little or no additional development.

Second, the use of objects enables developers to take advantage of Win32 security. Each object has its own access-control list (ACL) that specifies the types of actions processes can perform on the object. The operating system examines an object's ACL each time an application attempts to create a handle to the object. For more information about security, see Security.

For most objects, the Win32 API provides functions that create the object, create an object handle, close the object handle, and destroy the object. These tasks may be combined or unnecessary, depending on the type of object and the situation. For example, an application could create an event object. Other applications could open the event and each would have a unique handle to the same event object. In this scenario, as the applications finish using the object, each closes its handle. When there are no open handles to the event object, the operating system removes the object from memory.

In contrast, an application could obtain the existing window-object handle. In this instance, when the window object is no longer needed, the application must remove the object from memory, which invalidates the window handle.

When a process terminates, the system automatically closes handles and deletes objects created by the process. However, when a thread terminates, the system usually does not close handles or delete objects. The only exceptions are window, hook, window position, and dynamic data exchange (DDE) conversation objects that are deleted when the creating thread terminates.

Handles and objects consume memory. Therefore, to preserve system performance, an application should close handles and delete objects as soon as they are no longer needed. Applications that do not do this can slow the operating system, due to excessive use of the paging file.

Windows provides three categories of objects: user, graphics device interface (GDI), and kernel, as shown in the following tables. The system uses user objects to support window management, GDI objects to support graphics, and kernel objects to support memory management, process execution, and interprocess communications (IPC). For information about creating and using a specific object, refer to the associated overview.

Windows User Objects

User object | Overview
Accelerator table | Keyboard Accelerators
Caret | Carets
Cursor | Cursors
Dynamic data exchange conversation | Dynamic Data Exchange Management Library
Desktop | Security
Hook | Hooks
Icon | Icons
Menu | Menus (Windows NT)
Window | Windows
Window position | Windows
Window station | Windows

Windows GDI Objects

GDI object | Overview
Bitmap | Bitmaps
Brush | Brushes
Font | Fonts and Text
Palette | Colors
Pen | Pens
Extended pen | Pens
Region | Regions
Device context | Device Contexts
Memory device context | Device Contexts
Metafile | Metafiles
Metafile device context | Metafiles
Enhanced metafile | Metafiles
Enhanced-metafile device context | Metafiles

Windows Kernel Objects

Kernel object | Overview
Process | Processes and Threads
Thread | Processes and Threads
File | Files
File mapping | File Mapping
Event | Synchronization
Semaphore | Synchronization
Mutex | Synchronization
Pipe (named and anonymous) | Pipes
Mailslot | Mailslots
Communications device | Communications
Heap | Memory Management
Module | Dynamic-Link Libraries
Update resource | Resources
Find file | Files
Event log | Event Logging
Change notification | Files
Token | Security
Service object | Printing and Print Spooler
LZ file | Data Decompression Library

+$#A Handle Limitations

Some objects, such as user and GDI objects, support only one handle at a time. The system provides the handle when an application creates the object and invalidates the handle when the application destroys the object. Other objects, such as some kernel objects, support multiple handles to a single object. The operating system automatically removes the object from memory after the last handle to the object is closed.

The total number of open handles in the operating system is limited only by the amount of memory available to the operating system. However, a single process may have no more than 16,384 open GDI object handles. The per-process limit on kernel handles is 2^30. There is no per-process limit on user handles, but there is a systemwide limit of 65,536.

+$#A User and Graphics Device Interface Objects

User and GDI objects support only one handle per object. Processes cannot inherit or duplicate handles to user or GDI objects.

Handles to user objects are public to all processes. That is, any process can use the user object handle, provided that the process has access to the object as defined by user security. For more information about security, see Security.

Handles to GDI objects are private to a process. That is, only the process that created the GDI object can use the object handle.

In the following illustration, an application creates a window object. The CreateWindow function creates the window object and returns an object handle.

{ewc msdncd, EWGraphic, group10750 0 /a "SDK_01.bmp"}

After the window object has been created, the application can use the window handle to display or change the window. The handle remains valid until the window object is destroyed.

In the next illustration, the application destroys the window object. The DestroyWindow function removes the window object from memory, which invalidates the window handle.

{ewc msdncd, EWGraphic, group10750 1 /a "SDK_02.bmp"}

The following table lists the Windows user objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or simply return the existing object handle. The destructor functions remove the object from memory, which invalidates the object handle.

Note that the window station and desktop objects are created and maintained by the system; applications cannot delete these objects.

User Objects

Object | Creator function | Destructor function
Accelerator table | CreateAcceleratorTable | DestroyAcceleratorTable
Cursor | CreateCursor, LoadCursor, GetCursor, SetCursor | DestroyCursor
DDE conversation | DdeConnect, DdeConnectList, DdeQueryNextServer, DdeReconnect | DdeDisconnect, DdeDisconnectList
Desktop | GetThreadDesktop | −
Hook | SetWindowsHook, SetWindowsHookEx | UnhookWindowsHook, UnhookWindowsHookEx
Menu | CreateMenu, CreatePopupMenu, GetMenu, GetSubMenu, GetSystemMenu, LoadMenu, LoadMenuIndirect | DestroyMenu
Window | CreateWindow, CreateWindowEx, CreateDialogParam, CreateDialogIndirectParam, CreateMDIWindow, FindWindow, GetWindow, GetClipboardOwner, GetDesktopWindow, GetDlgItem, GetForegroundWindow, GetLastActivePopup, GetOpenClipboardWindow, GetTopWindow, WindowFromDC, WindowFromPoint, and others | DestroyWindow
Window position | BeginDeferWindowPos | EndDeferWindowPos
Window station | GetProcessWindowStation | −

The following table lists the Windows GDI objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or simply return the existing object handle. The destructor functions remove the object from memory, which invalidates the object handle.

GDI Objects

Object | Creator function | Destructor function
Bitmap | CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection, CreateDiscardableBitmap | DeleteObject
Brush | CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush | DeleteObject
Font | CreateFont, CreateFontIndirect | DeleteObject
Palette | CreatePalette | DeleteObject
Pen | CreatePen, CreatePenIndirect | DeleteObject
Extended pen | ExtCreatePen | DeleteObject
Region | CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreatePolyPolygonRgn, CreateRectRgn, CreateRectRgnIndirect, CreateRoundRectRgn, ExtCreateRegion, PathToRegion | DeleteObject
Device context (DC) | CreateDC, GetDC | DeleteDC, ReleaseDC
Memory DC | CreateCompatibleDC | DeleteDC
Metafile | CloseMetaFile, CopyMetaFile, GetMetaFile, SetMetaFileBitsEx | DeleteMetaFile
Metafile DC | CreateMetafile | CloseMetaFile
Enhanced metafile | CloseEnhMetaFile, CopyEnhMetaFile, GetEnhMetaFile, SetEnhMetaFileBits | DeleteEnhMetaFile
Enhanced-metafile DC | CreateEnhMetaFile | CloseEnhMetaFile

+$#A Kernel Objects

Kernel object handles are process specific. That is, a process must either create a kernel object or open an existing object to obtain an object handle. Any process can create a new handle to an existing kernel object (even one created by another process), provided that the process knows the name of the object and has access to the object as defined by user security. For more information about security, see Security.

Processes can inherit or duplicate handles to the following types of kernel objects:

• Processes

• Threads

• Files (including file-mapping objects)

• Events

• Semaphores

• Mutexes

• Pipes (named and anonymous)

• Mailslots

• Communications devices

All other kernel objects are private to the process that created them; object handles cannot be duplicated or inherited.

A child process inherits an open handle from its parent process if inheritance was specified when the original handle was created and if the child process was created with the "inherit handles" flag set. An inherited handle is valid only in the context of the child process.

The DuplicateHandle function duplicates a handle into the current process or into another process. If an application duplicates one of its handles into another process, the duplicated handle is valid only in the context of the other process.

A duplicated or inherited handle is a unique value, but it refers to the same object as the original handle.

Kernel object handles include access rights that indicate the actions the application wants to perform on the object. An application specifies access rights when it creates an object or obtains an existing object handle. Each type of kernel object supports its own set of access rights. For example, event handles can have "set" or "wait" access (or both), file handles can have "read" or "write" access (or both), and so on. For more information about access rights, see Security, and topics that describe individual objects.

In the following illustration, an application creates an event object. The CreateEvent function creates the event object and returns an object handle.

{ewc msdncd, EWGraphic, group10750 2 /a "SDK_03.bmp"}

After the event object has been created, the application can use the event handle to set or wait on the event. The handle remains valid until the application closes the handle or terminates.

Most kernel objects support multiple handles to a single object. For example, the application in the preceding illustration could obtain additional event object handles by using the OpenEvent function, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10750 3 /a "SDK_04.bmp"}

This method enables an application to have handles with different access rights. For example, Handle 1 might have "set" and "wait" access to the event, and Handle 2 might have only "wait" access.

If another process knows the event name and has security access to the object, it can create its own event object handle by using OpenEvent, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10750 4 /a "SDK_05.bmp"}

The original application could also duplicate one of its handles into the same process or into another process by using the DuplicateHandle function, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10750 5 /a "SDK_06.bmp"}

The following illustration shows how a child process inherits a handle created by its parent process.

{ewc msdncd, EWGraphic, group10750 6 /a "SDK_07.bmp"}

A child process of an application automatically inherits handles created by its parent process if inheritance was specified when the handles were created and if the child process was created with the "inherit handles" flag set. Note that in the preceding illustration, Handle 2 is not equivalent to Handle 1 but does refer to the same event object.

For more information about inheritance, see Processes and Threads and Files

An object remains in memory as long as at least one object handle exists. In the following illustration, the applications use the CloseHandle function to close their event object handles. When there are no event handles, the system removes the object from memory, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10750 7 /a "SDK_08.bmp"}

Occasionally, an object remains in memory after all object handles have been closed. For example, a thread could create an event object and wait on the event handle. While the thread is waiting, another thread could close the same event object handle. The event object remains in memory, without any event object handles, until the event object is set to the signaled state and the wait operation is completed. At this time, the system removes the object from memory.

Windows manages file objects somewhat differently from other kernel objects. File objects contain the file pointer ⎯ the pointer to the next byte to be read or written in a file. Whenever an application creates a new file handle, the system creates a new file object. Therefore, more than one file object can refer to a single file on disk, as shown in the next illustration.

{ewc msdncd, EWGraphic, group10750 8 /a "SDK_09.bmp"}

Only through duplication or inheritance can more than one file handle refer to the same file object, as shown in the following illustration.

{ewc msdncd, EWGraphic, group10750 9 /a "SDK_10.bmp"}

The following table lists each of the Windows kernel objects and each object's creator and destructor functions. The creator functions either create the object and an object handle or create a new existing object handle. The destructor functions close the object handle. When an application closes the last handle to a kernel object, the system removes the object from memory.

Kernel Objects

Object | Creator function | Destructor function
Process | CreateProcess, OpenProcess, GetCurrentProcess | CloseHandle, TerminateProcess
Thread | CreateThread, CreateRemoteThread, GetCurrentThread | CloseHandle, TerminateThread
File | CreateFile | CloseHandle, DeleteFile
File mapping | CreateFileMapping, OpenFileMapping | CloseHandle
Event | CreateEvent, OpenEvent | CloseHandle
Semaphore | CreateSemaphore, OpenSemaphore | CloseHandle
Mutex | CreateMutex, OpenMutex | CloseHandle
Pipe | CreatePipe | CloseHandle
Named pipe | CreateNamedPipe | CloseHandle, DisconnectNamedPipe
Mailslot | CreateMailslot | CloseHandle
Communications device | GetStdHandle | CloseHandle
Heap | HeapCreate | HeapDestroy
Module | LoadLibrary, GetModuleHandle | FreeLibrary
Update resource | BeginUpdateResource | EndUpdateResource
Find file | FindFirstFile | FindClose
Event log | OpenEventLog, RegisterEventSource, OpenBackupEventLog | CloseEventLog
Change notification | FindFirstChangeNotification | FindCloseChangeNotification

+$#A Handles and Objects Functions

CloseHandle
DuplicateHandle
GetHandleInformation
SetHandleInformation

Créé avec HelpNDoc Personal Edition: Créer des sites web d'aide facilement


Handling Errors with Low-Level Audio Functions.rtf

+$#AK Handling Errors with Low-Level Audio Functions

Low-level audio functions return a nonzero error code. A set of functions convert these error codes into a textual description of the error. The application must still look at the error value itself to determine how to proceed, but textual descriptions of errors can be used in dialog boxes describing errors to users.

The following functions can be used to get textual descriptions of low-level audio errors.

Function | Description
midiInGetErrorText | Retrieves a textual description of a specified MIDI input error.
midiOutGetErrorText | Retrieves a textual description of a specified MIDI output error.
waveInGetErrorText | Retrieves a textual description of a specified waveform input error.
waveOutGetErrorText | Retrieves a textual description of a specified waveform output error.

The only low-level audio functions that don't return error codes are the device-numbering functions (auxGetNumDevs, midiInGetNumDevs, and so on). These functions return a value of zero if no devices are present in a system, or if any errors are encountered by the function.

+$#A Playing Waveform Audio

If your application plays waveform audio, you can use the Media Control Interface (MCI) to control waveform output devices. If the MCI waveform playback services don't meet the needs of your application, you can manage waveform playback by using the low-level waveform services.

+$#A Waveform Output Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for the waveform output functions.

Type | Description
HKL | Handle to a keyboard layout.
HWAVEOUT | Handle to an audio waveform output device.
WAVEOUTCAPS | Structure used to inquire about the capabilities of a particular waveform output device.
WAVEFORMAT | Structure that specifies the data formats supported by a particular waveform output device. This structure is also used for waveform input devices.
WAVEHDR | Structure that is a header for a block of waveform output data. This structure is also used for waveform input devices.

+$#A Querying Waveform Output Devices

Before playing a waveform, call the waveOutGetDevCaps function to determine the waveform output capabilities of the playback device as described earlier in this section. This function takes a pointer to a WAVEOUTCAPS structure, which it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.

In addition, the WAVEOUTCAPS structure provides information on the standard waveform formats and features supported by the device driver.

+$#A Determining Standard Format Support

The dwFormats member of the WAVEOUTCAPS structure specifies the standard waveform formats supported by a device. The MMSYSTEM.H header file defines the following standard waveform format identifiers for the dwFormats member.

Format identifier | Waveform format
WAVE_FORMAT_1M08 | 8-bit mono at 11.025 kHz
WAVE_FORMAT_1S08 | 8-bit stereo at 11.025 kHz
WAVE_FORMAT_1M16 | 16-bit mono at 11.025 kHz
WAVE_FORMAT_1S16 | 16-bit stereo at 11.025 kHz
WAVE_FORMAT_2M08 | 8-bit mono at 22.05 kHz
WAVE_FORMAT_2S08 | 8-bit stereo at 22.05 kHz
WAVE_FORMAT_2M16 | 16-bit mono at 22.05 kHz
WAVE_FORMAT_2S16 | 16-bit stereo at 22.05 kHz
WAVE_FORMAT_4M08 | 8-bit mono at 44.1 kHz
WAVE_FORMAT_4S08 | 8-bit stereo at 44.1 kHz
WAVE_FORMAT_4M16 | 16-bit mono at 44.1 kHz
WAVE_FORMAT_4S16 | 16-bit stereo at 44.1 kHz

The value of the dwFormats member is the result of a logical OR operation on the flags listed above.

This information on standard-format support also applies to the WAVEINCAPS structure used with waveform input devices. For information about the WAVEINCAPS structure, see Querying Waveform Input Devices.

To determine whether a specific format is supported by a device, use the waveOutOpen function with the WAVE_FORMAT_QUERY flag as shown in the next section.

+$#A Determining Low-Level Audio Nonstandard Format Support

Waveform devices can support nonstandard formats not listed in the preceding table. To see whether a particular format (standard or nonstandard) is supported by a device, you can call the waveOutOpen function with the WAVE_FORMAT_QUERY flag. The WAVE_FORMAT_QUERY flag tells waveOutOpen to check whether the requested format is supported. The wave device is not actually opened. The requested format is specified by the structure pointed to by the lpFormat parameter passed to waveOutOpen. For information about setting up this structure, see Specifying Waveform Data Formats. The following example uses this technique to determine whether a given waveform device supports a given format.

/*

* Determines whether the given waveform output device supports

* a given wave-form format. Returns 0 if the format is supported,

* WAVEERR_BADFORMAT if the format is not supported, and one of

* the MMSYSERR_ error codes if there are other errors in opening

* the given waveform device.

*/

UINT IsFormatSupported(LPPCMWAVEFORMAT lpPCMWaveFormat,

UINT wDeviceID)

{

return (waveOutOpen(

NULL, // NULL for query

wDeviceID, // device identifier

(LPWAVEFORMAT)lpPCMWaveFormat, // requested format

NULL, // no callback

NULL, // no instance data

WAVE_FORMAT_QUERY)); // query only

}

This technique to determine nonstandard format support also applies to waveform input devices. The only difference is that the waveInOpen function is used in place of waveOutOpen to query for format support.

Note To determine whether a particular waveform-data format is supported by any of the waveform devices in a system, use the technique illustrated in the previous example, but specify the WAVE_MAPPER constant for the wDeviceID parameter. For more information about using the WAVE_MAPPER constant, see Selecting a Waveform Output Device.

+$#A Determining Capabilities of Waveform Output Devices

Waveform output devices vary in the capabilities they support. The dwSupport member of the WAVEOUTCAPS structure indicates whether a given device supports capabilities such as volume and pitch changes. MMSYSTEM.H defines the following flags for the dwSupport member.

Flag | Description
WAVECAPS_PITCH | Pitch-change support
WAVECAPS_PLAYBACKRATE | Playback-rate-change support
WAVECAPS_VOLUME | Volume-control support
WAVECAPS_LRVOLUME | Individual volume-control support for both left and right channels

The dwSupport member is a logical OR of the flags listed in the preceding table.

For more information about playback volume levels, see Changing Waveform Playback Volume. For more information about pitch and playback rates, see Changing Pitch and Playback Rate.

+$#A Opening Waveform Output Devices

Use the waveOutOpen function to open a waveform output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Selecting a Waveform Output Device

Some multimedia computers have multiple waveform output devices. Unless you know you want to open a specific waveform output device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen function chooses the device in the system best capable of playing the given data format.

+$#A Specifying Waveform Data Formats

When you call the waveOutOpen function to open a device driver for playback or determine whether the driver supports a particular data format, use the lpFormat parameter to specify a pointer to a structure containing the requested waveform data format.

The WAVEFORMAT structure specifies format information common to all types of waveform data formats. Currently the only format supported is pulse code modulated (PCM), but in the future other types such as adaptive differential pulse code modualtion (ADPCM) may be supported.

In addition to the general information in the WAVEFORMAT structure, specific information is needed to describe a PCM waveform data format completely. For PCM waveform data, the PCMWAVEFORMAT structure includes a WAVEFORMAT structure along with an additional member containing PCM-specific information.

+$#A Using the PCMWAVEFORMAT Structure

For PCM audio data, use the PCMWAVEFORMAT structure to specify the data format. The following example shows how to set up a PCMWAVEFORMAT structure for 11.025 kilohertz (kHz) 8-bit mono and for 44.1 kHz 16-bit stereo. After setting up PCMWAVEFORMAT, the example calls the IsFormatSupported function to verify that the PCM waveform output device supports the format. The source code for IsFormatSupported is given in an example in "Determining Nonstandard Format Support."

UINT wReturn;

PCMWAVEFORMAT pcmWaveFormat;

/* Set up PCMWAVEFORMAT for 11 kHz 8-bit mono. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;

pcmWaveFormat.wf.nChannels = 1;

pcmWaveFormat.wf.nSamplesPerSec = 11025L;

pcmWaveFormat.wf.nAvgBytesPerSec = 11025L;

pcmWaveFormat.wf.nBlockAlign = 1;

pcmWaveFormat.wBitsPerSample = 8;

/* See if format is supported by any device in system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)

MessageBox(hMainWnd, "11 kHz 8-bit mono is supported.",

"", MB_ICONINFORMATION);

else if (wReturn == WAVERR_BADFORMAT)

MessageBox(hMainWnd, "11 kHz 8-bit mono NOT supported.",

"", MB_ICONINFORMATION);

else

MessageBox(hMainWnd, "Error opening waveform device.",

"Error", MB_ICONEXCLAMATION);

/* Set up PCMWAVEFORMAT for 44.1 kHz 16-bit stereo. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;

pcmWaveFormat.wf.nChannels = 2;

pcmWaveFormat.wf.nSamplesPerSec = 44100L;

pcmWaveFormat.wf.nAvgBytesPerSec = 176400L;

pcmWaveFormat.wf.nBlockAlign = 4;

pcmWaveFormat.wBitsPerSample = 32;

/* See if format is supported by any device in the system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)

MessageBox(hMainWnd, "44.1 kHz 16-bit stereo is supported.",

"", MB_ICONINFORMATION);

else if (wReturn == WAVERR_BADFORMAT)

MessageBox(hMainWnd, "44.1 kHz 16-bit stereo NOT supported.",

"", MB_ICONINFORMATION);

else

MessageBox(hMainWnd, "Error opening waveform device.",

"Error", MB_ICONEXCLAMATION);

+$#A Getting Format Information from a WAVE File

The easiest way to get waveform-format information from a WAVE file is by using the multimedia file input and output (I/O) services. To do this, use the mmioDescend function to locate the "fmt" chunk containing the format information and the mmioRead function to read the format chunk directly into the proper format structure. (Chunks are the basic building blocks of RIFF files.) The following example illustrates this technique. For more information about multimedia file I/O, see Multimedia File Input/Output.

void GetFormatInfo(void)

{

HMMIO hmmio;

MMCKINFO mmckinfoParent;

MMCKINFO mmckinfoSubchunk;

DWORD dwFmtSize;

HANDLE hFormat;

WAVEFORMAT *pFormat;

.ifdef REF

.

.

.

.endif

/* Open the given file for reading using buffered I/O. */

.ifdef REF

...

.endif

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure it's a WAVE file.

*/

.ifdef REF

...

.endif

/*

* Now, find the format chunk (form type "fmt "). It should be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

MessageBox(hwndApp, "WAVE file is corrupted.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

/*

* Get the size of the format chunk;

* allocate and lock memory for it.

*/

dwFmtSize = mmckinfoSubchunk.cksize;

hFormat = LocalAlloc(LMEM_MOVEABLE, dwFmtSize);

if (hFormat == NULL) {

MessageBox(hwndApp, "Out of memory.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

pFormat = (WAVEFORMAT *) LocalLock(hFormat);

if (pFormat == NULL) {

MessageBox(hwndApp, "Failed to lock memory for format chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/* Read the format chunk. */

if (mmioRead(hmmio, (LPSTR)pFormat,

dwFmtSize) != (LRESULT)dwFmtSize) {

MessageBox(hwndApp, "Failed to read format chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/* Make sure it's a PCM file. */

if (pFormat->wFormatTag != WAVE_FORMAT_PCM) {

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

MessageBox(hwndApp, "The file is not a PCM file.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

/*

* Make sure the system has a waveform output

* device capable of playing this format.

*/

if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat,

NULL, 0L, WAVE_FORMAT_QUERY)) {

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

MessageBox(hwndApp,

"The waveform device can't play this format.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

.ifdef REF

.

.

.

.endif

}

+$#A Writing Waveform Data

After successfully opening a waveform output device driver, you can begin waveform playback.

The waveOutWrite function writes a data block to a waveform output device. Use the WAVEHDR structure to specify the waveform data block you are sending. This structure contains a pointer to a locked data block, the length of the data block, and assorted flags.

After you send a data block to an output device by using waveOutWrite, you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the system for the completion of each data block so that you know when to send additional blocks. For details about different techniques for monitoring data block completion, see Managing Audio Data Blocks.

+$#A Example of Writing Waveform Data

The following example illustrates the steps required to allocate and set up a WAVEHDR data structure and write a block of data to a waveform output device.

/*

* Global variables. Must be visible to window-procedure function

* so it can unlock and free the data block after it has been played.

*/

HANDLE hData = NULL; // handle of waveform data memory

HPSTR lpData = NULL; // pointer to waveform data memory

void WriteWaveData(void)

{

HWAVEOUT hWaveOut;

HGLOBAL hWaveHdr;

LPWAVEHDR lpWaveHdr;

HMMIO hmmio;

UINT wResult;

HANDLE hFormat;

WAVEFORMAT *pFormat;

DWORD dwDataSize;

.ifdef REF

.

.

.

.endif

/* Open a waveform device for output using window callback. */

if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,

(LPWAVEFORMAT)pFormat,

(LONG)hwndApp, 0L, CALLBACK_WINDOW)) {

MessageBox(hwndApp,

"Failed to open waveform output device.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/*

* Allocate and lock memory for the waveform data. The memory

* for waveform data must be globally allocated with

* GMEM_MOVEABLE and GMEM_SHARE flags.

*/

hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );

if (!hData)

{

MessageBox(hwndApp, "Out of memory.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

if ((lpData = GlobalLock(hData)) == NULL) {

MessageBox(hwndApp, "Failed to lock memory for data chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

GlobalFree(hData);

mmioClose(hmmio, 0);

return;

}

/* Read the waveform data subchunk. */

if(mmioRead(hmmio, (HPSTR) lpData, dwDataSize) != (LRESULT)dwDataSize) {

MessageBox(hwndApp, "Failed to read data chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

GlobalUnlock(hData);

GlobalFree(hData);

mmioClose(hmmio, 0);

return;

}

/*

* Allocate and lock memory for the header. This memory must

* also be globally allocated with GMEM_MOVEABLE and

* GMEM_SHARE flags.

*/

hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,

(DWORD) sizeof(WAVEHDR));

if (hWaveHdr == NULL) {

GlobalUnlock(hData);

GlobalFree(hData);

MessageBox(hwndApp, "Not enough memory for header.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);

if (lpWaveHdr == NULL) {

GlobalUnlock(hData);

GlobalFree(hData);

MessageBox(hwndApp,

"Failed to lock memory for header.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

/* After allocation, set up and prepare header. */

lpWaveHdr->lpData = lpData;

lpWaveHdr->dwBufferLength = dwDataSize;

lpWaveHdr->dwFlags = 0L;

lpWaveHdr->dwLoops = 0L;

waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));

/*

* Now the data block can be sent to the output device. The

* waveOutWrite function returns immediately and waveform

* data is sent to the output device in the background.

*/

wResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));

if (wResult != 0) {

waveOutUnprepareHeader(hWaveOut, lpWaveHdr,

sizeof(WAVEHDR));

GlobalUnlock( hData);

GlobalFree(hData);

MessageBox(hwndApp, "Failed to write block to device",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

.ifdef REF

.

.

.

.endif

}

+$#A PCM Waveform Data Format

The lpData member in the WAVEHDR structure points to the waveform data samples. For 8-bit PCM data, each sample is represented by a single unsigned data byte. For 16-bit PCM data, each sample is represented by a 16-bit signed value. The following table summarizes the maximum, minimum, and midpoint values for PCM waveform data.

Data format | Maximum value | Minimum value | Midpoint value
8-bit PCM | 255 (0xFF) | 0 | 128 (0x80)
16-bit PCM | 32767 (0x7FFF) | -32768 (0x8000) | 0

The order of the data bytes varies between 8-bit and 16-bit, and between mono and stereo formats. The following tables show data packing for the first four words of different PCM waveform data formats.

Data packing for 8-bit mono PCM:

Sample 1 | Sample 2 | Sample 3 | Sample 4
Channel 0 | Channel 0 | Channel 0 | Channel 0

Data packing for 8-bit stereo PCM:

Sample 1 |  | Sample 2 |
Channel 0 (left) | Channel 1 (right) | Channel 0 (left) | "Channel 1 (right)"
Low-order byte | High-order byte | Low-order byte | High-order byte

Data packing for 16-bit mono PCM:

Sample 1 |  | Sample 2 |
Channel 0 low byte | Channel 0 high byte | Channel 0 low byte | Channel 0 high byte

Data packing for 16-bit stereo PCM:

Sample 1 |  |  |
Channel 0 (left) byte | Channel 0 (left) byte | Channel 1 (right) byte | Channel 1 (right) byte

+$#A Waveform Looping

Waveform looping is controlled by the dwLoops and dwFlags members in the WAVEHDR structures passed to the device with waveOutWrite. Use the WHDR_BEGINLOOP and WHDR_ENDLOOP flags in the dwFlags member to specify the beginning and ending data blocks for looping.

To loop on a single block, specify both flags for the same block. To specify the number of loops, use the dwLoops member in the WAVEHDR structure for the first block in the loop.

You can stop waveform looping with the waveOutBreakLoop function. The blocks making up the loop are played to the end before the loop is stopped.

+$#A Using Window Messages to Manage Waveform Playback

The following messages can be sent to a window-procedure function for managing waveform playback.

Message | Description
MM_WOM_CLOSE | Sent when the device is closed using waveOutClose.
MM_WOM_DONE | Sent when the device driver is finished with a data block sent using waveOutWrite.
MM_WOM_OPEN | Sent when the device is opened using waveOutOpen.

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. For the MM_WOM_DONE message, lParam specifies a pointer to a WAVEHDR structure identifying the completed data block. The lParam parameter is unused for the MM_WOM_CLOSE and MM_WOM_OPEN messages.

The most useful message is MM_WOM_DONE. When this message signals that playback of a data block is complete, you can clean up and free the data block. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_WOM_OPEN and MM_WOM_CLOSE messages.

The following example shows how to process the MM_WOM_DONE message. The example assumes the application does not play multiple data blocks, so it can close the output device after playing a single data block.

/* WndProc--Main window-procedure function. */

LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

switch (msg) {

.ifdef REF

.

.

.

.endif

case MM_WOM_DONE:

/* A data block has been played and can now be freed. */

waveOutUnprepareHeader((HWAVEOUT) wParam,

(LPWAVEHDR) lParam, sizeof(WAVEHDR) );

GlobalUnlock(hData);

GlobalFree(hData);

waveOutClose((HWAVEOUT) wParam);

break;

}

return DefWindowProc(hWnd,msg,wParam,lParam);

}

+$# Using a Low-Level Callback Function to Manage Waveform Playback

The syntax of the low-level callback function for waveform output devices is as follows:

void CALLBACK waveOutCallback(hWaveOut, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of waveform output callback functions.

Message | Description
WOM_CLOSE | Sent when the device is closed using the waveOutClose function.
WOM_DONE | Sent when the device driver is finished with a data block sent using the waveOutWrite function.
WOM_OPEN | Sent when the device is opened using the waveOutOpen function.

These messages are similar to the messages sent to window-procedure functions, the parameters are different. A handle of the open waveform device is passed as a parameter to the callback function, along with the doubleword of instance data that was passed using waveOutOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WOM_DONE message, dwParam1 specifies a pointer to a WAVEHDR structure identifying the completed data block and dwParam2 is not used. For the WOM_OPEN and WOM_CLOSE messages, neither of the parameters is used.

After the driver is finished with a data block, you can clean up and free the data block, as described in "Allocating and Preparing Audio Data Blocks." Because of the suggested restrictions on low-level audio callback functions, it is better to not do this from within the callback. See Using a Callback Function to Process Driver Messages for details about suggested restrictions on using callback functions.

+$#A Getting the Current Playback Position

While a waveform is playing, you can monitor the current playback position within the waveform. The Win32 API provides the waveOutGetPosition function for this purpose.

This function takes three parameters: a handle of a waveform output device, a pointer to an MMTIME structure, and an unsigned integer specifying the size of the MMTIME structure.

For waveform devices, the preferred time format in which to represent the current position is samples. Thus the current position of a waveform device is specified as the number of samples for one channel from the beginning of the waveform.

To query the current position of a waveform device, set the wType member of the MMTIME structure to TIME_SAMPLES and pass the structure to waveOutGetPosition.

+$#A Stopping, Pausing, and Restarting Playback

You can stop or pause playback while a waveform is playing. Once playback has been paused, you can restart it. The Win32 API provides the following functions for controlling waveform playback.

Function | Description
waveOutPause | Pauses playback on a waveform output device.
waveOutReset | Stops playback on a waveform output device and marks all pending data blocks as done.
waveOutRestart | Resumes playback on a paused waveform output device.

Use the waveOutPause function to pause a waveform device that is currently playing. To resume playback on a paused device, use the waveOutRestart function. These functions take a single parameter: the waveform output device handle returned by the waveOutOpen function. Pausing a waveform device may not be instantaneous ⎯ the driver can finish playing the current block before pausing playback.

Typically, as soon as the first waveform data block is sent by using the waveOutWrite function, the waveform device begins playing. If you don't want the waveform to start playing immediately, call waveOutPause before calling waveOutWrite. When you want to begin playing the waveform, call waveOutRestart.

To stop a waveform from playing, use the waveOutReset function. This function differs from waveOutPause in that it also marks all pending data blocks as being done. You can not use waveOutRestart to restart a device that has been stopped by using waveOutReset. You must use waveOutWrite to send the first data block to resume playback on the device.

+$#A Closing Waveform Output Devices

After waveform playback is complete, call the waveOutClose function to close the waveform device. If waveOutClose is called while a waveform is playing, the close operation fails and the function returns an error code indicating that the device was not closed. If you don't want to wait for playback to end before closing the device, call the waveOutReset function before closing. This stops playback and allows the device to be closed. Be sure to clean up the preparation on all data blocks before closing the waveform output device.

+$#A Changing Waveform Playback Volume

Windows provides the following functions to query and set the volume level of waveform output devices.

Function | Description
waveOutGetVolume | Gets the volume level of the specified waveform device.
waveOutSetVolume | Sets the volume level of the specified waveform device.

Not all waveform devices support volume changes. Some devices support individual volume control on both the left and right channels. For information about how to determine the volume-control capabilities of waveform devices, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure that your application opens an audio device before changing its volume. Also, be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value of which the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left (or mono) channel.

For devices that don't support left- and right-channel volume control, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Volume-level values range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same when increasing the volume level from 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

The waveOutGetVolume function returns the volume as a doubleword value at an address specified by a pointer parameter.

+$#A Changing Pitch and Playback Rate

Some waveform output devices can vary the pitch and the playback rate of waveform data. Not all waveform devices support pitch and playback-rate changes. For information about how to determine if a particular waveform device supports pitch and playback-rate changes, see Determining Capabilities of Waveform Output Devices.

Following are the differences between changing pitch and playback rate:

• Changing the playback rate is performed by the device driver and does not require specialized hardware. The sample rate is not changed, but the driver interpolates by skipping or synthesizing samples. For example, if the playback rate is changed by a factor of two, the driver skips every other sample.

• Changing the pitch requires specialized hardware. The playback rate and sample rate are not changed.

Windows provides the following functions to query and set waveform pitch and playback rates.

Function | Description
waveOutGetPitch | Gets the pitch for the specified waveform output device.
waveOutGetPlaybackRate | Gets the playback rate for the specified waveform output device.
waveOutSetPitch | Sets the pitch for the specified waveform output device.
waveOutSetPlaybackRate | Sets the playback rate for the specified waveform output device.

The pitch and playback rates are changed by a factor specified with a fixed-point number packed into a doubleword value. The high-order 16 bits specify the integer part, and the low-order 16 bits specify the fractional part of the number. For example, the value 1.5 is represented as 0x00018000L. The value 0.75 is represented as 0x0000C000L. A value of 1.0 (0x00010000) means the pitch or playback rate is unchanged.

+$#A Recording Low-Level Waveform Audio

If the MCI waveform-recording services don't meet the needs of your application, you can handle waveform recording using the low-level waveform services.

+$#A Waveform Input Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for waveform-input functions.

Type | Description
HWAVEIN | Handle of an open waveform input device.
WAVEINCAPS | Data structure used to inquire about the capabilities of a particular waveform input device.
WAVEFORMAT | Structure that specifies the data formats supported by a particular waveform input device. This structure is also used for waveform output devices.
WAVEHDR | Structure that is a header for a block of waveform input data. This structure is also used for waveform output devices.

+$#A Querying Waveform Input Devices

Before recording a waveform, you should call the waveInGetDevCaps function to determine the waveform input capabilities of the system. This function takes a pointer to a WAVEINCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the WAVEINCAPS structure provides information about the standard waveform formats that the device supports.

+$#A Opening Waveform Input Devices

Use the waveInOpen function to open a waveform input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Selecting a Waveform Input Device

Some multimedia computers will have multiple waveform input devices. Unless you know you want to open a specific waveform input device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen function will choose the device in the system best able to record in the given data format.

+$#A Managing Waveform Recording

Once you open a waveform input device, you can begin recording waveform data. Waveform data is recorded into application-supplied buffers specified by a WAVEHDR structure. This is the same structure used for waveform playback described in Writing Waveform Data. Memory for the WAVEHDR structure and its accompanying data buffer must be allocated and prepared, as shown in Allocating and Preparing Audio Data Blocks.

Windows provides the following functions to manage waveform recording:

Function | Description
waveInAddBuffer | Sends a buffer to the device driver so it can be filled with recorded waveform data.
waveInReset | Stops waveform recording and marks all pending buffers as done.
waveInStart | Starts waveform recording.
waveInStop | Stops waveform recording.

Use the waveInAddBuffer function to send data buffers to the device driver. As the buffers are filled with recorded waveform data, the application is notified with either a window message or with a callback message, depending on the flag specified when the device was opened.

Use the waveInStart function to begin recording. Before beginning to record, send at least one buffer to the driver or incoming data may be lost. To stop waveform recording, use the waveInStop function.

Before closing the device by using the waveOutClose function, call the waveOutReset function to mark any pending data blocks as being done.

+$#A Using Window Messages to Manage Waveform Recording

The following messages can be sent to a window-procedure function for managing waveform recording.

Message | Description
MM_WIM_CLOSE | Sent when the device is closed using the waveInClose function.
MM_WIM_DATA | Sent when the device driver is finished with a data buffer sent using the waveInAddBuffer function.
MM_WIM_OPEN | Sent when the device is opened using the waveInOpen function.

There is a wParam and lParam parameter associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. The lParam parameter is unused for the MM_WIM_CLOSE and MM_WIM_OPEN messages.

For the MM_WIM_DATA message, lParam specifies a pointer to a WAVEHDR structure that identifies the data buffer. This data buffer may not be completely filled with waveform data, because recording can stop before the buffer is filled. Use the dwBytesRecorded member of the WAVEHDR structure to determine the amount of valid data present in the buffer.

The most useful message is MM_WIM_DATA. Unless you need to allocate memory or initialize variables, you probably don't need to use the MM_WIM_OPEN and MM_WIM_CLOSE messages. When the device driver is finished with a data block, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks.

+$# Using a Low-Level Callback Function to Manage Waveform Recording

The syntax of the low-level callback function for waveform input devices is as follows.

void CALLBACK waveInCallback(hWaveIn, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of waveform input callback functions.

Message | Description
WIM_CLOSE | Sent when the device is closed using the waveInClose function.
WIM_OPEN | Sent when the device is opened using the waveInOpen function.

These messages resemble messages sent to window-procedure functions, but their parameters are different. A handle of an open waveform device is passed as a parameter to the callback function, along with the doubleword value of instance data that was passed using waveInOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WIM_CLOSE and WIM_OPEN messages, these parameters are not used. For the WIM_DONE message, dwParam1 specifies a pointer to a WAVEHDR structure identifying the completed data block and dwParam2 is not used.

After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions of low-level audio callback functions, it is better not to do this within the callback function. It is better to set events or semaphores and do this outside the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages.

+$#A Playing MIDI Audio

To play MIDI files, use the MCI sequencer. If the MCI sequencer services don't meet the needs of your application, you can manage MIDI playback using the low-level MIDI services.

+$#A Low-Level Audio MIDI Output Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all low-level audio functions. You must include this header file in any source module that uses these functions. MMSYSTEM.H defines the following data types for low-level MIDI output function:

Type | Description
HMIDIOUT | Handle of a MIDI output device.
MIDIHDR | Structure is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIOUTCAPS | Structure used to inquire about the capabilities of a particular MIDI output device.

+$#A Querying Low-Level MIDI Output Devices

Before playing MIDI audio, you should call the midiOutGetDevCaps function to determine the capabilities of the MIDI output hardware present in the system. This function takes a pointer to a MIDIOUTCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the MIDIOUTCAPS structure provides information on the device technology, the number of voices and notes supported, the MIDI channels that the device responds to, and features supported by the driver.

+$#A Determining the Technology of the Device

MIDI output devices can be either internal synthesizers or external MIDI output ports. The wTechnology member specifies the technology of the device. MMSYSTEM.H defines the following flags to identify device technology.

Flag | Description
MOD_MIDIPORT | The device is an external MIDI output port.
MOD_SQSYNTH | Device is an internal square-wave synthesizer.
MOD_FMSYNTH | Device is an internal FM synthesizer.
MOD_SYNTH | Device is an internal synthesizer (generic).
MOD_MAPPER | Device is the MIDI Mapper.

+$#A Determining Capabilities of Internal Synthesizers

If the device is an internal synthesizer, additional device information is available in the wVoices, wNotes, and wChannelMask members of the MIDIOUTCAPS structure. If the device is an external output port, these members are unused.

The wVoices member specifies the number of voices the device supports. Each voice can have a different sound or timbre. Voices are differentiated by MIDI channel. For example, a four-voice synthesizer uses four MIDI channels. The wNotes member specifies the polyphony of the device ⎯ the maximum number of notes that can be played simultaneously. The wChannelMask member is a bit representation of the MIDI channels that the device responds to. For example, if the device responds to the first eight MIDI channels, wChannelMask is 0x00FF.

The dwSupport member of the MIDIOUTCAPS structure indicates whether the device driver supports volume changes and patch caching. MMSYSTEM.H defines the following flags for the dwSupport member.

Flag | Description
MIDICAPS_VOLUME | Indicates the driver supports volume control.
MIDICAPS_LRVOLUME | Indicates the driver supports individual volume control for the left and right channels.
MIDICAPS_CACHE | Indicates the driver supports patch caching.

Volume changes are only supported by internal synthesizer devices. External MIDI output ports don't support volume changes. For information about changing volume, see Changing Internal MIDI Synthesizer Volume.

+$#A Opening Low-Level Audio MIDI Output Devices

Use the midiOutOpen function to open a MIDI output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Sending MIDI Messages

Once you open a MIDI output device, you can begin sending it MIDI messages by using the following functions.

Function | Description
midiOutLongMsg | Sends a buffer of MIDI data to the specified MIDI output device. Use this function to send multiple MIDI events, including system-exclusive messages, to a MIDI device.
midiOutShortMsg | Sends a MIDI message to a specified MIDI output device. This function can use running status by omitting the status byte.
midiOutReset | Turns off all notes on all channels for a specified MIDI output device. Any pending system-exclusive buffers are marked as done and returned to the application.

Use midiOutShortMsg to send any MIDI message (except for system-exclusive messages). This function takes an HMIDIOUT parameter specifying the MIDI output device to which to send the message, and a doubleword value for the MIDI message. The message is packed into the doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte | Not used
 | Low-order byte | MIDI data 2 (optional)
Low-order word | High-order Byte | MIDI data 1 (optional)
 | Low-order byte | MIDI status

The two MIDI data bytes are optional, depending on the MIDI status byte. The following example uses midiOutShortMsg to send a given MIDI event to a given MIDI output device.

/* Sends a given MIDI event to the given output device */

UINT sendMIDIEvent(hMidiOut, bStatus, bData1, bData2)

HMIDIOUT hMidiOut; // handle of the output device

BYTE bStatus; // MIDI status byte

BYTE bData1; // first MIDI data byte

BYTE bData2; // second MIDI data byte

{

union {

DWORD dwData;

BYTE bData[4];

} u;

/* Construct the MIDI message */

u.bData[0] = bStatus;

u.bData[1] = bData1;

u.bData[2] = bData2;

u.bData[3] = 0;

/* Send the message */

return midiOutShortMsg(hMidiOut, u.dwData);

}

Note MIDI output drivers are not required to verify data before sending it to an output port. It is up to applications to ensure that only valid data is sent using the midiOutShortMsg function.

+$#A Sending Buffered Messages

MIDI system-exclusive messages are the only MIDI messages that will not fit into a single doubleword value. System exclusive messages can be any length. Windows provides the midiOutLongMsg function for sending one or more messages, including system-exclusive messages, to MIDI output devices.

Use the MIDIHDR structure to specify MIDI system-exclusive data blocks. This structure contains a pointer to a locked data block, the data-block length, and assorted flags.

Memory for the MIDIHDR structure and the data block pointed to by lpData must be allocated and prepared, as shown in "Allocating and Preparing Audio Data Blocks."

After you send a system-exclusive data block using midiOutLongMsg, you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the completion of each data block so you know when to send additional blocks. (If your application uses a callback function or callback window, it will receive the MOM_DONE message when the driver finishes with the data block.) For information on different techniques for monitoring data-block completion, see Managing Audio Data Blocks.

Note Any MIDI status byte other than a system-real-time message will terminate a system-exclusive message. If you are using multiple data blocks to send a single system-exclusive message, do not send any MIDI messages other than system-real-time messages between data blocks.

+$#A Using Window Messages to Manage Buffered Playback

The following messages can be sent to a window-procedure function for managing MIDI system-exclusive playback.

Message | Description
MM_MOM_CLOSE | Sent when the device is closed using the midiOutClose function.
MM_MOM_DONE | Sent when the device driver is finished with a data block sent using the midiOutLongMsg function.
MM_MOM_OPEN | Sent when the device is opened using the midiOutOpen function.

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. For the MM_MOM_DONE message, lParam specifies a pointer to a MIDIHDR structure identifying the completed data block. The lParam parameter is unused for the MM_MOM_CLOSE and MM_MOM_OPEN messages.

The most useful message is the MM_MOM_DONE message. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_MOM_OPEN and MM_MOM_CLOSE messages. When playback of a data block is completed, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks.

+$#A Using a Callback to Manage Buffered Playback

This syntax of the low-level callback function for MIDI output devices is as follows.

void CALLBACK midiOutCallback(hMidiOut, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent to the wMsg parameter of MIDI output callback functions.

Message | Description
MOM_CLOSE | Sent when the device is closed using the midiOutClose function.
MOM_DONE | Sent when the device driver is finished with a data block sent using the midiOutLongMsg function.
MOM_OPEN | Sent when the device is opened using the midiOutOpen function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback function along with the doubleword value of instance data passed using midiOutOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the MOM_OPEN and MOM_CLOSE messages, these parameters are not used. For the MOM_DONE message, dwParam1 specifies a pointer to a MIDIHDR structure identifying the completed data block and dwParam2 is not used.

After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages.

+$#A Sending MIDI Messages Using Running-Status

The MIDI 1.0 Specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. You can send MIDI messages using running status by using the midiOutShortMsg function by packing the message into a doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte | Not used
 | Low-order byte | Not used
Low-order word | High-order Byte | MIDI data 2 (optional)
 | Low-order byte | MIDI data 1

+$#A Resetting Low-Level Audio MIDI Output

The midiOutReset function will turn off all notes, and mark any pending system-exclusive buffers as done and return them to the application. It may be useful in an application that uses MIDI output to provide the user with the ability to reset MIDI output.

Note Terminating a system-exclusive message without sending an EOX (end-of-exclusive) byte may cause problems for the receiving device. The midiOutReset function does not send an EOX byte when it terminates a system-exclusive message; applications are responsible for doing this.

+$#A Changing Low-Level Audio Internal MIDI Synthesizer Volume

Windows provides the following functions to query and set the volume level of internal MIDI synthesizer devices.

Function | Description
midiOutGetVolume | Gets the volume level of the specified internal MIDI synthesizer device.
midiOutSetVolume | Sets the volume level of the specified internal MIDI synthesizer device.

Not all MIDI output devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume change, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure your application opens an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restore the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value; the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left channel.

For devices that don't support individual volume changes on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

When querying volume levels using midiOutGetVolume, the volume is returned in a doubleword location specified by a pointer parameter.

+$# Preloading Low-Level Audio Patches With Internal MIDI Synthesizers

Some internal MIDI synthesizer devices can't keep all of their patches loaded simultaneously. These devices must preload their patch data.

Windows provides the following functions to request that a synthesizer preload and cache specified patches.

Function | Description
midiOutCachePatches | Requests that an internal MIDI synthesizer device preload and cache specified melodic patches.
midiOutCacheDrumPatches | Requests that an internal MIDI synthesizer device preload and cache specified key-based percussion patches.

The midiOutCachePatches function takes a pointer to a PATCHARRAY to indicate the patches to be cached.

Each element in the array corresponds to a patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if patch number 0 is used by physical MIDI channels 0 and 8, set element 0 of the array to 0x0101.

The midiOutCacheDrumPatches function takes a pointer to a KEYARRAY to indicate the key-based percussion patches to be cached.

Each element in the array corresponds to a key-based percussion patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if the percussion patch for key number 60 is used by physical MIDI channels 9 and 15, set element 60 of the array to 0x8200.

+$#A Using the MIDI Mapper with Low-Level MIDI Functions

The MIDI Mapper provides standard patch services for device-independent playback of MIDI files. Applications that use MIDI files for audio should use the MIDI Mapper. For information about the MIDI Mapper and about authoring device-independent MIDI files, see High-Level Audio.

To use the MIDI Mapper, open it by using the midiOutOpen function with the wDeviceID parameter set to the constant MIDIMAPPER. You can then send MIDI messages to the MIDI Mapper by using the midiOutShortMsg or midiOutLongMsg function.

+$#A Recording Low-Level MIDI Audio

To record MIDI audio data, you must use low-level MIDI input functions. MCI does not currently provide a device handler for recording MIDI audio.

+$#A Low-Level Audio MIDI Input Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the low-level audio functions. MMSYSTEM.H defines the following data types for low-level MIDI input functions.

Type | Description
HMIDIIN | A handle of a MIDI input device.
MIDIHDR | A structure that is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIINCAPS | A structure used to inquire about the capabilities of a MIDI input device.

+$#A Querying MIDI Input Devices for Low-Level Audio

Before recording MIDI audio, you should call the midiInGetDevCaps function to determine the capabilities of the MIDI input hardware present in the system. This function takes a pointer to a MIDIINCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.

+$#A Opening Low-Level MIDI Input Devices

Use the midiInOpen function to open a MIDI input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Managing MIDI Recording of Low-Level Audio

Once you open a MIDI input device, you can begin recording MIDI data. Win32 provides the following functions for managing MIDI recording.

Function | Description
midiInAddBuffer | Sends a buffer to the device driver so it can be filled with recorded MIDI data.
midiInReset | Stops MIDI recording and marks all pending buffers as done.
midiInStart | Starts MIDI recording and resets the time stamp to zero.
midiInStop | Stops MIDI recording.

Use the midiInAddBuffer function to send data buffers to the device driver for recording system-exclusive messages. Before you use midiInAddBuffer, you must allocate and prepare memory for the MIDIHDR data structure and the data block pointed to by the lpData member. For information about allocating and preparing this memory, see Allocating and Preparing Audio Data Blocks.

Use the midiInStart function to begin recording. To record system-exclusive messages, send at least one buffer to the driver with midiInAddBuffer before starting to record. As the buffers are filled with recorded data, the application is notified by one of the techniques discussed in "Managing Audio Data Blocks."

To stop recording, use the midiInStop function. Before closing the device by using the midiInClose function, call midiInReset to mark any pending data blocks as being done. Before freeing the memory used by a data block, call the midiInUnprepareHeader function to clean up the preparation performed previously.

You must use either a window-procedure function or a low-level callback function to receive MIDI data. If you want time-stamped data, you must use a low-level callback function.

To record system-exclusive messages, you must supply the device driver with data buffers. These buffers are specified by a MIDIHDR structure. This is the same structure used for MIDI buffered playback as described in "Sending Buffered Messages." Memory for the MIDIHDR structure and its accompanying data buffer must be allocated and prepared as shown in Allocating and Preparing Audio Data Blocks.

+$#A Using Window Messages to Manage MIDI Recording

The following messages can be sent to a window-procedure function for managing MIDI recording.

Message | Description
MM_MIM_CLOSE | Sent when the device is closed using the midiInClose function.
MM_MIM_DATA | Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_ERROR | Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_LONGDATA | Sent when either a complete MIDI system-exclusive message is received, or when a data buffer sent using the midiInAddBuffer function is filled with system-exclusive data.
MM_MIM_LONGERROR | Sent when an invalid MIDI system-exclusive message is received.
MM_MIM_OPEN | Sent when the device is opened using the midiInOpen function.

A wParam and an lParam parameter are associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. The lParam parameter is unused for the MM_MIM_CLOSE and MM_MIM_OPEN messages.

For the MM_MIM_DATA message, lParam specifies the received MIDI data. This data is packed into a doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte |
 | Low-order byte | MIDI data 2 (optional)
Low-order word | High-order Byte | MIDI data 1 (optional)
 | Low-order byte | MIDI status

For the MM_MIM_LONGDATA message, lParam specifies a pointer to a MIDIHDR structure that identifies the data buffer for system-exclusive messages. The data buffer might not be completely filled ⎯ you usually don't know the size of the system-exclusive messages before recording them, so you must allocate a buffer large enough for the largest expected message. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.

+$# Using a Low-Level Callback Function to Manage MIDI Recording

This syntax of the low-level callback function for MIDI input devices is as follows:

void CALLBACK midiInCallback(hMidiIn, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of MIDI input callback functions.

Message | Description
MIM_CLOSE | Sent when the device is closed using the midiInClose function.
MIM_DATA | Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_ERROR | Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_LONGERROR | Sent when an invalid MIDI system-exclusive message is received.
MIM_LONGDATA | Sent when either a complete MIDI system-exclusive message is received, or when a data buffer is filled with system-exclusive data.
MIM_OPEN | Sent when the device is opened using the midiInOpen function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback, along with the doubleword value of instance data that was passed using the midiInOpen value.

The callback has two message-specific parameters: dwParam1 and dwParam2. For the MIM_OPEN and MIM_CLOSE messages, these parameters are unused.

For the MIM_DATA message, dwParam1 specifies the received MIDI data and dwParam2 specifies a time stamp for the data. The data is packed into a doubleword value, as shown in the previous section on using window messages.

For the MIM_LONGDATA message, dwParam1 specifies a pointer to a MIDIHDR structure that identifies the data buffer for system-exclusive messages. As with the MIM_DATA message, dwParam2 specifies a time stamp for the data. The data buffer may not be completely filled. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.

After the device driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. It is better to set events or semaphores and do this outside of the callback function. See Using a Callback Function to Process Driver Messages for details about the suggested restrictions on using callback functions.

+$#A Receiving Time-Stamped MIDI Messages

Because of the delay between when the device driver receives a MIDI message and the time the application receives the message, MIDI input device drivers time-stamp the MIDI message with the time the message was received. MIDI time stamps are defined as the time the first byte of the message was received and are specified in milliseconds. The midiInStart function resets the time stamps for a device to zero.

As stated earlier, to receive time stamps with MIDI input, you must use a low-level callback function. The dwParam2 parameter of the callback function specifies the time stamp for data associated with the MIM_DATA and MIM_LONGDATA messages.

+$#A Receiving Running-Status Messages

The MIDI 1.0 specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. All MIDI input device drivers are required to expand messages using running status to complete messages ⎯ you always receive complete MIDI messages from a MIDI input device driver.

+$#A Auxiliary Audio Devices

Auxiliary audio devices are audio devices whose output is mixed with the MIDI and waveform output devices in a multimedia computer. An example of an auxiliary audio device is the compact disc audio output from a CD-ROM drive.

Control for auxiliary audio devices is provided by a software-controlled audio mixer. The mixer can reside on the motherboard of a multimedia computer, or it can be on an add-in sound card. The following illustration shows the conceptual audio-signal routing in a multimedia computer.

In this multimedia computer, there are two auxiliary audio devices: the CD-ROM and the external audio input. The external audio input is an input jack that allows the user to connect other audio devices.

+$#A Querying Low-Level Auxiliary Audio Devices

Not all multimedia systems have auxiliary audio support. You can use the auxGetNumDevs function to determine the number of controllable auxiliary devices present in a system.

To get information on a particular auxiliary audio device, use the auxGetDevCaps function. This function takes a pointer to an AUXCAPS structure that it fills with information about the capabilities of a given device.

+$#A Determining Auxiliary Audio Device Types

The MMSYSTEM.H header file defines the following flags for the wTechnology member of the AUXCAPS structure to determine the device type of an auxiliary audio device.

Flag | Description
AUXCAPS_CDAUDIO | The device is an internal CD-ROM drive.
AUXCAPS_AUXIN | The device is an auxiliary audio input jack.

+$#A Determining Capabilities of Auxiliary Audio Devices

The dwSupport member of the AUXCAPS structure indicates whether the device driver supports volume changes. The MMSYSTEM.H header file defines the following flags for the dwSupport member.

Flag | Description
AUXCAPS_VOLUME | Volume-control support
AUXCAPS_LRVOLUME | Individual volume-control support for both the left and right channels

+$#A Changing Auxiliary Audio-Device Volume

Windows provides the following functions to query and set the volume for auxiliary audio devices.

Function | Description
auxGetVolume | Gets the volume level of the specified auxiliary audio device.
auxSetVolume | Sets the volume level of the specified auxiliary audio device.

Not all auxiliary audio devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume changes, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, you should open an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

The volume level is specified (as it was in the waveform and MIDI volume-control functions) in a doubleword value with the high-order 16 bits specifying the relative volume of the right channel and the low-order 16 bits specifying the relative volume of the left channel. For devices that don't support individual volume control on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored.

Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. This means the perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

When querying volume levels with the auxGetVolume function, the volume is returned in a doubleword location specified by a pointer parameter.

+$#A Audio Clipboard Formats

In addition to the standard and nonstandard waveform formats previously presented in this section, there are two Clipboard formats that can be used to represent audio data: CF_WAVE and CF_RIFF. Use the CF_WAVE format to represent data in one of the standard formats, such as 11 kHz or 22 kHz PCM. Use the CF_RIFF format to represent more complex data formats that cannot be represented as standard wave files.

+$#A Low-Level Audio Functions

Following are the functions used with low-level audio services.

auxGetDevCaps
auxGetNumDevs
auxGetVolume
auxOutMessage
auxSetVolume
midiInAddBuffer
midiInClose
midiInGetDevCaps
midiInGetErrorText
midiInGetID
midiInGetNumDevs
midiInMessage
midiInOpen
midiInPrepareHeader
midiInReset
midiInStart
midiInStop
midiInUnprepareHeader
midiOutCacheDrumPatches
midiOutCachePatches
midiOutClose
midiOutGetDevCaps
midiOutGetErrorText
midiOutGetID
midiOutGetNumDevs
midiOutGetVolume
midiOutLongMsg
midiOutMessage
midiOutOpen
midiOutPrepareHeader
midiOutReset
midiOutSetVolume
midiOutShortMsg
midiOutUnprepareHeader
MidiProc
waveInAddBuffer
waveInClose
waveInGetDevCaps
waveInGetErrorText
waveInGetID
waveInGetNumDevs
waveInGetPosition
waveInMessage
waveInOpen
waveInPrepareHeader
waveInReset
waveInStart
waveInStop
waveInUnprepareHeader
waveOutBreakLoop
waveOutClose
waveOutGetDevCaps
waveOutGetErrorText
waveOutGetID
waveOutGetNumDevs
waveOutGetPitch
waveOutGetPlaybackRate
waveOutGetPosition
waveOutGetVolume
waveOutMessage
waveOutOpen
waveOutPause
waveOutPrepareHeader
waveOutReset
waveOutRestart
waveOutSetPitch
waveOutSetPlaybackRate
waveOutSetVolume
waveOutUnprepareHeader
waveOutWrite
WaveProc

+$#A Media Control Interface

The Media Control Interface (MCI) provides applications written for Microsoft® Windows® with device-independent capabilities for controlling media devices such as audio hardware, videodisc players, and animation players. This overview presents an introduction to MCI and shows how to begin using it in Windows-based applications.

+$#A About the Media Control Interface

MCI provides a device-independent, extensible interface for controlling virtually any type of media device. Because of the high level of device independence provided by MCI, you are encouraged to use MCI rather than low-level functions to access the multimedia capabilities of Windows.

Click on a topic below to view it:

• The Architecture of MCI

• The MCI Command Set

• MCI Programming Interfaces

• The Command-Message interface

• The Command-String Interface (an introduction)

For more information about using MCI to control different types of audio devices, see High-Level Audio.

+$#A The Architecture of MCI

To provide extensibility, MCI is designed around an architecture that uses special MCI device drivers to interpret and execute MCI commands.

MCI device drivers can control media hardware directly or through the low-level multimedia functions provided by Windows. Most commonly used devices, such as waveform audio and Musical Instrument Digital Interface (MIDI) devices, are controlled through the low-level multimedia functions.

+$#A The MCI Programming Interfaces

MCI provides two programming interfaces: command string and command message.

+$#A The Command-String Interface

The command-string interface allows you to use English-language commands to communicate with MCI devices. For example, the following command string plays a WAVE file named TIMPANI.WAV.

play timpani.wav

The command-string interface is designed for use with high-level programming and authoring environments such as Microsoft® Visual Basic® and Asymetrix® ToolBook®. Use the command-string interface for applications that provide a text-based interface to let users control MCI devices.

+$#A The Command-Message Interface

The command-message interface uses a message-passing paradigm to communicate with MCI devices. The following example performs the same operation as the command-string example in The Command-String Interface.

WORD wDeviceID;

MCI_PLAY_PARMS mciPlayParms;

mciSendCommand(wDeviceID, /* device identifier */

MCI_PLAY, /* command message */

0, /* flags */

(DWORD) (LPVOID) &mciPlayParms); /* structure */

The command-message interface is designed to be used by applications requiring a C-language interface to control multimedia devices. Use the command-message interface for applications that directly control multimedia devices.

+$#A The MCI Command Set

The MCI command set provides a generic core set of commands to control different types of media devices. For example, MCI uses the same command to begin playback of a waveform audio file, a videodisc track, and an animation sequence. Some devices have unique capabilities, such as the capability of an animation player to use a frame-based time format. For such devices, MCI provides extended commands that are unique to a particular type of device.

The commands in the command-string interface provide a good overview of the MCI command set. Each of these commands is represented by a similar command message in the command-message interface. For example, the close command string is the equivalent of the MCI_CLOSE command message.

Following are some commonly used commands.

Command | Description
capability | Requests information about the capabilities of a device.
close | Closes a device after it has been used.
info | Requests information about a device (for example, a description of the hardware associated with the device).
open | Opens and initializes a device.
pause | Pauses playing or recording on a device.
play | Begins playing on a device.
record | Begins recording on a device.
resume | Resumes playing or recording on a paused device.
seek | Changes the current position in the media.
set | Changes control settings, such as the time format the device is using.
status | Requests information about the status of a device (for example, whether the device is playing or paused).
stop | Stops playing or recording on a device.

+$#A About MCI Devices

MCI device drivers can be classified as either simple devices or compound devices. Simple devices do not require a data file for playback. Videodisc players and compact disc (CD) audio players are examples of simple devices. Compound devices require a data file for playback. MIDI sequencers and waveform audio players are examples of compound devices. The data file associated with a compound device is known as a device element. Examples of device elements are MIDI files and WAVE files.

+$#A Standard MCI Device Types

A device type identifies a class of MCI devices that responds to a common set of commands. Following are the currently defined device types.

Device type | Description
animation | Animation device
cdaudio | CD audio player
dat | Digital audio tape player
digitalvideo | Digital video in a window (not based on graphics device interface)
other | Undefined MCI device
overlay | Overlay device (analog video in a window)
scanner | Image scanner
sequencer | MIDI sequencer
videodisc | Videodisc player
waveaudio | Audio device that plays digitized waveform files

+$#A MCI Device Names

For any given device type, there might be several MCI drivers that share the command set but operate on different data formats. For example, for animation devices there are several MCI drivers that use the same command set but use different file formats. To uniquely identify an MCI driver, MCI uses device names.

Device names are identified in the [mci32] section of the registry. This section identifies all MCI device drivers to Windows. The following is part of a typical [mci] section.

[mci32]

WaveAudio=mciwave.dll

Sequencer=mciseq.dll

AVIVideo=mciavi.dll

CDAudio=mcicda.dll

The key name (on the left side of the equal sign) is the device name. The value corresponding to the key name (on the right side of the equal sign) identifies the filename of the MCI driver. The device name is often the same as the device type for the driver, as is the case for the WaveAudio, Sequencer, and CDAudio devices in the preceding example. The AVIVideo device is a digital video device, but it uses a unique device name.

If an MCI driver is installed using a device name that already exists in the [mci32] section, Windows appends an integer to the device name of the new driver, creating a unique device name. In the preceding example, a driver installed using the cdaudio device name would be assigned device name cdaudio1. A subsequent cdaudio device would be assigned device name cdaudio2.

+$#A Opening MCI Devices

Before using an MCI device, you must initialize it by opening it. Opening a device loads its driver into memory (if it isn't already loaded) and establishes a device identifier to identify the device in subsequent commands. (The device identifier is not used in the command-string interface.) There are several ways to specify the device you want to open:

• With simple devices, you can open the device by specifying the device name.

• With compound devices, you can open the device by specifying the device name, the device element, or both.

For example, the following command string opens a CD audio device (a simple device) by specifying the device name.

open cdaudio

The next command string opens a waveform audio device (a compound device) by specifying the device name and a device element.

open bells.wav type waveaudio

You can also open a compound device by specifying only the device element, as shown in the following example.

open bells.wav

If you specify only a device element when opening a compound device, MCI uses the filename extension of the device element filename to determine which device to open. The registry contains an [mci extensions] section that associates filename extensions and corresponding MCI device types. The following is part of an [mci extensions] section.

[mci extensions]

Wav=WaveAudio

Mid=Sequencer

rmi=Sequencer

avi=avivideo

+$#A Using the Command-Message Interface

Applications that require a C-language interface to MCI should use the command-message interface. The following topics provide background and detailed information about using this interface.

About Command Messages
Summary of MCI Command Messages
Sending MCI Command Messages
Opening an MCI Device
Closing an MCI Device
Using the Wait and Notify Flags
Using MCI_SYSINFO to Obtain MCI System Information
About the Command-String Interface
Sending MCI Command Strings
Additional Information About Command Strings

+$#A About Command Messages

MCI command messages consist of the following three elements:

• A constant message value

• A structure (parameter block) specifying additional parameters for the command

• A set of flags specifying options for the command and validating members in the structure

Commands are sent by using the mciSendCommand function, which has parameters for the message, flags, and a pointer to the structure. For more information about the mciSendCommand function, see Sending MCI Command Messages.

+$#A Command-Message Example

For an example of an MCI command message, consider MCI_PLAY. The MMSYSTEM.H header file defines the MCI_PLAY command message, the MCI_NOTIFY, MCI_WAIT, MCI_FROM, and MCI_TO flags, and the MCI_PLAY_PARMS structure. The MCI_PLAY_PARMS structure has the following form.

typedef struct {

DWORD dwCallback;

DWORD dwFrom;

DWORD dwTo;

} MCI_PLAY_PARMS;

The MCI_WAIT and MCI_NOTIFY flags are option flags. MCI_WAIT tells the driver to wait until the command is complete before returning control to the application. MCI_NOTIFY tells the driver to notify the application when the command is complete. For more information on using these flags, see Using the Wait and Notify Flags.

The MCI_FROM and MCI_TO flags validate the dwFrom and dwTo members in the MCI_PLAY_PARMS structure. These members specify beginning and ending positions for the play operation. Both flags are optional. If either flag is specified, the driver takes the value from the corresponding member in the structure as a beginning or ending position; otherwise, the members are ignored.

Note Members in a structure are ignored unless corresponding flags are specified to validate the members.

+$#A Items in Query Commands

Two command messages that query devices for information, MCI_GETDEVCAPS and MCI_STATUS, extend the command-message paradigm by adding a dwItem member to the structure and defining item constants for the member. Item constants specify which item of information is being requested. For example, MMSYSTEM.H defines the MCI_STATUS_PARMS structure for the MCI_STATUS command message as follows.

typedef struct {

DWORD dwCallback;

DWORD dwReturn;

DWORD dwItem;

DWORD dwTrack;

} MCI_STATUS_PARMS;

If you use the MCI_STATUS_ITEM flag to validate the dwItem member, make sure that dwItem contains a constant defined by MMSYSTEM.H to indicate the exact status information requested. The MCI_STATUS command message uses the following constants for dwItem:

MCI_STATUS_CURRENT_TRACK
MCI_STATUS_LENGTH
MCI_STATUS_MEDIA_PRESENT
MCI_STATUS_MODE
MCI_STATUS_NUMBER_OF_TRACKS
MCI_STATUS_POSITION
MCI_STATUS_READY
MCI_STATUS_TIME_FORMAT

+$#A Summary of MCI Command Messages

MCI defines four classifications of command messages. The command messages and options in the following two classifications are defined as the minimum command set for any MCI driver:

• System command messages are handled directly by MCI rather than by the driver.

• Required command messages are handled by the driver. All drivers should support the required command messages and options.

Regardless of the specific driver you use, you should be able to assume that the command messages and options in the two preceding groups are available.

The command messages in the following two classifications are not supported by all drivers:

• Basic (or optional) command messages are used by some devices. If a device supports a basic command message, it must support a defined set of options for the command message.

• Extended command messages are specific to certain device types or drivers. They include new command messages (for example, the MCI_PUT and MCI_WHERE command messages for the animation device type) and extensions to existing command messages (for example, the MCI_ANIM_GETDEVCAPS_CAN_STRETCH option added to the MCI_STATUS command message for the animation device type).

If you need to use a basic or extended command message or option, you should query the device driver before trying to use the command message or option (that is, unless you are certain that the MCI driver you have used during development is the same one that will be available on the delivery system). The following tables summarize the specific command messages in each category.

MCI directly processes the following system command messages, rather than passing them to MCI device drivers.

Command | Description
MCI_BREAK | Sets a break key for an MCI device.
MCI_SYSINFO | Returns information about MCI devices.

Required command messages must be supported by all MCI device drivers. The following table lists the required command messages.

Command | Description
MCI_CLOSE | Closes the device.
MCI_GETDEVCAPS | Obtains the capabilities of a device.
MCI_INFO | Obtains textual information from a device.
MCI_OPEN | Initializes the device.
MCI_STATUS | Obtains status information from the device.

Following are the basic command messages. The use of these command messages by a device is optional.

Command | Description
MCI_LOAD | Loads data from a file.
MCI_PAUSE | Stops playing.
MCI_PLAY | Starts transmitting output data.
MCI_RECORD | Starts recording data.
MCI_RESUME | Resumes playing or recording on a paused device.
MCI_SAVE | Saves data to a file.
MCI_SEEK | Seeks forward or backward.
MCI_SET | Sets the operating state of the device.
MCI_STATUS | Obtains status information about the device. (Supplements the required MCI_STATUS command message with options for devices using linear media with identifiable positions.)
MCI_STOP | Stops playing or recording.

Some MCI devices have additional command messages or add options to existing command messages. Although some extended command messages apply only to a specific device driver, most of them apply to all drivers of a particular device type. For example, the sequencer command set extends the MCI_SET command message to add time formats that are needed by MIDI sequencers.

Unless you are certain that the specific MCI driver you use during development will be available on the delivery system, do not assume that the device supports the extended command messages or options. You can use the MCI_GETDEVCAPS command message to determine whether a specific feature is supported. Applications should be ready to handle an MCIERR_UNSUPPORTED_FUNCTION return value.

The following extended command messages are available with certain device types.

Command Message | Device types | Description
MCI_CUE | waveaudio | Prepares for playing or recording.
MCI_DELETE | waveaudio | Deletes a data segment from the media element.
MCI_ESCAPE | videodisc | Sends custom information to a device.
MCI_FREEZE | overlay | Disables video acquisition to the frame buffer.
MCI_PUT | animation overlay | Defines the source, destination, and frame windows.
MCI_REALIZE | animation | Tells the device to select and realize its palette into a display context of the displayed window.
MCI_SPIN | videodisc | Starts the disc spinning or stops the disc from spinning.
MCI_STEP | animation, videodisc | Steps the play one or more frames forward or reverse.
MCI_UNFREEZE | overlay | Enables the frame buffer to acquire video data.
MCI_UPDATE | animation | Repaints the current frame into the display context.
MCI_WHERE | animation, overlay | Obtains the rectangle specifying the source, destination, or frame area.
MCI_WINDOW | animation, overlay | Controls display window options such as caption text.

+$#A Sending MCI Command Messages

Windows provides the following functions to send command messages to devices and to query devices for error information.

Function | Description
mciSendCommand | Sends a command message to an MCI device.
mciGetErrorString | Returns the error string corresponding to an error number.

+$#A The MCI Device Identifier

MCI returns a device identifier when you open a device by using the MCI_OPEN command message. Use this identifier to identify the opened device when sending subsequent commands.

MMSYSTEM.H defines a special constant, MCI_ALL_DEVICE_ID, to indicate that a command message is to be sent to all of the MCI devices that an application has opened. You can use MCI_ALL_DEVICE_ID with any command message that does not return information. For instance, the following example closes all of the MCI devices that have been opened by an application.

DWORD dwReturn;

/*

* Closes all MCI devices opened by this application.

* Waits until devices are closed before returning.

*/

if (dwReturn = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE,

MCI_WAIT, NULL))

/* Error, unable to close all devices. */

return FALSE;

Note Although using the MCI_ALL_DEVICE_ID identifier is a convenient way to broadcast a command to all your devices, don't rely on it to synchronize devices; the timing between messages can vary.

+$#A Opening an MCI Device

Before using a device, you must initialize it by using the MCI_OPEN command message. The variations of this command message make it one of the most complex of all MCI commands.

+$#A The MCI_OPEN_PARMS Structure

Like all MCI command messages, MCI_OPEN has an associated structure. The default structure for MCI_OPEN is the MCI_OPEN_PARMS structure. Certain devices, such as waveaudio, animation, and overlay devices, have extended structures to accommodate additional optional parameters. Unless you need to use these additional parameters, you can use the MCI_OPEN_PARMS structure with any MCI device.

MCI uses the wDeviceID member of this structure to return the device identifier to the application. You should check the return value of the mciSendCommand function before using the device identifier, to be sure that it is valid. A nonzero return value indicates that an error occurred during the opening of the device process.

Note You can also determine the device identifier by specifying the device name in a call to the mciGetDeviceID function.

Other members used in opening a device correspond to the following flags for the command message.

Flag | Meaning
MCI_OPEN_ALIAS | The lpstrAlias member of the structure contains a pointer to a device alias.
MCI_OPEN_ELEMENT | The lpstrElementName member of the structure contains a pointer to the element name.
MCI_OPEN_SHAREABLE | The device or element was opened as shareable.
MCI_OPEN_TYPE | The lpstrDeviceType member of the structure contains a pointer to the device-type identifier.
MCI_OPEN_TYPE_ID | The lpstrDeviceType member of the structure contains an integer device-type identifier.

+$#A Opening Simple Devices

To open a simple device such as a CD audio or videodisc device, use the lpstrDeviceType member in the MCI_OPEN_PARMS structure to identify the device to open. There are three ways to identify devices:

• Specify a pointer to a null-terminated string containing the device name.

• Specify a device-type constant.

• Specify the actual name of the device driver.

Regardless of which method you use to identify the device, you must specify the MCI_OPEN_TYPE flag to validate the lpstrDeviceType member. For instance, the following example opens a CD audio device by specifying the device name.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/* Open a compact disc device by specifying the device name. */

mciOpenParms.lpstrDeviceType = "cdaudio";

if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying a device-type constant. To be opened by means of a device-type constant, device drivers must be installed in the [mci] section of the registry by using the following device types.

Device type | Device-type constant
animation | MCI_DEVTYPE_ANIMATION
cdaudio | MCI_DEVTYPE_CD_AUDIO
dat | MCI_DEVTYPE_DAT
digitalvideo | MCI_DEVTYPE_DIGITAL_VIDEO
other | MCI_DEVTYPE_OTHER
overlay | MCI_DEVTYPE_OVERLAY
scanner | MCI_DEVTYPE_SCANNER
sequencer | MCI_DEVTYPE_SEQUENCER
videodisc | MCI_DEVTYPE_VIDEODISC
waveaudio | MCI_DEVTYPE_WAVEFORM_AUDIO

If you open a device by specifying a device-type constant, you must specify the MCI_OPEN_TYPE_ID flag in addition to the MCI_OPEN_TYPE flag. The following example opens a CD audio device by specifying a device-type constant.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/*

* Open a compact disc device by

* specifying a device-type constant.

*/

mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

When you open a device by specifying a device-type constant, MCI uses the high-order word of the lpstrDeviceType member to distinguish between multiple occurrences of the device type in the registry.

For example, if the registry lists entries for waveaudio1 and waveaudio2, the following statement sets the lpstrDeviceType member to specify the device identified by waveaudio2.

LPSTR lpstrDeviceType;

lpstrDeviceType = (LPSTR)MAKELONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 2);

Similarly, to specify the device identified by waveaudio1, set the high-order word of lpstrDeviceType to 0 or 1. (Setting the high-order word to 0 tells MCI to use an unnumbered device or the lowest-numbered device.)

You can also open a device by specifying the filename of the device driver in the lpstrDeviceType member of the MCI_OPEN_PARMS structure. You don't need to specify the complete path or the filename extension ⎯ MCI assumes that drivers are located in the Windows system directory and have a filename extension of .DRV.

Note Opening MCI devices by specifying the filename of the device driver may be useful during development and testing. However, this technique is not recommended for applications because it makes them device dependent. Applications that open MCI devices by filename may not work in all systems.

+$#A Opening Compound Devices

There are three ways to open a compound device:

• Specify only the device (by using the device name, a device-type constant, or the filename of the device driver).

• Specify both the device and the device element.

• Specify only the device element.

The first method ⎯ specifying only the device ⎯ is useful for determining the capabilities of a device by using the MCI_GETDEVCAPS command message. To actually control a media device, you must specify a device element when you open the device.

To specify a device element, fill the lpstrElementName member of the MCI_OPEN_PARMS structure with a pointer to a null-terminated string containing the filename of the device element. You must also validate the lpstrElementName member by specifying the MCI_OPEN_ELEMENT flag in the dwFlags parameter of the mciSendCommand function.

You can specify the device in the same manner used for simple devices: by device name, by device-type constant, or by the filename of the driver. The following example opens the waveaudio device with a WAVE file named TIMPANI.WAV.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/*

* Open a waveaudio device by specifying

* the device name and device element.

*/

mciOpenParms.lpstrDeviceType = "waveaudio";

mciOpenParms.lpstrElementName = "timpani.wav";

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying just a device element. MCI uses the filename extension of the given device element along with the [mci extensions] section of the registry to select the device to open. For more information about opening devices by specifying only a device element, see Opening MCI Devices.

+$#A Closing an MCI Device

The MCI_CLOSE command message releases access to a device or device element. MCI frees a device when all processes using a device have closed it. To help MCI manage devices, applications should explicitly close each device or device element when they are finished using it.

Note It is very important that applications close all opened MCI devices before terminating. You can close all devices with a single command message by specifying the MCI_ALL_DEVICE_ID constant as the device identifier.

+$#A Using the Wait and Notify Flags

Typically, MCI returns control to the application immediately, even if it takes several seconds to complete the action initiated by a command. For example, after a CD audio device receives a seek command, control returns to the application before the seek operation is complete. You can use the following two flags to request that MCI wait until a command is complete before returning control to the application, or that MCI notify the application when the command is complete.

Flag | Description
MCI_NOTIFY | Directs the device to return control immediately and post the MM_MCINOTIFY message when the requested action is complete.
MCI_WAIT | Directs the device to wait until the requested action is complete before returning control to the application.

+$#A Using the Notify Flag

The MCI_NOTIFY flag directs MCI to post the MM_MCINOTIFY message when the device completes an action. The application specifies the handle of the destination window for the message in the dwCallBack member of the structure sent with the command message. Every structure associated with a command message contains this member.

Note MCI can have only one pending notification message for each device.

When MCI posts the MM_MCINOTIFY message, it sets the low-order word of the lParam parameter of the window procedure to the identifier of the device initiating the callback. The wParam parameter of the window procedure contains one of the following constants specifying the notification status of the callback.

Flag | Meaning
MCI_NOTIFY_SUCCESSFUL | The conditions required for initiating the callback are satisfied and the command finished without interruption.
MCI_NOTIFY_SUPERSEDED | MCI had a notification pending and received another notify request. MCI resets the callback conditions to correspond to the notify request of the new command.
MCI_NOTIFY_ABORTED | The application sent a new command that prevented the callback conditions set by a previous command from being satisfied. If an application interrupts a command that requested notification, MCI does not inform the window procedure of the notify command being aborted.
MCI_NOTIFY_FAILURE | A device error occurred while a device was executing the command. For example, MCI posts this message when a hardware error occurs during a play command.

+$#A Using the Wait Flag and Break Command Message

Specifying the MCI_WAIT flag with a command message causes MCI to wait until the command has finished before returning control to the application. Using the wait flag with commands that take a long time essentially disables a system from receiving any user input until the command is complete.

MCI supports a break key to interrupt the wait operation and return control to the application. The break key does not interrupt the command, but by returning control to the application, it allows the application to monitor user input.

By default, MCI defines the break key as CTRL+BREAK. Applications can redefine this key by using the MCI_BREAK command message. MCI_BREAK uses the MCI_BREAK_PARMS structure.

The following flags validate members in the MCI_BREAK_PARMS structure:

Flag | Description
MCI_BREAK_HWND | Validates the hwndBreak member, which specifies the window that must have the focus to enable break detection.
MCI_BREAK_KEY | Validates the nVirtKey member, which specifies the virtual-key code to be used for the break key.
MCI_BREAK_OFF | Disables any existing break key.

+$#A Using MCI_SYSINFO to Obtain MCI System Information

The MCI_SYSINFO command message obtains system information about MCI devices. MCI handles this message without relaying it to any MCI device. MCI returns the system information in the MCI_SYSINFO_PARMS structure.

The MCI_SYSINFO command message has the following flags.

Flag | Meaning
MCI_SYSINFO_QUANTITY | MCI returns the number of devices of a given type listed in the [mci] section of the registry. When this flag is used with the MCI_SYSINFO_OPEN flag, the number of open devices is returned.
MCI_SYSINFO_NAME | MCI returns the name of a device that satisfies the query. When this flag is used with the MCI_SYSINFO_OPEN flag, only the names of open devices are returned.
MCI_SYSINFO_OPEN | MCI returns the number or names of open devices.
MCI_SYSINFO_INSTALLNAME | MCI returns the name listed in the registry entry used to install the device.

You use the MCI_SYSINFO_QUANTITY flag to determine the number of devices for a particular type listed in the [mci] section of the registry. For this flag, set the wDeviceType member of the structure to the device type. When requesting the number of devices, set the wDeviceID parameter to NULL. MCI returns the number of devices as a doubleword in the lpstrReturn member of the structure. If you combine the MCI_SYSINFO_QUANTITY flag with the MCI_SYSINFO_OPEN flag, MCI returns the number of devices of that type opened by the process.

Once you have the number of devices, you can enumerate the names of the devices by using the MCI_SYSINFO_NAME flag. To get information by using this flag, you must create a buffer for the return name. In the structure, specify a pointer to the buffer, the size of the buffer, the index number corresponding to the device (1 is the first device), and the device type. This information is entered in the lpstrReturn, dwRetSize, dwNumber, and wDeviceType members. MCI returns the device name in the buffer. When requesting the name of a particular device, set the wDeviceID parameter to NULL. To restrict the names to open devices, use the MCI_SYSINFO_OPEN flag with MCI_SYSINFO_NAME.

To retrieve information on all devices in the system, you can assign the MCI_ALL_DEVICE_ID constant to the wDeviceID parameter. When you use this identifier, MCI ignores the contents of the wDeviceType member and returns information on all MCI devices listed in the registry. When you use the MCI_SYSINFO_OPEN flag with the MCI_ALL_DEVICE_ID identifier, MCI returns information about the devices opened by the task.

You can retrieve the installation name of an open device by using the MCI_SYSINFO_INSTALLNAME flag. To get the installation name, you must create a buffer for the return name. In the structure, specify a pointer to the buffer and the size of the buffer in the lpstrReturn and dwRetSize members. MCI places the device that corresponds to the wDeviceID parameter name in the buffer.

+$#A About the Command-String Interface

This section presents an introduction to the MCI command-string interface. MCI provides the following functions for working with command strings.

Function | Description
mciSendString | Sends a command string to an MCI device driver. This function also has parameters for callback functions and return strings.
mciGetErrorString | Returns the error string corresponding to an error number.

+$#A Sending MCI Command Strings

Use the mciSendString function to send command strings to a device. The following statement passes the "play to 500" command string to a waveaudio device driver.

DWORD dwErrorCode;

dwErrorCode = mciSendString("play waveaudio to 500", NULL, 0, 0L);

If the mciSendString function succeeds, it returns zero. If the function fails, it returns an error code. To get extended error information, use the mciGetErrorString function.

+$#A Additional Information About Command Strings

The MCITEST application contains examples of using the mciSendString and mciGetErrorString functions. You can use this application to experiment with MCI devices and command strings.

+$#A Multimedia File Input/Output

Most multimedia applications require file input/output (I/O) ⎯ the ability to create, read, and write disk files. The multimedia file I/O services provided by the Microsoft® Win32® application programming interface (API) offer buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible with custom I/O procedures.

+$#A About the Multimedia File Input/Output Services

Most applications need only the basic file I/O services and the RIFF file I/O services. Applications sensitive to file I/O performance, such as applications that stream data from a CD-ROM in real time, can optimize performance by using services to directly access the file I/O buffer. Applications that access custom storage systems can provide their own I/O procedure that reads and writes elements of the storage system. A storage system is a method of physically storing data in a file, such as a file-archival system or a database storage system.

This section covers the following topics:

• Using basic file I/O services

• Performing buffered file I/O

• Working with RIFF files

• Directly accessing a file I/O buffer

• Using memory files

• Writing a custom I/O procedure

The multimedia file I/O services provide support for the following file I/O operations:

• Basic unbuffered and buffered file I/O

• RIFF file I/O

• Direct access to the file I/O buffer

• Memory files

• Custom storage system I/O using application-supplied I/O procedures

+$#A Comparison With Other File I/O

You might ask why you need another set of file I/O services, when you already have the services of the C run-time and Win32 API libraries. The multimedia file I/O services offer the following advantages over other file I/O services:

• They are a part of the system software, so they don't increase the size of your application, like linking to the C run-time libraries

• They provide more functionality than the Win32 services, such as support for buffered I/O, RIFF files, memory files, and custom storage systems

In addition, the multimedia file I/O services are optimized for performance-intensive applications.

+$#A Function Prefixes

All multimedia file I/O function names begin with the mmio prefix. Similarly, all multimedia file I/O message names begin with the MMIOM_ prefix.

+$#A Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all multimedia file I/O functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H depends on declarations made in WINDOWS.H, so you must first include the WINDOWS.H header file. MMSYSTEM.H defines the following data types for the multimedia file I/O functions.

Type | Description
FOURCC | A four-character code identifying an element of a RIFF file.
HMMIO | A handle of an open file.
MMCKINFO | A structure containing information about a chunk in a RIFF file.
MMIOINFO | A structure used to maintain the current state of a file accessed using the multimedia file I/O services.
MMIOPROC | A custom multimedia file I/O procedure.

+$#A Performing Basic File Input/Output

Using the basic I/O services is similar to using the C run-time file I/O services. Files must be opened before they can be read or written. After reading or writing, the file must be closed. You can change the current read/write location by seeking to a specified position in an open file. The following table lists the basic file I/O functions.

Function | Description
mmioClose | Closes an open file.
mmioOpen | Opens a file for reading or writing or both and returns a handle of the open file.
mmioRead | Reads a specified number of bytes from an open file.
mmioSeek | Changes the current position for reading or writing in an open file.
mmioWrite | Writes a specified number of bytes to an open file.

These file I/O functions provide the core of the multimedia file I/O services ⎯ you can use them for buffered and unbuffered I/O, as well as for I/O to RIFF files, memory files, and custom storage systems.

+$#A Opening a Multimedia File

Before doing any I/O operations to a file, you must first open the file by using the mmioOpen function. The mmioOpen function returns a file handle that you use to identify the open file when calling other file I/O functions.

Note Do not use HMMIO file handles with Windows or C run-time file I/O functions.

There are options you can use with the mmioOpen function for operations beyond basic file I/O. By specifying an MMIOINFO structure with the lpmmioinfo parameter you can open memory files, specify a custom I/O procedure, or supply a buffer for buffered I/O. These topics are discussed later in this section. First, this section discusses the most basic use of mmioOpen ⎯ opening files for basic unbuffered file I/O.

To open a file for basic I/O operations, set the lpmmioinfo parameter of mmioOpen to NULL. For example, the following example opens a file named C:\SAMPLES\SAMPLE1.TXT for reading, and checks the return value for errors.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

if ((hFile = mmioOpen("C:\\SAMPLES\\SAMPLE1.TXT",

NULL, MMIO_READ)) != NULL) /* File opened successfully */

return TRUE;

else

/* File cannot be opened. */

return FALSE;

+$#A Options for Opening a File

When you open a file, you must specify whether you are opening the file for reading, writing, or both reading and writing. In addition, you can specify other options, such as to create or delete a new file. Use the dwFlags parameter of mmioOpen to specify options for opening a file.

Following are the basic options for opening a file using mmioOpen:

Flag | Description
MMIO_READ | Opens a file for reading only.
MMIO_WRITE | Opens a file for writing only.
MMIO_READWRITE | Opens a file for reading and writing.
MMIO_CREATE | Creates a new file (if the file already exists, it truncates it to zero length).
MMIO_DELETE | Deletes a file.
MMIO_ALLOCBUF | Opens a file for buffered I/O.

The MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags are read/write privilege flags. These flags are mutually exclusive ⎯ specify only one when opening a file. If you don't specify one of these flags, mmioOpen opens the file for reading only.

The MMIO_CREATE and MMIO_DELETE flags are also mutually exclusive. You can specify one of the read/write privilege flags with the MMIO_CREATE flag. You can't specify any additional flags with the MMIO_DELETE flag.

For information on using the MMIO_ALLOCBUF flag, see Performing Buffered File Input/Output.

In addition to the basic options for opening a file, there are sharing options you can use for opening files so they can be opened and accessed by more than one process. Following are the sharing options for opening a file using mmioOpen.

Flag | Description
MMIO_COMPAT | Opens a file in compatibility mode.
MMIO_EXCLUSIVE | Opens a file in exclusive mode.
MMIO_DENYWRITE | Opens a file and denies other processes write access to the file.
MMIO_DENYREAD | Opens a file and denies other processes read access to the file.
MMIO_DENYNONE | Opens a file without denying other processes read or write access to the file.

+$#A Creating and Deleting Files

To create a new file, specify the MMIO_CREATE option with the mmioOpen function. For example, the following example creates a new file and opens it for both reading and writing.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

hFile = mmioOpen("NEWFILE.TXT",

NULL, MMIO_CREATE | MMIO_READWRITE);

if (hFile != NULL)

/* File created successfully. */

return TRUE;

else

/* File could not be created. */

return FALSE;

Note If the file you are creating already exists, it will be truncated to zero length.

To delete a file, specify the MMIO_DELETE flag with the mmioOpen function. Once you delete a file, it can't be recovered. If the file deletion is the result of a request from a user, always query the user to be sure the user wants to delete the specified file before you actually delete it.

+$#A Reading and Writing a File

To read and write to open files, use the mmioRead and mmioWrite functions. Each of these functions takes an HMMIO file handle, a pointer to a buffer, and a parameter specifying the number of bytes to read or write.

For an example using mmioRead to read from a file, see Example of RIFF File I/O.

+$#A Seeking to a New Position in a Multimedia File

The current position or file pointer in a file is the location where the next read or write operation will occur. To change the current position in an open file, use the mmioSeek function.

The return value is the new position, specified in bytes from the beginning of the file. If an error occurs, the return value is −1. If you seek to an invalid location in a file, such as past the end of the file, mmioSeek may not return an error, but subsequent I/O operations can fail.

+$#A Performing Buffered File Input/Output

Most of the overhead in file I/O involves accessing the media (the physical device). If you are reading or writing many small blocks of information, the media device can spend a lot of time seeking the physical location on the media for each read or write operation. In this case, better performance is achieved by using buffered file I/O. With buffered I/O, the file I/O manager maintains an intermediate buffer larger than the blocks of information you are reading or writing. It only accesses the media when the buffer must be filled from or written to the disk.

+$#A Deciding When to Use Buffered File I/O

It's difficult to provide exact metrics telling you when you need to use buffered I/O. It depends on how many read and write operations you perform on a file, and on the size of these read and write operations. A general guideline is to use buffered I/O if you are doing many small I/O operations (less than 2 kilobytes each). But this rule isn't absolute ⎯ it's best to understand exactly how your program uses file I/O and experiment to optimize file I/O for your program's requirements.

+$#A Opening a File for Buffered File I/O

The multimedia file I/O manager provides several ways to set up and use buffered file I/O. The main distinction between these different approaches is whether the file I/O manager or the application allocates the buffer ⎯ either the file I/O manager or the application can allocate the I/O buffer. Unless you want to access the I/O buffer directly or open a memory file, let the file I/O manager allocate the buffer. For more information about accessing an I/O buffer directly and using memory files, see Directly Accessing a File Input/Output Buffer and Performing File Input/Output on Memory Files.

A buffer allocated by the file I/O manager is called an internal buffer. To open a file for buffered I/O using an internal buffer, specify the MMIO_ALLOCBUF flag with the mmioOpen function when you open the file. Once a file is opened for buffered I/O, the buffer is essentially transparent to the application. You can read, write, and seek the same way as with unbuffered I/O.

+$#A I/O Buffer Control Functions

The multimedia file I/O services also include some functions that give you more control over the file I/O buffer. Using the following functions, you can force the contents of an I/O buffer to be written to disk, enable buffered I/O on a file opened for unbuffered I/O, change the size of an I/O buffer, and supply your own I/O buffer.

Function | Description
mmioFlush | Writes the contents of the I/O buffer to disk.
mmioSetBuffer | Changes the size of the I/O buffer, and allows applications to supply their own buffer.

+$#A Flushing an I/O Buffer

Flushing an I/O buffer means writing the contents of the buffer to disk. You don't have to call the mmioFlush function to flush an I/O buffer ⎯ the buffer is automatically flushed when you close a file using the mmioClose function. If you don't close a file immediately after writing to it, you flush the buffer to ensure the information is written to disk.

Note If you run out of disk space, mmioFlush may fail, even if the preceding mmioWrite calls were successful. Similarly, mmioClose may fail when it is flushing its I/O buffer.

+$#A Changing the Size of the Internal I/O Buffer

The default size of the internal I/O buffer is 8K. If this size is not adequate, you can use the mmioSetBuffer function to change the size of the buffer. You can also use mmioSetBuffer to enable buffering on a file opened for unbuffered I/O.

The following example opens a file named SAMPLE.TXT for unbuffered I/O, and then enables buffered I/O with an internal 16K buffer.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

if ((hFile = mmioOpen("SAMPLE.TXT", NULL, MMIO_READ)) != NULL) {

/* File opened successfully; request an I/O buffer */

if (mmioSetBuffer(hFile, NULL, 16384L, 0))

/* Buffer cannot be allocated. */

return FALSE;

else

/* Buffer allocated successfully. */

return TRUE;

}

else

/* File cannot be opened. */

return FALSE;

+$#A Supplying Your Own I/O Buffer

You can also use mmioSetBuffer to supply your own buffer for use as a memory file. For details on using memory files, see Performing File Input/Output on Memory Files.

+$#A Working With RIFF Files

The preferred format for multimedia files is the Resource Interchange File Format (RIFF). The RIFF format is a tagged-file structure.

The multimedia file I/O services provide the following functions to support file I/O to RIFF files.

Function | Description
mmioAscend | Ascends out of a RIFF file chunk to the next chunk in the file.
mmioCreateChunk | Creates a new chunk in a RIFF file.
mmioDescend | Descends into a RIFF file chunk beginning at the current file position, or searches for a specified chunk.
mmioFOURCC | Converts four individual characters into a four-character code.
mmioStringToFOURCC | Converts a null-terminated string into a four-character code.

These functions work with the basic buffered and unbuffered file I/O services ⎯ you can open, read, and write RIFF files the same as other file types.

+$#A About RIFF Files

The basic building block of a RIFF file is called a chunk. Each chunk consists of the following fields:

• A four-character code specifying the chunk identifier

• A doubleword specifying the size of the data field in the chunk

• A data field

The only chunks allowed to contain other chunks (subchunks) are those with a chunk identifier of "RIFF" or "LIST". The first chunk in a RIFF file must be a "RIFF" chunk. All other chunks in the file are subchunks of the "RIFF" chunk.

+$#A "RIFF" Chunks

"RIFF" chunks include an additional field in the first four bytes of the data field. This additional field provides the form type of the field. The form type is a four-character code identifying the format of the data stored in the file. For example, Microsoft waveform audio files (WAVE files) have a form type of "WAVE". The following illustration shows a "RIFF" chunk containing two subchunks.

+$#A "LIST" Chunks

"LIST" chunks also include an additional field in the first four bytes of the data field. This additional field contains the list type of the field. The list type is a four-character code identifying the contents of the list. For example, a "LIST" chunk with a list type of "INFO" can contain "ICOP" and "ICRD" chunks providing copyright and creation date information. The following illustration shows a "RIFF" chunk containing a "LIST" chunk and one other subchunk (the "LIST" chunk contains two subchunks).

+$#A Generating Four-Character Codes

A four-character code is a 32-bit quantity representing a sequence of one to four ASCII alphanumeric characters, padded on the right with blank characters. The data type for a four-character code is FOURCC. Use the mmioFOURCC function to convert four characters to a four-character code, as shown in the following example, which generates a four-character code for "WAVE":

FOURCC fourccID;

.ifdef REF

.

.

.

.endif

fourccID = mmioFOURCC('W', 'A', 'V', 'E');

To convert a null-terminated string into a four-character code, use the mmioStringToFOURCC function, as shown in the following example, which also generates a four-character code for "WAVE":

FOURCC fourccID;

.ifdef REF

.

.

.

.endif

fourccID = mmioStringToFOURCC("WAVE", 0);

The second parameter in mmioStringToFOURCC specifies options for converting the string to a four-character code. If you specify the MMIO_TOUPPER flag, mmioStringToFOURCC converts all alphabetic characters in the string to uppercase. This is useful when you need to specify a four-character code to identify a custom I/O procedure (four-character codes identifying file-extension names must be all uppercase).

+$#A Creating RIFF Chunks

To create a new chunk, use the mmioCreateChunk function to write a chunk header at the current position in an open file.

The following example creates a new chunk with a chunk ID of "RIFF" and a form type of "RDIB".

HMMIO hmmio;

MMCKINFO mmckinfo;

.ifdef REF

.

.

.

.endif

mmckinfo.fccType = mmioFOURCC('R', 'D', 'I', 'B');

mmioCreateChunk(hmmio, &mmckinfo, MMIO_CREATERIFF);

If you're creating a "RIFF" or "LIST" chunk, you must specify the form type in the fccType field of the MMCKINFO structure. In the previous example, the form type is "RDIB".

If you know the size of the data field in a new chunk, you can set the cksize member in the MMCKINFO structure when you create the chunk. This value will be written to the data size field in the new chunk. If this value is not correct when you call the mmioAscend function to mark the end of the chunk, it will be rewritten automatically to reflect the correct size of the data field.

After you create a new chunk using mmioCreateChunk, the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). If the chunk is a "RIFF" or "LIST" chunk, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk).

+$#A Navigating RIFF Files

RIFF files consist of nested chunks of data. Multimedia file I/O services include two functions you can use to navigate between chunks in a RIFF file: mmioAscend and mmioDescend. You might think of these functions as high-level seek functions. When you descend into a chunk, the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). For "RIFF" and "LIST" chunks, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk). When you ascend out of a chunk, the file position is set to the location following the end of the chunk.

+$#A Descending Into a Chunk

The mmioDescend function descends into a chunk or searches for a chunk, beginning at the current file position.

The mmioDescend function fills an MMCKINFO structure with information on the chunk. This information includes the chunk identifier, the size of the data field, and the form type, or list type if the chunk is a "RIFF" or "LIST" chunk.

+$#A Searching for a Chunk

To search for a chunk in an open RIFF file, specify the MMIO_FINDCHUNK flag in the wFlags parameter of mmioDescend. Set the ckid member of the MMCKINFO structure referenced by lpck to the four-character code of the chunk you want to search for.

If you are searching for a "RIFF" or "LIST" chunk, you don't need to set the ckid member of the MMCKINFO structure ⎯ mmioDescend sets this member for you. Set the fccType member to the four-character code of the form type or list type of the chunk.

The following example searches for a "RIFF" chunk with a form type of "WAVE" to verify that the file that has just been opened is a WAVE waveform audio file.

HMMIO hmmio;

MMCKINFO mmckinfoParent;

MMCKINFO mmckinfoSubchunk;

.ifdef REF

.

.

.

.endif

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure the file is a WAVE file.

*/

mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');

if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent,

NULL, MMIO_FINDRIFF))

/* The file is not a WAVE file. */

return FALSE;

else

/* The file is a WAVE file. */

return TRUE;

If the chunk you are searching for is a subchunk enclosed by a parent chunk (as are all chunks other than "RIFF" chunks), you should identify the parent chunk with the lpckParent parameter. In this case, mmioDescend will only search within the specified parent chunk.

The following example searches for the "fmt " chunk in the "RIFF" chunk descended into by the previous example.

/*

* Find the format chunk (form type "fmt "); it should be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

/* Error, cannot find the "fmt " chunk. */

return FALSE;

}

else {

/* "fmt " chunk found. */

return TRUE;

}

If you do not specify a parent chunk, the current file position should be at the beginning of a chunk before you call mmioDescend to search for a chunk. If you do specify a parent chunk, the current file position can be anywhere in the parent chunk.

If the search for a subchunk fails, the current file position is undefined. You can use the mmioSeek function and the dwDataOffset field of the MMCKINFO structure for the enclosing parent chunk to seek back to the beginning of the parent chunk, as in the following example:

mmioSeek(hmmio, mmckinfoParent.dwDataOffset + 4, SEEK_SET);

Since the dwDataOffset member specifies the offset to the beginning of the data portion of the chunk, you must seek four bytes past dwDataOffset to set the file position to be after the form type.

+$#A Ascending Out of a Chunk

After you descend into a chunk and read the data in the chunk, you can move the file position to the beginning of the next chunk by ascending out of the chunk by using the mmioAscend function.

For example, the following statement ascends out of the "fmt " subchunk descended into by the previous example, illustrating searching for a subchunk:

/* Ascend out of the "fmt " subchunk.

*/

mmioAscend(hmmio, &mmckinfoSubchunk, 0);

+$#A Example of RIFF File I/O

The following example shows how to open a RIFF file for buffered I/O, as well as how to descend, ascend, and read RIFF chunks.

/* ReversePlay--Plays a WAVE waveform audio file backwards. */

void ReversePlay(void)

{

char szFileName[128]; /* filename of file to open */

HMMIO hmmio; /* file handle for open file */

MMCKINFO mmckinfoParent; /* parent chunk information structure */

MMCKINFO mmckinfoSubchunk; /* subchunk information structure */

DWORD dwFmtSize; /* size of "fmt" chunk */

DWORD dwDataSize; /* size of "data" chunk */

WAVEFORMAT *pFormat; /* pointer to memory for "fmt" chunk */

.ifdef REF

...

.endif

/* Get the filename from the edit control. */

.ifdef REF

...

.endif

/*

* Open the given file for reading with buffered I/O

* using the default internal buffer.

*/

hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);

if(hmmio == NULL){

Error("Failed to open file.");

return;

}

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure the file is a WAVE file.

*/

mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');

if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL,

MMIO_FINDRIFF)) {

Error("This is not a WAVE file.");

mmioClose(hmmio, 0);

return;

}

/*

* Find the "fmt " chunk (form type "fmt "); it must be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

Error("WAVE file has no \"fmt\" chunk");

mmioClose(hmmio, 0);

return;

}

/*

* Get the size of the "fmt " chunk--allocate and lock memory for it.

*/

dwFmtSize = mmckinfoSubchunk.cksize;

.ifdef REF

...

.endif

/* Read the "fmt " chunk. */

if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LRESULT)dwFmtSize){

Error("Failed to read format chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Ascend out of the "fmt " subchunk. */

mmioAscend(hmmio, &mmckinfoSubchunk, 0);

/*

* Find the data subchunk. The current file position

* should be at the beginning of the data chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

Error("WAVE file has no data chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Get the size of the data subchunk. */

dwDataSize = mmckinfoSubchunk.cksize;

if (dwDataSize == 0L) {

Error("The data chunk contains no data.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Open a waveform output device. */

.ifdef REF

...

.endif

/* Allocate and lock memory for the waveform data. */

.ifdef REF

...

.endif

/* Read the waveform data subchunk. */

if(mmioRead(hmmio, (HPSTR) lpData,

dwDataSize) != (LRESULT)dwDataSize) {

Error("Failed to read data chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Close the file. */

mmioClose(hmmio, 0);

/* Reverse the sound and play it. */

.ifdef REF

...

.endif

}

+$#A Directly Accessing a File Input/Output Buffer

Applications that are performance sensitive, such as applications that must stream data in real time from a CD-ROM, can optimize file I/O performance by accessing the file I/O buffer directly. Exercise care if you choose to do this ⎯ by accessing the file I/O buffer directly, you bypass some of the safeguards and error checking provided by the file I/O manager.

The multimedia file I/O services provide the following functions to support direct I/O buffer access.

Function | Description
mmioAdvance | Fills and/or flushes the I/O buffer of a file set up for direct I/O buffer access.
mmioGetInfo | Retrieves information on the file I/O buffer of a file opened for buffered I/O.
mmioSetInfo | Changes information on the file I/O buffer of a file opened for buffered I/O.

To access a file I/O buffer directly, open the file for buffered I/O as described in "Performing Buffered File I/O." You can use the internal file I/O buffer or supply your own buffer by using the mmioSetBuffer function.

+$#A Getting Information on the File I/O Buffer

Use the mmioGetInfo function to get information on a file I/O buffer, such as the buffer size and address.

+$#A Reading and Writing the File I/O Buffer

There are three members in the MMIOINFO structure used for reading and writing the file I/O buffer: pchNext, pchEndRead, and pchEndWrite. The pchNext field points to the next location in the buffer to read or write. You must increment pchNext as you read and write the buffer. The pchEndRead field identifies the last valid character you can read from the buffer. Likewise, pchEndWrite identifies the last location in the buffer you can write. To be precise, both pchEndRead and pchEndWrite point to the memory location following the last valid data in the buffer.

+$#A Advancing the File I/O Buffer

When you reach the end of the file I/O buffer, you must advance the buffer to fill it from the disk (if you are reading), and flush it to the disk (if you are writing). Use the mmioAdvance function to advance a file I/O buffer.

The mmioAdvance function advances the buffer by (pchNext − cchBuffer) bytes. All data in the buffer before pchNext is assumed to have been read already, while all data from pchNext to pchEndRead will be considered unread. So, when mmioAdvance is called, all data before pchNext will be overwritten. The buffer must not be treated as an array and pchNext must be incremented until pchNext equals pchEndRead.

The following illustrations show how the file I/O buffer is advanced as a file is read or written.

+$#A Using the mmioAdvance Function

To fill a file I/O buffer from disk, call mmioAdvance with the MMIO_READ flag. If there is not enough data remaining in the file to fill the buffer, the pchEndRead member in the MMIOINFO structure points to the location following the last valid byte in the buffer.

To flush a buffer to disk, first set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure. Then call mmioAdvance with the MMIO_WRITE flag.

+$#A Example of Accessing a File I/O Buffer

The following example is based on the ReversePlay example function discussed earlier in this section. In this example, direct-buffer access is used to read waveform data from a file.

HMMIO hmmio;

MMIOINFO mmioinfo;

DWORD dwDataSize;

DWORD dwCount;

HPSTR hptr;

.ifdef REF

.

.

.

.endif

/* Get information on the file I/O buffer. */

if (mmioGetInfo(hmmio, &mmioinfo, 0)) {

Error("Failed to get I/O buffer info");

mmioClose(hmmio, 0);

return FALSE;

}

/*

* Read the entire file by directly reading the file I/O buffer.

* When the end of the I/O buffer is reached, advance the buffer.

*/

for (dwCount = dwDataSize, hptr = lpData; dwCount > 0; dwCount--) {

/* Check to see if the I/O buffer must be advanced. */

if (mmioinfo.pchNext == mmioinfo.pchEndRead) {

if(mmioAdvance(hmmio, &mmioinfo, MMIO_READ)) {

Error("Failed to advance buffer.");

mmioClose(hmmio, 0);

return FALSE;

}

}

/* Get a character from the buffer. */

*hptr++ = *mmioinfo.pchNext++;

}

/* End direct buffer access and close the file. */

mmioSetInfo(hmmio, &mmioinfo, 0);

mmioClose(hmmio, 0);

+$#A Ending Direct Access of a File I/O Buffer

When you finish accessing a file I/O buffer, pass the MMIOINFO structure filled by the mmioGetInfo function to the mmioSetInfo function to stop direct-buffer access. If you wrote to the buffer, set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure before calling mmioSetInfo. Otherwise, the buffer will not be flushed to disk.

+$#A Performing File Input/Output on Memory Files

The multimedia file I/O services let you to treat a block of memory as a file. This can be useful if you already have a file image in memory. Memory files let you reduce the number of special-case conditions in your code because, for I/O purposes, you can treat file memory images as if they were disk-based files. You can also use memory files with the Clipboard.

+$#A Opening Memory Files

Like I/O buffers, memory files can use memory allocated by the application or by the file I/O manager. In addition, memory files can be either expandable or non-expandable. When the file I/O manager reaches the end of an expandable memory file, it expands the memory file by a predefined increment.

To open a memory file, use mmioOpen with the szFileName parameter set to NULL and the MMIO_READWRITE flag set in the dwOpenFlags parameter. Set the lpmmioinfo parameter to point to an MMIOINFO structure as follows:

• Set the pIOProc member to NULL.

• Set the fccIOProc member to FOURCC_MEM.

• Set the pchBuffer member to point to the memory block. To request that the file I/O manager allocate the memory block, set pchBuffer to NULL.

• Set the cchBuffer member to the initial size of the memory block.

• Set the adwInfo[0] member to the minimum expansion size of the memory block. For a non-expandable memory file, set adwInfo[0] to NULL.

• Set all other members to zero.

+$#A Allocating Memory for Memory Files

There are no restrictions on allocating memory for use as a non-expandable memory file. You can use static memory or stack memory, or you can use locally allocated or globally allocated memory. For expandable memory files, you must use memory allocated using the GlobalAlloc function and locked using the GlobalLock function.

+$#A Using Custom Input/Output Procedures

Multimedia file I/O services use I/O procedures to handle the physical input and output associated with reading and writing to different types of storage systems, such as file-archival systems and database-storage systems. There are predefined I/O procedures for standard Microsoft® Windows® files and for memory files. In the future, there will be a predefined I/O procedure for accessing elements of compound files. Compound files consist of a number of individual files, called file elements, bound together in one physical file.

You can supply a custom I/O procedure for accessing a unique storage system such as a database or file archive. This I/O procedure is private to your application.

The multimedia file I/O services provide the following functions to support custom I/O procedures:

Function | Description
mmioInstallIOProc | Installs, removes, or locates an I/O procedure.
mmioSendMessage | Sends a custom message to the I/O procedure associated with a file.

• Opening a file using a custom I/O procedure

• Writing an I/O procedure

• Installing an I/O procedure

+$#A Opening a File Using a Custom I/O Procedure

Before learning about how to write an I/O procedure, it's helpful to understand how to use one. To open a file using a custom I/O procedure, use the mmioOpen function as you would to open any other file. Use a plus sign (+) in the filename to separate the name of the physical file from the name of the element of the file you want to open. For example, the following statement opens a file element named "element" from a file named FILENAME.ARC:

mmioOpen("filename.arc+element", NULL, MMIO_READ);

When the file I/O manager encounters a plus sign in a filename, it looks at the preceding filename extension to determine which I/O procedure to associate with the file. In the previous example, the file I/O manager will attempt to use the I/O procedure associated with the .ARC filename extension. If no I/O procedure is installed, the mmioOpen function returns an error.

+$#A Writing an I/O Procedure

An I/O procedure is a message-processing function supplied by an application. An I/O procedure function has the following syntax:

LRESULT IOProc(lpmmioinfo, wMsg, lParam1, lParam2)

The lpmmioinfo parameter specifies a pointer to an MMIOINFO structure associated with the file being accessed.

The wMsg parameter is of type UINT and specifies the message being sent by the file I/O manager to the I/O procedure.

The lParam1 parameter is of type LPARAM and specifies 32 bits of message-specific information.

The lParam2 parameter is of type LPARAM and specifies 32 bits of message-specific information.

The return value is message-specific. If the I/O procedure does not recognize a message, it should return zero.

+$#A I/O Procedure Messages

I/O procedures must respond to the MMIOM_CLOSE, MMIOM_OPEN, MMIOM_READ, MMIOM_WRITE, MMIOM_SEEK, and MMIOM_WRITEFLUSH messages. Each of these messages has two parameters.

You can also create custom messages and send them to your I/O procedure using the mmioSendMessage function. If you define your own messages, be sure they are defined at or above the MMIOM_USER message. For example, the following example defines a message named MMIOM_MYMESSAGE:

#define MMIOM_MYMESSAGE MMIOM_USER + 0

+$#A Using the MMIOINFO Structure

In addition to processing messages, an I/O procedure must maintain the lDiskOffset member in the MMIOINFO structure referenced by the lpmmioinfo parameter. The lDiskOffset member must always contain the file offset to the location that the next MMIOM_READ or MMIOM_WRITE message will access. The offset is specified in bytes and is relative to the beginning of the file. The I/O procedure can use the adwInfo[] member to maintain any required state information. The I/O procedure should not modify any other members in the MMIOINFO structure.

+$#A Installing an I/O Procedure

Use mmioInstallIOProc to install, remove, or locate an I/O procedure.

For example, to install an I/O procedure associated with the filename extension ARC, use the following statement:

mmioInstallIOProc (mmioFOURCC('A', 'R', 'C', ' '),

(LPMMIOPROC)lpmmioproc, MMIO_INSTALLPROC);

Note Be sure to remove any I/O procedures you've installed before closing your application.

+$#A Installing an I/O Procedure Using MmioOpen

When you install an I/O procedure by using the mmioInstallIOProc function, the procedure remains installed until you remove it. The I/O procedure will be used for any file you open if the file has the appropriate filename extension. You can also temporarily install an I/O procedure by using the mmioOpen function. In this case, the I/O procedure is only used with a file opened by mmioOpen and is removed when the file is closed by using the mmioClose function.

To specify an I/O procedure when you open a file using mmioOpen, use the lpmmioinfo parameter to reference an MMIOINFO structure as follows:

• Set the fccIOProc member to NULL.

• Set the pIOProc member to the procedure-instance address of the I/O procedure.

• Set all other members to zero (unless you are opening a memory file, or directly reading or writing to the file I/O buffer).

+$#A File Input/Output Functions

Following are the functions used with file input/output.

mmioAdvance
mmioAscend
mmioClose
mmioCreateChunk
mmioDescend
mmioFlush
mmioFOURCC
mmioGetInfo
mmioInstallIOProc
mmioOpen
MMIOProc
mmioRead
mmioRename
mmioSeek
mmioSendMessage
mmioSetBuffer
mmioSetInfo
mmioStringToFOURCC
mmioWrite
mmsystemGetVersion

+$#A Introduction to Multimedia Services

Microsoft® Windows® provides developers with high-level and low-level services for developing multimedia applications that use the extended capabilities of a multimedia personal computer.

+$#A About Windows Multimedia Services

Windows provides the following multimedia services:

• A Media Control Interface (MCI) for controlling media devices.

• Extensible string-based and message-based interfaces for communicating with MCI device drivers.

• MCI device drivers for playing and recording waveform audio, playing Musical Instrument Digital Interface (MIDI) files, and playing compact disc audio from a CD-ROM drive.

• Low-level application programming interface (API) support for multimedia-related services.

• Low-level support for playing and recording audio with waveform and MIDI audio devices.

• Low-level support for precision timer services.

• Multimedia file input and output (I/O) services that provide buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible by means of custom I/O procedures.

• A MIDI Mapper supporting standard MIDI patch services. This allows MIDI files to be authored independently of users' MIDI synthesizer setups.

• Control Panel options that let users change display drivers, set up a screen saver, install multimedia device drivers, assign waveform sounds to system alerts, and configure the MIDI Mapper.

This overview covers the following topics:

• The software architecture of the multimedia services

• The design philosophy and implementation of the multimedia services

• Basic information about developing applications that use the multimedia services

+$#A Architecture of Windows Multimedia Services

Although the multimedia services are provided by a number of files, the overall architecture can be viewed as consisting of just three software modules:

• The WINMM library, which provides the MCI services and low-level multimedia support functions

• Multimedia device drivers, which provide communication between the low-level WINMM functions and multimedia devices, such as waveform, MIDI, and timer hardware

• Drivers for MCI, which provide high-level control of media devices

+$#A Windows Multimedia Design Philosophy

The architecture of the multimedia services is designed around the concepts of extensibility and device independence. Extensibility allows the software architecture to accommodate advances in technology easily, without changes to the architecture itself. Device independence makes it easy to develop multimedia applications that will run on a wide range of hardware providing different levels of multimedia support. Three design elements of the system software provide extensibility and device independence:

• A translation layer (WINMM) that isolates applications from device drivers and centralizes device-independent code

• Run-time linking that allows the translation layer to link to the drivers it needs

• A well-defined and consistent driver interface that minimizes special-case code and makes the installation and upgrade process easier

+$#A Building a Multimedia Application

Before writing a multimedia application, you should be familiar with programming in the Windows environment.

When using the multimedia services, you must include the MMSYSTEM.H header file in all source-code files that call multimedia functions. This header file depends on declarations made in the WINDOWS.H header file, so you must first include WINDOWS.H. The MMSYSTEM.H header file provides function prototypes as well as definitions of multimedia data types and constants.

In addition to linking the normal Windows libraries, you must also link to the WINMM.LIB import library when linking an application that uses multimedia functions.

+$#A Multimedia Timer and Joystick Services

This overview shows you how to add joystick input capabilities to your application and how to control event timing.

+$#A About Multimedia Timers

The joystick services can receive control signals from up to two joysticks. Timer services provide improved timer resolution and accuracy. Intel®-based systems have an accuracy of up to 16 milliseconds and MIPS®-based systems have an accuracy of up to 10 milliseconds.

The functions and data structures associated with the joystick and timer-interrupt services are defined in MMSYSTEM.H and WINMM.LIB.

+$#A Timer Services

The multimedia timer services provided with the Microsoft® Win32® application programming interface (API) let applications schedule timed periodic or one-time asynchronous events. Unlike the timer services that post WM_TIMER messages to a message queue, the multimedia timer services call a callback function.

+$#A Timer Data Types

The MMSYSTEM.H header file defines data types and function prototypes for timer functions. You must include this header file in any source-code module that uses timer services. MMSYSTEM.H defines the following new data types.

Type | Description
MMTIME | A structure for representing time in one of several formats.
TIMECAPS | A structure for querying timer capabilities.

+$#A Using Timer Services

Timer services let an application request and receive timer messages at application-specified intervals. Real-time multimedia applications can use the following functions to control the pace of data and synchronized presentations.

Function | Description
timeBeginPeriod | Establishes the minimum timer resolution an application will use.
timeEndPeriod | Clears a minimum timer resolution previously set using timeBeginPeriod.
timeGetDevCaps | Returns information about the capabilities of the timer services.
timeGetTime | Returns the system time in milliseconds.
timeGetSystemTime | Fills an MMTIME structure with the system time in milliseconds.
timeSetEvent | Creates a timer event that executes a specific action at a specific time or at periodic intervals.
timeKillEvent | Cancels a timer event previously created using timeSetEvent.

+$#A Getting the System Time

An application can get the current system time using the timeGetTime or timeGetSystemTime function. The system time is the count of milliseconds since the Microsoft® Windows® operating system was started. The timeGetTime function returns the system time, and the timeGetSystemTime function fills an MMTIME structure with the system time.

+$#A Determining Maximum and Minimum Event Periods

You can use the timeGetDevCaps function to determine the minimum and maximum timer-event periods provided by the timer services. These values vary across computers and can vary depending on the mode in which Windows is running.

+$#A Establishing Minimum Timer Resolution

Before starting timer events, your application must establish the minimum timer resolution that it intends to use. It must clear this value after finishing with the timer-event services.

Use the timeBeginPeriod and timeEndPeriod functions to set and clear the minimum timer-event resolution for your application. You must match a call to timeBeginPeriod with a corresponding call to timeEndPeriod, specifying the same minimum resolution in both calls. An application can make multiple timeBeginPeriod calls, as long as each call is matched with a call to timeEndPeriod.

+$#A Starting Timer Events

To initialize and start timer events, use the timeSetEvent function. This function returns a timer identifier that can be used to stop or identify timer events.

The timeSetEvent function returns a timer identifier if successful or NULL if unsuccessful. Interrupt timers are a scarce resource, and periodic timers with resolution less than 100 milliseconds consume a significant portion of CPU time. For periodic timers, you must pair calls to timeSetEvent with calls to timeKillEvent. For more information, see Canceling a Timer Event.

The lpFunction parameter contains the address of the function to be called when the timer event takes place.

Since the callback function is accessed at interrupt time, it must adhere to strict programming guidelines. Timer-callback functions follow the same programming guidelines as callback functions for the low-level audio services. For information about writing low-level audio callback functions, see Low-Level Audio. For more information about timer callback functions, see TimeProc.

+$#A Canceling a Multimedia Timer Event

You can cancel an active timer event at any time. Be sure to cancel any outstanding timers before freeing the module containing the callback function. To cancel a timer event, use the timeKillEvent function.

+$#A Using Timer Callbacks

This topic describes how an application can use the timer services. First, the application calls the timeGetDevCaps function to determine the minimum and maximum resolution supported by the timer services. Before setting up any timer events, the application uses the timeBeginPeriod function to establish the minimum timer resolution it will use, as shown in the following example:

#define TARGET_RESOLUTION 1 /* Try for 1-millisecond accuracy. */

TIMECAPS tc;

UINT wTimerRes;

if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {

/* Error; application can't continue. */

}

wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION),

tc.wPeriodMax);

timeBeginPeriod(wTimerRes);

To start the timer event, the application specifies the amount of time before the callback occurs, the required resolution, the address of the callback function, and user data to supply with the callback. The application can use a function like the following to start a one-time timer event.

UINT SetTimerCallback(NPSEQ npSeq, /* Sequencer data */

UINT msInterval) /* Event interval */

{

npSeq->wTimerID = timeSetEvent(

msInterval, /* Delay */

wTimerRes, /* Resolution (global variable) */

(LPTIMECALLBACK) OneShotTimer, /* Callback function */

(DWORD) npSeq, /* User data */

TIME_ONESHOT); /* Event type (one-time) */

if (npSeq->wTimerID != 0)

return TIMERR_NOCANDO;

else

return TIMERR_NOERROR;

}

The following callback function is used for the one-shot timer.

void PASCAL OneShotTimer(UINT wTimerID, UINT msg,

DWORD dwUser, DWORD dw1, DWORD dw2)

{

NPSEQ npSeq; /* Pointer to sequencer data */

npSeq = (NPSEQ) dwUser;

npSeq->wTimerID = 0; /* Invalidate unused timer id */

TimerIntRoutine(npSeq); /* Handle interrupt-time tasks */

UNREFERENCED_PARAMETER(dw1);

UNREFERENCED_PARAMETER(dw2);

UNREFERENCED_PARAMETER(msg);

UNREFERENCED_PARAMETER(wTimerID);

}

Before freeing the dynamic-link library (DLL) that contains the callback function, the application cancels any outstanding timers. To cancel one timer event, it can call the following function.

void DestroyTimer(NPSEQ npSeq)

{

if (npSeq->wTimerID) { /* If timer event is pending */

timeKillEvent(npSeq->wTimerID); /* Cancel the event */

npSeq->wTimerID = 0;

}

}

Finally, to cancel the minimum timer resolution it established, the application calls timeEndPeriod as follows.

timeEndPeriod(wTimerRes);

+$#A Joystick Services

The joystick is an input device that provides position information. It is an additional supported input device, not a replacement for the mouse. All absolute-position devices, including touch screens, digitizing tablets, and light pens, can use the joystick services to provide position and button information to applications.

The joystick services are loaded when Windows is started. The joystick services can monitor two joysticks, each with two- or three-axis movement, and up to four buttons. Applications access the joystick services through the set of functions described in this section.

Note The driver for the IBM® Game Adapter supports two 2-axis joysticks or one 3-axis joystick.

+$#A Joystick Data Types

The MMSYSTEM.H header file defines new data types and function prototypes for joystick functions. MMSYSTEM.H defines the following new data types.

Type | Description
JOYCAPS | A structure that contains joystick capability information.
JOYINFO | A structure that contains joystick position and button information.

+$#A Using Joystick Services

Joystick services include functions to query each joystick for its capabilities, to poll each joystick for position and button information, and to receive messages in response to joystick events. Your application can use the following joystick functions to accept input from one or two joysticks.

Function | Description
joyGetNumDevs | Returns the number of joysticks supported by the joystick services.
joyGetDevCaps | Returns joystick capabilities.
joyGetPos | Returns joystick position and button information.
joySetCapture | Causes joystick input to be sent to a specified window at regular intervals or when the joystick state changes.
joyReleaseCapture | Releases the joystick captured using joySetCapture.
joyGetThreshold | Returns the movement threshold of a joystick.
joySetThreshold | Sets the movement threshold of a joystick.

+$#A Determining Joystick Capabilities

The various joysticks in use today can support two or three axes and a variety of button configurations. Also, joysticks support different ranges of motion and polling frequencies. Joystick drivers can support either one or two joysticks. Two functions allow you to determine the capabilities of the joystick services and joystick devices installed on a system.

+$#A Getting the Joystick Driver Capabilities

You can use the joyGetNumDevs function to determine the number of joystick devices supported by the joystick services. This function returns the number of joysticks, or zero if there is no joystick support. The value returned is not necessarily the number of joysticks attached to the system. To determine whether a joysticf supportek is attached, call the joyGetPos function for the device. The function returns JOYERR_UNPLUGGED if the specified device is disconnected.

The following example determines whether the joystick services are available and then determines if a joystick is attached to one of the ports:

JOYINFO joyinfo;

UINT wNumDevs, wDeviceID;

BOOL bDev1Attached, bDev2Attached;

if((wNumDevs = joyGetNumDevs()) == 0)

return ERR_NODRIVER;

bDev1Attached =

joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED;

bDev2Attached =

wNumDevs == 2 &&

joyGetPos(JOYSTICKID2,&joyinfo) != JOYERR_UNPLUGGED;

/* Decide which joystick to use */

if(bDev1Attached || bDev2Attached)

wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2;

else

return ERR_NODEVICE;

+$#A Getting the Joystick Capabilities

You can use the joyGetDevCaps function to obtain the specific capabilities of each joystick attached to a given system. This function fills a JOYCAPS structure with the range of each axis on the joystick, the number of buttons, and the maximum and minimum polling frequency.

+$#A Methods for Checking Joystick Status

An application can receive information from the joystick in one of two ways:

• By processing joystick messages from a captured joystick

• By polling the joystick directly

The message-processing method can be simpler to use; your application is sent messages that indicate the position of the stick and the state of the buttons (pressed or released).

+$#A Capturing Joystick Messages to a Window Function

You can capture joystick input to a window function; your application then receives joystick messages at specified intervals or when the user manipulates the joystick. The messages are described in the following topic.

Only one application can capture joystick messages from a given joystick. Capturing joystick messages does not, however, prevent your application (or other applications) from polling the joystick using joyGetPos. If joyGetPos is called while joystick input is captured, joystick events occurring close to the time of the joyGetPos call may not be accurately reported to the capture window.

The joySetCapture function captures joystick input to a window function you specify. To release the joystick, call the joyReleaseCapture function.

To capture messages from two joysticks attached to the system, you must call joySetCapture twice, once for each joystick. Your window then receives separate messages for each device.

You cannot capture an unplugged joystick. The joySetCapture function returns zero if successful; it returns JOYERR_UNPLUGGED if the specified device is unplugged.

Note The joystick services set up a Windows timer event with each call to joySetCapture.

Set the period parameter for joySetCapture to a value that falls within the minimum and maximum resolution range for the joystick. To determine the minimum and maximum resolution of the joystick, call the joyGetDevCaps function, which fills the wPeriodMin and wPeriodMax members in the JOYCAPS structure.

If the specified period value is outside the range of valid resolution values for the joystick, the joystick services use the minimum or maximum resolution value, whichever is closer to the specified period.

+$#A Processing Multimedia Timer Joystick Messages

The following joystick messages can be sent to a window function. Numerals 1 and 2 in these messages correspond to the joystick initiating the message. MM_JOY1 messages are sent to the window function if your application requests input from the first joystick, and MM_JOY2 messages are sent if your application requests input from the second joystick. All messages report nonexistent buttons as released.

Messages | Description
MM_JOY1MOVE, MM_JOY2MOVE | Report a change in the x-axis and/or y-axis position of the joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1ZMOVE, MM_JOY2ZMOVE | Report a change in the z-axis position of a 3-axis joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1BUTTONUP, MM_JOY2BUTTONUP, MM_JOY1BUTTONDOWN, MM_JOY2BUTTONDOWN | Report that a joystick button has been pressed or released. The wParam parameter contains one JOY_BUTTONCHG bit flag specifying which button changed state and a combination of JOY_BUTTON bit flags specifying the current button states. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.

The joystick services use the following bit flags, passed in the wParam parameter of the window function, to report the state of the joystick buttons.

Flag | Description
JOY_BUTTON1 | Set when button 1 is pressed.
JOY_BUTTON1CHG | Set when button 1 has changed state.
JOY_BUTTON2 | Set when button 2 is pressed.
JOY_BUTTON2CHG | Set when button 2 has changed state.
JOY_BUTTON3 | Set when button 3 is pressed.
JOY_BUTTON3CHG | Set when button 3 has changed state.
JOY_BUTTON4 | Set when button 4 is pressed.
JOY_BUTTON4CHG | Set when button 4 has changed state.

The MM_JOY*MOVE messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick.

The MM_JOY*BUTTONUP and MM_JOY*BUTTONDOWN messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick. Also, they use JOY_BUTTONCHG flags to indicate which button changed state, thereby generating the message.

For example, if the user presses and holds buttons 1 and 2 and then moves the stick, a window function may receive the following messages.

Message | wParam Flags
MM_JOY1BUTTONDOWN | JOY_BUTTON1 | JOY_BUTTON1CHG
MM_JOY1BUTTONDOWN | JOY_BUTTON1 | JOY_BUTTON2 | JOY_BUTTON2CHG
MM_JOY1MOVE | JOY_BUTTON1 | JOY_BUTTON2

A window function may receive the following messages when the user presses and releases button 3 without moving the stick.

Message | wParam Flags
MM_JOY1BUTTONDOWN | JOY_BUTTON3 | JOY_BUTTON3CHG
MM_JOY1BUTTONUP | JOY_BUTTON3CHG

+$#A Releasing the Joystick

When your application no longer needs to receive periodic joystick messages, it should release the joystick by using the joyReleaseCapture function. If your application does not release the joystick before ending, the joystick is released shortly after the capture window is destroyed.

+$#A Setting the Movement Threshold

You can change the movement threshold of the joystick by calling the joySetThreshold function. The movement threshold is the number of device units that the stick must be moved before an MM_JOY*MOVE message is sent to the window that has captured the device.

+$#A Polling the Joystick

You can poll the joystick for position and button information. For example, an application may poll the joystick to get baseline position values; the Joystick Control Panel application uses this technique when calibrating the joystick. The joyGetPos function allows you to poll the joystick for position and button information.

Note Calling joyGetPos while joystick input is captured can prevent the joystick services from accurately reporting joystick events to the capture window.

+$#A Using Joystick Messages

The remainder of this topic presents examples from a simple joystick game that performs the useful function of shooting holes in the desktop: it gets position and button-state information from the joystick services and, when a user presses the joystick buttons, plays waveform resources and paints bullet holes on the screen.

Most of the joystick-control code is in the main window function. In the following WM_CREATE case of the message handler, the application captures input from joystick 1.

case WM_CREATE:

if(joySetCapture(hWnd, JOYSTICKID1, NULL, FALSE)) {

MessageBeep(MB_ICONEXCLAMATION);

MessageBox(hWnd, "Couldn't capture the joystick.", NULL,

MB_OK | MB_ICONEXCLAMATION);

PostMessage(hWnd,WM_CLOSE,0,0L);

}

break;

In response to the MM_JOY1MOVE messages, the application changes the position of the cursor and, if either button is pressed, draws a hole in the desktop.

case MM_JOY1MOVE :

if((UINT) wParam & (JOY_BUTTON1 | JOY_BUTTON2))

DrawFire(hWnd);

DrawSight(lParam); /* Calculate new cursor position */

break;

In response to the MM_JOY1BUTTONDOWN messages, the application uses sndPlaySound to play a waveform audio file.

case MM_JOY1BUTTONDOWN :

if((UINT) wParam & JOY_BUTTON1) {

sndPlaySound(lpButton1,

SND_LOOP | SND_ASYNC | SND_MEMORY);

DrawFire(hWnd);

}

else if(wParam & JOY_BUTTON2) {

sndPlaySound(lpButton2,

SND_ASYNC | SND_MEMORY | SND_LOOP);

DrawFire(hWnd);

}

break;

By specifying the SND_LOOP and SND_ASYNC flags with sndPlaySound, the JOYTOY application repeats the waveform playback until the button is released.

When a button is released, the window function receives a MM_JOY1BUTTONUP message, and handles it with the following statement.

case MM_JOY1BUTTONUP:

sndPlaySound(NULL, 0);

break;

This sequence stops the waveform-audio playback.

+$#A Multimedia Timer and Joystick Functions

Following are the functions used with joystick services.

joyGetDevCaps
joyGetNumDevs
joyGetPos
joyGetThreshold
joyReleaseCapture
joySetCapture
joySetThreshold

Following are the functions used with timer services.

timeBeginPeriod
timeEndPeriod
timeGetDevCaps
timeGetSystemTime
timeGetTime
timeKillEvent
TimeProc
timeSetEvent

Créé avec HelpNDoc Personal Edition: Générateur de documentation iPhone gratuit


Header Controls.rtf

+$#AK Header Controls

A header control is a window that is usually positioned above columns of text or numbers. It contains a title for each column, and it can be divided into parts. The user can drag the dividers that separate the parts to set the width of each column. The following illustration shows a header control that has labeled columns giving detailed information about files in a directory.

{ewc msdncd, EWGraphic, group10597 0 /a "SDK_01.bmp"}

+$#A About Header Controls

You can create a header control by using the CreateWindowEx function, specifying the WC_HEADER window class. This window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls function. After you create a header control, you can divide it into parts, set the text in each part, and control the appearance of the window by using header window messages.

+$#A About Header Control Styles

Header controls have a number of styles that determine the control's appearance and behavior. You set the initial styles when you create the header control. To retrieve and change the styles after creating the control, use the GetWindowLong and SetWindowLong functions.

A header control can be divided into two or more parts called header items. If a header control has the HDS_BUTTONS style, each item in the control looks and behaves like a push button. This style is useful if an application carries out a task when the user clicks an item in the header control. For example, an application could sort information in the columns differently depending on which item the user clicks.

The HDS_HORZ style creates a header control with a horizontal orientation.

+$#A Header Control Size and Position

Typically, you must set the size and position of a header control to fit within the boundaries of a particular rectangle, such as the client area of a window. By using the HDM_LAYOUT message, you can retrieve the appropriate size and position values from the header control.

When sending HDM_LAYOUT, you specify the address of an HD_LAYOUT structure that contains the coordinates of the rectangle that the header control is to occupy and provides a pointer to a WINDOWPOS structure. The control fills the WINDOWPOS structure with size and position values appropriate for positioning the control along the top of the specified rectangle. The height value is the sum of the heights of the control's horizontal borders and the average height of characters in the font currently selected into the control's device context.

If you want to use HDM_LAYOUT to set the initial size and position of a header control, you should set the initial visibility state of the control so that it is hidden. After sending HDM_LAYOUT to retrieve the size and position values, you can use the SetWindowPos function to set the new size, position, and visibility state.

+$#A Items

Each item in a header control can have a string, a bitmapped image, and an application-defined 32-bit value associated with it. The string and bitmap are displayed within the boundaries of the header item. If an item contains both a string and an image, the image is displayed above the string. If the string and image overlap, the string overwrites the overlapping portion of the image.

You add an item to a header control by sending the HDM_INSERTITEM message to the control. The message includes the address of an HD_ITEM structure, which defines the properties of the header item, including its string, bitmap, initial size, and application-defined value. The structure also specifies formatting flags that tell the control whether to center, left-align, or right-align the string or bitmap in the item's rectangle.

HDM_INSERTITEM returns the index of the newly added item. You can use the index in other messages to set properties or retrieve information about the item. You can delete an item by using the HDM_DELETEITEM message, specifying the index of the item to delete.

You can use the HDM_SETITEM message to set the properties of an existing header item and the HDM_GETITEM message to retrieve the current properties of an item. To retrieve a count of the items in a header control, use the HDM_GETITEMCOUNT message.

You can use the HBT_SPRING value to give a header item a "springy" quality. Although an item has a minimum width, it can grow wider if there is extra room in the owner window. A header control can have multiple springy items; the extra room in the owner window is distributed among the springy items.

To make an item springy, send the WM_SETTEXT message to the header control. In the wParam parameter, use the logical OR operator to combine the index of the header item with the HBT_SPRING value.

+$#A Owner-Drawn Header Controls

You can define individual items of a header control to be owner-drawn items. Using this technique gives you more control than you would otherwise have over the appearance of a header item.

You can use the HDM_INSERTITEM message to insert a new owner-drawn item into a header control or the HDM_SETITEM message to change an existing item to an owner-drawn item. Both messages include the address of an HD_ITEM structure, which should have the fmt member set to the HDF_OWNERDRAW value.

When a header control must draw an owner-drawn item, it sends the WM_DRAWITEM message to the parent window. The wParam parameter of the message is the child window identifier of the header control, and the lParam parameter is a pointer to a DRAWITEMSTRUCT structure. The parent window uses the information in the structure to draw the item. For an owner-drawn item in a header control, the DRAWITEMSTRUCT structure contains the following information.

Member | Description
CtlType | ODT_HEADER owner-drawn control type.
CtlID | Child-window identifier of the header control.
itemID | Index of the item to be drawn.
itemAction | ODA_DRAWENTIRE drawing-action flag.
itemState | ODS_SELECTED drawing-action flag if the cursor is on the item and the mouse button is down. Otherwise, this member is zero.
hwndItem | Handle of the header control.
hDC | Handle of the device context of the header control.
rcItem | Coordinates of the header item to be drawn. The coordinates are relative to the upper left corner of the header control.
itemData | Application-defined 32-bit value associated with the item.

+$#A About Header Control Notification Messages

A header control sends notification messages to its parent window when the user clicks or double-clicks an item, when the user drags an item divider, and when the attributes of an item change. The parent window receives the notifications in the form of WM_NOTIFY messages. The following notifications are used with header controls.

Notification | Description
HDN_BEGINTRACK | Signals the start of divider dragging.
HDN_DIVIDERDBLCLICK | Indicates that the user double-clicked a divider.
HDN_ENDTRACK | Signals the end of divider dragging.
HDN_ITEMCHANGED | Indicates a change in the attributes of an item.
HDN_ITEMCHANGING | Indicates that the attributes of an item are about to change.
HDN_ITEMCLICK | Indicates that the user clicked an item.
HDN_ITEMDBLCLICK | Indicates that the user double-clicked an item.
HDN_TRACK | Indicates that the user dragged a divider.

+$#A Default Header Control Message Processing

This section describes the window messages handled by the window procedure for the WC_HEADER window class.

Message | Processing performed
WM_CREATE | Initializes the header control.
WM_DESTROY | Frees resources allocated for the header control.
WM_ERASEBKGND | Erases the background of the header control using the current background color for the control.
WM_GETDLGCODE | Returns a combination of the DLGC_WANTTAB and DLGC_WANTARROWS values.
WM_GETFONT | Returns the handle of the current font, which is used by the header control to draw its text.
WM_LBUTTONDBLCLK | Captures mouse input. If the mouse cursor is on a divider, the control sends a HDN_BEGINTRACK notification message and begins tracking mouse movement. If the cursor is on a header item, the control redraws the item in the style that indicates the item is pressed.
WM_LBUTTONDOWN | Same as the WM_LBUTTONDBLCLK message.
WM_LBUTTONUP | Releases the mouse capture. If the control was tracking mouse movement, it sends the HDN_ENDTRACK notification message and redraws the header control. Otherwise, the control sends the HDN_ITEMCLICK notification message and redraws the header item that was clicked.
WM_MOUSEMOVE | If a divider is being dragged, the control sends the HDN_TRACK notification message and redraws itself. If the left mouse button is down and the cursor is on an item, the control redraws the item in the style that indicates the item is pressed.
WM_NCCREATE | Allocates and initializes an internal data structure.
WM_NCDESTROY | Frees resources allocated by the header control.
WM_PAINT | Paints the invalid region of the header control.
WM_SETCURSOR | Sets the cursor shape, depending on whether the cursor is on a divider or in a header item.
WM_SETFONT | Selects a new font handle into the device context for the header control.

+$#A Using Header Controls

• Creating a header control

• Adding an item to a header control

+$#A Creating a Header Control

The following example creates a header control and positions it along the top of the parent window's client area. The control is initially hidden. The example uses the HDM_LAYOUT message to determine the appropriate size and position of the control given the bounding rectangle of the parent window. Then the example sets the size and position of the control and makes it visible.

// DoCreateHeader - creates a header control that is positioned along

// the top of the parent window's client area.

// Returns the handle of the header control.

// hwndParent - handle of the parent window

//

// Global variable

// g_hinst - handle of the application instance

extern HINSTANCE g_hinst;

HWND DoCreateHeader(HWND hwndParent)

{

HWND hwndHeader;

RECT rcParent;

HD_LAYOUT hdl;

WINDOWPOS wp;

// Ensure that the common control DLL is loaded, and then create

// the header control.

InitCommonControls();

if ((hwndHeader = CreateWindowEx(0, WC_HEADER, (LPCTSTR) NULL,

WS_CHILD | WS_BORDER | HDS_BUTTONS | HDS_HORZ,

0, 0, 0, 0, hwndParent, (HMENU) ID_HEADER, g_hinst,

(LPVOID) NULL)) == NULL)

return (HWND) NULL;

// Retrieve the bounding rectangle of the parent window's

// client area, and then request size and position values

// from the header control.

GetClientRect(hwndParent, &rcParent);

hdl.prc = &rcParent;

hdl.pwpos = &wp;

if (!SendMessage(hwndHeader, HDM_LAYOUT, 0, (LPARAM) &hdl))

return (HWND) NULL;

// Set the size, position, and visibility of the header control.

SetWindowPos(hwndHeader, wp.hwndInsertAfter, wp.x, wp.y,

wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);

return hwndHeader;

}

+$#A Adding an Item to a Header Control

The following example demonstrates how to use the HDM_INSERTITEM message and the HD_ITEM structure to add an item to a header control. The new item consists of a string that is left-justified within the item rectangle.

// DoInsertItem - inserts items into a header control.

// Returns the index of the newly added item.

// hwndHeader - handle of the header control

// iInsertAfter - index of the previous item

// nWidth - width of the new item

// lpsz - address of the item string

int DoInsertItem(HWND hwndHeader, int iInsertAfter,

int nWidth, LPSTR lpsz)

{

HD_ITEM hdi;

int index;

hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;

hdi.pszText = lpsz;

hdi.cxy = nWidth;

hdi.cchTextMax = lstrlen(hdi.pszText);

hdi.fmt = HDF_LEFT | HDF_STRING;

index = SendMessage(hwndHeader, HDM_INSERTITEM,

(WPARAM) iInsertAfter, (LPARAM) &hdi);

return index;

}

+$#A Header Control Reference

The following messages, notification messages, and structures are associated with header controls. These elements can be grouped as follows.

Size and Position

HDM_LAYOUT
HD_LAYOUT

Inserting and Deleting Items

HDM_DELETEITEM
HDM_INSERTITEM

Item Attributes

HDM_SETITEM
HDN_ITEMCHANGED
HDN_ITEMCHANGING
HDN_TRACK

Item Information

HDM_GETITEM
HDM_GETITEMCOUNT
HDN_ITEMCLICK
HDN_ITEMDBLCLICK
HD_ITEM
HD_NOTIFY

Divider Tracking

HDN_BEGINTRACK
HDN_DIVIDERDBLCLICK
HDN_ENDTRACK

Hit Testing

HD_HITTESTINFO
HDM_HITTEST

+$#A Header Control Messages

An application sends messages to add items and to control the appearance and behavior of a header control. Each message has a corresponding macro, which you can use instead of sending the message explicitly. There are the following header control messages.

HDM_DELETEITEM
HDM_GETITEM
HDM_GETITEMCOUNT
HDM_HITTEST
HDM_INSERTITEM

+$#A Header Control Notification Messages

A header control sends notification messages, in the form of WM_NOTIFY messages, to its parent window to notify it about events. The following notification messages are used with header controls.

HDN_BEGINTRACK
HDN_DIVIDERDBLCLICK
HDN_ENDTRACK
HDN_ITEMCHANGED
HDN_ITEMCHANGING
HDN_ITEMCLICK
HDN_ITEMDBLCLICK
HDN_TRACK

+$#A Header Control Structures

The following structures are used with header controls.

HD_HITTESTINFO
HD_ITEM
HD_LAYOUT
HD_NOTIFY

+$#A Header Control Styles

The following window styles are used with header controls.

HDS_BUTTONS | Header items behave like buttons.
HDS_HORZ | The header control is horizontal.

Créé avec HelpNDoc Personal Edition: Créer des aides HTML, DOC, PDF et des manuels depuis une même source


Help.rtf

+,

+$#AK Help

Online help can come in a variety of forms, from detailed conceptual information to quick definitions. This overview discusses elements of the Win32 application programming interface (API) that support online help.

+$#A About Help

An important element of a user-friendly application is readily available online help. Microsoft® Windows® provides functions and messages that when used in conjunction with the Windows Help application make it easy to implement online help in your application. This overview discusses elements of the Win32 application programming interface (API) that support online help. It describes how to use these elements to give users a means to request help and explains how to use the Windows Help application to display help.

+$#A Help Requests

Most Windows-based applications provide online help information in a variety of forms, ranging from conceptual help that explains the purpose of an application's features to pop-up help that provides quick definitions of individual elements in the application's user interface. You use Win32 functions and messages to give users a variety of ways to request access to this information. The following sections describe these help requests.

+$#A Help Menu

Most applications provide user access to help information by including a Help menu in the main window. When the user selects an item from a Help menu, the corresponding window procedure receives a WM_COMMAND message that identifies the selected item. The application responds by displaying the appropriate help information, such as a list of help topics, an index, or an introduction to the application.

+$#A Help from the Keyboard

Windows provides user access to help information from the keyboard by notifying the application whenever the user presses the F1 key. The system sends WM_HELP to the window that has the keyboard focus when the user presses the F1 key. If the window is child window (for example, a control in a dialog box), the DefWindowProc function passes the message to the parent window. If a menu is active when F1 is pressed, the system sends the message to the window associated with the menu. The application responds by displaying help information associated with the window, control, or menu that has the focus or is active. For example, if the user selects a control in a dialog box and presses F1, the application displays help information for that control.

The lParam parameter of WM_HELP is a pointer to a HELPINFO structure that contains detailed information about the item for which help is requested. You use this information to determine the help topic to display. The HELPINFO structure also includes the coordinates of the mouse cursor at the time the user pressed the F1 key. You can use this information to provide help based on the location of the mouse cursor.

+$#A Help from the Mouse

Windows provides user access to help information from the mouse by notifying the application whenever the user clicks the right mouse button or clicks a window, control, or menu after clicking the Question (?) button. The application responds by displaying help information associated with the given window, control, or menu.

The system sends a WM_CONTEXTMENU message when the user clicks the right mouse button. The window that was clicked receives the message. If the window is a child window, such as a control, the DefWindowProc function passes the message to the parent window. The WM_CONTEXTMENU message specifies the coordinates of the mouse cursor. The x-coordinate is in the low-order word of the lParam parameter, and the y-coordinate is in the high-order word. If the user clicked a control, the wParam parameter is the handle of the control that received the button click.

The system sends a WM_HELP message when the user clicks an item in a window after clicking the Question (?) button that appears in the title bar of the window. You can add a Question button to a title bar by specifying the WS_EX_CONTEXTHELP style in the CreateWindowEx function when creating the window. The lParam parameter of WM_HELP is a pointer to a HELPINFO structure that contains detailed information about the item for which help is requested, including the coordinates of the mouse cursor at the time the user clicked the mouse button.

The Question button is recommended for use in dialog boxes only. In the past, applications have provided user access to help information about a dialog box by providing a Help button in the dialog box. This method is no longer recommended. Use the Question button instead.

+$#A Help Display and Windows Help

Once an application receives a request for help, it should display the appropriate help information. Because the Windows Help application supports a variety of ways to display help and a user interface that is consistent with that used by Windows to display its own help information, it is recommended that applications use Windows Help rather than other ways to display help. To direct Windows Help to display help information, an application uses the WinHelp function, specifying details such as the information to display and the form of the window in which to display it. The following sections explain how to use WinHelp to display help information.

+$#A Help Files

To view help information, you must specify a help file when calling the WinHelp function. The help file must have the Windows Help (.HLP) file format and have one or more topics. Each topic is a distinct unit of information, such as a conceptual description, a set of instructions, a picture, a glossary definition, and so on. Topics must be uniquely identified so that Windows Help can locate the topic whenever it is requested. Internally, Windows Help uses topic identifiers to locate topics, but applications most often use context identifiers (unique integer values) to specify topics to display. The help file author must explicitly map context identifiers to topic identifiers in the [MAP] section of the project file used to build the help file.

When you specify a help file but you do not specify a path, WinHelp looks for the help file in the Help directory or in a directory specified by the PATH environment variable. In addition, WinHelp can find a help file whose name is listed in the following registry location:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Help

To take advantage of the registry, you must create a value name that has the same name as your help file. The value assigned to that name must be the directory where the help file resides.

If WinHelp cannot find the given help file, it displays a dialog box that allows the user to specify the location of the help file. Because WinHelp saves the location information in the registry, WinHelp does not ask again for the location of the same help file.

For more information about how to author and build a help file, see the documentation provided with your development tools.

+$#A Starting Windows Help

The following example uses the WinHelp function to start the Windows Help application and open the Windows NT Help file to its Contents topic.

HWND hwnd; // main window handle

BOOL bResult // for checking boolean function result

bResult = WinHelp(hWnd, "WINNT.HLP", HELP_CONTENTS, 0L);

This next example opens the Windows NT user Help file, searches the file for the topic associated with a keyword string, and then displays the topic.

HWND hwnd; // main window handle

BOOL bResult // for checking boolean function result

bResult = WinHelp(hWnd, "WINNT.HLP", HELP_KEY,

(DWORD) "finding topics");

+$#A Help Topics Dialog Box

You can display the Help Topics dialog box by calling the WinHelp function with the HELP_FINDER command. The Help Topics dialog box lets the user select topics to display by viewing the titles of the topics, the keywords associated with the topics, or the words and phrases found in the topics. Applications typically display the Help Topics dialog box when the user chooses a command, such as Help Topics, from the Help menu. An application may also display this dialog box if the user presses the F1 key when no specific window, control, or menu in the application has the focus or is active.

In the past, applications have used the HELP_CONTENTS and HELP_INDEX commands with the WinHelp function to display the Contents topic and the keyword index of the help file. These commands are no longer recommended. Use the HELP_FINDER command instead.

+$#A Information Topics

You can display a specific topic by calling the WinHelp function with the HELP_CONTEXT command and specifying the context identifier for the topic. Applications typically use the HELP_CONTEXT command in response to user requests for topics containing conceptual information or procedural help rather than information about a specific control or menu. In such cases, the user may continue to browse the help file looking for related information before returning to the application.

The HELP_CONTEXT command invokes a regular instance of Windows Help, enabling the user to access other topics in the help file. It typically displays the main help window, which includes a title bar, system menu, minimize and maximize buttons, main menu, optional navigation bar, sizing border, and a client area. The text of the selected topic appears in the client area, and the user can navigate through the help file by using hot links or navigation buttons in the main window. The regular instance of Windows Help can also be used to display help in one or more secondary windows instead of the main window.

+$#A Pop-up Topics

You can display a pop-up topic that contains information for a specific control or menu by calling the WinHelp function with the HELP_WM_HELP or HELP_CONTEXTMENU command. These commands display a topic in a pop-up window near the corresponding control or menu. To let the user return immediately to work in the application, the pop-up window is destroyed as soon as the user presses a key or clicks the left mouse button.

You use the HELP_WM_HELP command when processing WM_HELP messages for control windows. Because most controls pass the WM_HELP message to the DefWindowProc function, the corresponding dialog box procedure (or parent window procedure) processes this message. Rather than give a specific context identifier, the dialog box procedure must pass an array of control and context identifier pairs to WinHelp along with the control handle specified in the hItemHandle member of HELPINFO structure passed with the WM_HELP message. The function determines the identifier of the control for which the WM_HELP message was generated and uses the matching context identifier to display the appropriate topic.

You use the HELP_CONTEXTMENU command when processing WM_CONTEXTMENU messages. Because most controls pass the WM_CONTEXTMENU message to the DefWindowProc function, the corresponding dialog box procedure (or parent window procedure) processes this message. Again, the procedure specify an array of control and context identifier pairs and the handle in wParam when calling WinHelp so that the function can pick the appropriate context identifier from the array and display the appropriate topic. Unlike the HELP_WM_HELP command, HELP_CONTEXTMENU first displays a What's This? command in a menu. If the user chooses the command, WinHelp displays the topic. Otherwise, the request is canceled.

You can also display pop-up topics by using the HELP_CONTEXTPOPUP command and specifying a context identifier of the topic. This command is similar to the HELP_CONTEXT command but invokes the pop-up instance of Windows Help used by HELP_WM_HELP and HELP_CONTEXTMENU. Applications can use this command in response to WM_HELP messages to display help for menus and for windows that are not controls in a dialog box. To use this command most effectively, the application should assign context identifiers to these menus and windows.

You can assign a context identifier to any window or menu in the application. When the user's help requests generates a WM_HELP message, the system includes the context identifier in the HELPINFO structure that it passes to the parent window along with the WM_HELP message. The parent window can then pass the context identifier to WinHelp to display the requested help topic.

You use the SetWindowContextHelpId function to assign a context identifier to a window or control and the SetMenuContextHelpId function to assign a context identifier to a menu. You can retrieve the context identifier for a window or menu by using the GetWindowContextHelpId or GetMenuContextHelpId function.

+$#A Keyword Searches

You can enable the user to find and view topics by assigning keywords to topics in the help file. A keyword is simply a string that is associated with one or more topics. Windows Help collects all keywords in a help file, places them in a table, and displays them in the Index list of the Help Topics dialog box. When the user selects a keyword, Windows Help displays the associated help topic or displays a list of topics from which the user can choose if there is more than one topic associated with the keyword.

In an application, you can use the HELP_KEY, HELP_PARTIALKEY, or HELP_MULTIKEY command with WinHelp to search for and display help topics based on whole or partial keywords. You specify the command, the keyword string, the help file, and the handle of the owner window. In all cases, if a single match is found, WinHelp displays the corresponding topic. If more than one match is found, the function displays the Topics Found dialog, letting the user choose which topic to view. If no match is found, WinHelp either displays the Index list (for HELP_KEY and HELP_PARTIALKEY) or displays an error message (for HELP_MULTIKEY).

You can search for multiple keywords in a single call to WinHelp by separating each keyword from the preceeding one with a semicolon (;). (Searching for multiple keywords is not supported for help files created for version 3.x.) You can also search for a keywords across multiple help files if the help file that you specify has a contents (.CNT) file that contains :Index or :Link commands. With the HELP_KEY command, WinHelp searches for keywords in all files specified by these commands. With the HELP_MULTIKEY and HELP_PARTIALKEY commands, the function searches all files except those specified by :Link commands.

By default, Windows Help recognizes only the keyword table identified by the K footnote character in the help source file. You can direct Windows Help to create additional keyword tables by specifying a footnote character other than K with the keyword definitions in the Windows Help file. (The footnote character A, however, is reserved.) You must define any additional keyword tables by using MULTIKEY statements in the [OPTIONS] section of the project file when building the help file.

An application can use the HELP_SETINDEX command with the WinHelp function to direct Windows Help to display a keyword table other than K in its Index list. To direct Windows Help to search for a keyword in an alternate keyword table, an application can use the HELP_MULTIKEY command. You specify the keyword and keyword table in a MULTIKEYHELP structure, which you pass to WinHelp.

When WinHelp displays a topic, it displays it in the window specified by the > footnote for the topic, or in the window specified by the :Base command in the contents file, or in the main window. If the main window is already open to a different help file when you call WinHelp, the function hides main window while searching. In this case, canceling both the Topics Found and Help Topics dialogs, close the main window.

+$#A Secondary Help Windows

The main window of the Windows Help application is called the primary window. Windows Help can also display help topics in a secondary window. Unlike the main help window, a secondary window does not contain a menu bar. You can include a navigation bar in a secondary window, and you can add buttons to the bar. You can also choose to have Windows Help automatically adjust the height of secondary window to accommodate the topic.

You must define secondary windows in the [WINDOWS] section of your help project file, providing the name and, optionally, initial size and position of each window. You can direct the Windows Help application to display a topic in a secondary window by appending an angle bracket (>) and the name you have defined for the secondary window to the name of the help file and then passing the resulting string to the WinHelp function.

An application can change the size and position of a primary or secondary window by specifying the address of a HELPWININFO structure and the HELP_SETWINPOS command in a call to WinHelp. HELPWININFO specifies the name of the window and its new size and position. The primary help window is the "main" window.

+$#A Training Card Help

Using training card help, an application can display a sequence of instructions to guide the user through the steps of a task. A training card typically consists of text that explains a particular step and authorable buttons associated with TCard macros that allow the user to tell the application what to do next. Training cards may only be displayed in secondary windows and must not contain hot links to other topics in the help file.

An application initiates the training card instance of Windows Help by calling the WinHelp function and specifying the HELP_TCARD command in combination with another command such as HELP_CONTEXT. Subsequently, when the user clicks an authorable button in the training card, clicks a hot spot assigned to the TCard macro, or closes the training card, Windows Help notifies the application by sending it a WM_TCARD message. The wParam parameter identifies the button or user action, and the lParam parameter contains additional data, the meaning of which depends on the value of wParam.

+$#A Canceling Help

Windows Help requires an application to explicitly cancel Help so that it can free any resources it used to keep track of the application and its help files. The application can do this at any time by calling the WinHelp function and specifying the HELP_QUIT command. Note that this is not true for the pop-up instance of Windows Help. An application should not try to close the pop-up instance.

If an application has made any calls to WinHelp, it must cancel Help before it closes its main window (for example, in response to the WM_DESTROY message in the main window procedure). An application needs to call WinHelp only once to cancel Help, no matter how many help files it has opened. Windows Help remains running until all applications or dynamic-link libraries (DLLs) that have called WinHelp have canceled Help.

To close the training card instance of Windows Help, you must specify both the HELP_TCARD and HELP_QUIT commands when calling the WinHelp function. An application does not need to cancel the training card instance of Windows Help if the user cancels it first. Windows Help notifies an application when the user cancels the training card instance by sending the WM_TCARD message with the wParam parameter set to IDCLOSE.

+$#A Using Help

This section shows how to provide context-sensitive help for a dialog box and how to set the appearance of a secondary help window.

• Providing Help in a dialog box

• Setting the appearance of a secondary help window

+$#A Providing Help in a Dialog Box

To provide context-sensitive help in a dialog box, you must create an array consisting of pairs of doubleword values. The first value in each pair is the identifier of a control in the dialog box, and the second is the context identifier of the help topic for the control. The array should contain one pair of identifiers for each control in the dialog box.

The dialog box procedure must process the WM_HELP and WM_CONTEXTMENU messages. The dialog box procedure receives WM_HELP when the user presses the F1 key and WM_CONTEXTMENU when the user clicks the right mouse button.

The lParam parameter of WM_HELP is a pointer to a HELPINFO structure. The hItemHandle member of this structure identifies the control for which the user has requested help. You must pass the handle to the WinHelp function along with the HELP_WM_HELP command, the name of your help file, and a pointer to the array of identifiers. The WinHelp function searches the array for the control identifier of the specified control and then retrieves the corresponding help context identifier. Next, the function passes the help context identifier to Windows Help, which finds the corresponding topic and displays it in a pop-up window. If the control has an identifier of − 1, the system searches for the next control that is a tab stop and uses its identifier to find the help context identifier. For this reason, it is important that you place static text before controls in a resource file.

Processing WM_CONTEXTMENU is similar to processing WM_HELP when calling the WinHelp function, with these two exceptions:

• You pass the wParam parameter from WM_CONTEXTMENU, which is the handle of the control that sent the message.

• You specify the HELP_CONTEXTMENU command instead of HELP_WM_HELP.

The HELP_CONTEXTMENU command causes Windows Help to display a menu before it displays the help topic. The menu is system defined. It allows the user to display help for the control or to display the Help Topics dialog box.

The following example shows how to implement context-sensitive help in a dialog box.

LRESULT CALLBACK EditDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

// Create an array of control identifiers and context identifiers.

static DWORD aIds[] = {

ID_SAVE, IDH_SAVE,

ID_DELETE, IDH_DELETE,

ID_COPY, IDH_COPY,

ID_PASTE, IDH_PASTE,

0,0

};

switch (uMsg) {

case WM_HELP:

WinHelp(((LPHELPINFO) lParam)->hItemHandle, "helpfile.hlp",

HELP_WM_HELP, (DWORD) (LPSTR) aIds);

break;

case WM_CONTEXTMENU:

WinHelp((HWND) wParam, "helpfile.hlp", HELP_CONTEXTMENU,

(DWORD) (LPVOID) aIds);

break;

.

. // process other messages here

.

}

return FALSE;

}

+$#A Setting the Appearance of a Secondary Help Window

An application can set the size, position, and show state of a secondary help window by passing the HELP_SETWINPOS command and the address of a HELPWININFO structure to the WinHelp function. The members of HELPWININFO specify the name of the window to change and the window's new size, position, and show state.

The following example sets the appearance of a secondary window named "wnd_menu". The name must be defined in the [WINDOWS] section of the help project file.

BOOL DoWindowSize(VOID)

{

HANDLE hhwi;

LPHELPWININFO lphwi;

WORD wSize;

char *szWndName = "wnd_menu";

wSize = sizeof(HELPWININFO) + lstrlen(szWndName);

hhwi = GlobalAlloc(GHND, wSize);

lphwi = (LPHELPWININFO) GlobalLock(hhwi);

lphwi->wStructSize = wSize;

lphwi->x = 256; // horizontal position

lphwi->y = 256; // vertical position

lphwi->dx = 767; // width

lphwi->dy = 512; // height

lphwi->wMax = SW_SHOW; // show the window

lstrcpy(lphwi->rgchMember, szWndName); // secondary window

WinHelp(hwnd, "myhelp.hlp", HELP_SETWINPOS, (DWORD) lphwi);

GlobalUnlock(hhwi);

GlobalFree(hhwi);

return;

}

+$#A Help Reference

The following sections list the messages and functions associated with Help controls.

+$#A Help Functions

The following functions are used with online Help.

GetMenuContextHelpId
GetWindowContextHelpId
SetMenuContextHelpId
SetWindowContextHelpId
WinHelp

+$#A Help Messages

The following messages are used with online Help.

WM_HELP
WM_TCARD

Créé avec HelpNDoc Personal Edition: Générer des livres électroniques EPub facilement


Hooks.rtf

+$#AK Hooks

A hook is a point in the Microsoft® Windows® message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.

This overview describes Windows hooks and explains how to use them in a Windows-based application.

+$#A About Hooks

Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.

+$#A Hook Chains

Windows contains many different types of hooks; each type provides access to a different aspect of the Windows message-handling mechanism. For example, an application can use the WH_MOUSE hook to monitor the message traffic for mouse messages.

Windows maintains a separate hook chain for each type of hook. A hook chain is a list of pointers to special, application-defined callback functions called hook procedures. When a message occurs that is associated with a particular type of hook, Windows passes the message to each hook procedure referenced in the hook chain, one after the other. The action a hook procedure can take depends on the type of hook involved. The hook procedures for some types of hooks can only monitor messages; others can modify messages or stop their progress though the chain, preventing them from reaching the next hook procedure or the destination window.

+$#A Hook Procedures

To take advantage of a particular type of hook, the developer provides a hook procedure and uses the SetWindowsHookEx function to install it into the chain associated with the hook. A hook procedure must have the following syntax:

LRESULT CALLBACK HookProc(nCode, wParam, lParam)
int nCode;
WPARAM wParam;
LPARAM lParam;

HookProc is a placeholder for an application-defined name. Because Windows must call the hook procedure, the actual name must be exported by listing it and, optionally, its ordinal value, in the EXPORTS statement of the application's module-definition file.

The nCode parameter is a hook code that the hook procedure uses to determine the action to perform. The value of the hook code depends on the type of the hook; each type has its own characteristic set of hook codes. The values of the wParam and lParam parameters depend on the hook code, but they typically contain information about a message that was sent or posted.

SetWindowsHookEx always installs a hook procedure at the beginning of a hook chain. When an event occurs that is monitored by a particular type of hook, Windows calls the procedure at the beginning of the hook chain associated with the hook. Each hook procedure in the chain determines whether to pass the event to the next procedure. A hook procedure passes an event to the next procedure by calling the CallNextHookEx function.

Note that the hook procedures for some types of hooks can only monitor messages. Windows passes messages to each hook procedure, regardless of whether a particular procedure calls CallNextHookEx.

A hook procedure can be global, monitoring messages for all threads in the system, or it can be thread specific, monitoring messages for only an individual thread. A global hook procedure can be called in the context of any application, so the procedure must be in a separate dynamic-link library (DLL) module. A thread specific hook procedure is called only in the context of the associated thread. If an application installs a hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the application's code or in a DLL. If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL. For information about DLLs, see Dynamic-Link Libraries.

Note You should use global hooks only for debugging purposes; otherwise, you should avoid them. Global hooks hurt system performance and cause conflicts with other applications that implement the same type of global hook.

+$#A Hook Types

Each type of hook enables an application to monitor a different aspect of the Windows message-handling mechanism. The following sections describe the types of hooks available in Windows.

+$#A WH_CALLWNDPROC and WH_CALLWNDPROCRET Hooks

The WH_CALLWNDPROC and WH_CALLWNDPROCRET hooks enable an application to monitor messages sent to window procedures by the SendMessage function. Windows calls a WH_CALLWNDPROC hook procedure before passing the message to the receiving window procedure, and calls the WH_CALLWNDPROCRET hook procedure after the window procedure has processed the message.

The WH_CALLWNDPROCRET hook passes the address of a CWPRETSTRUCT structure to the hook procedure. The structure contains the return value from the window procedure that processed the message, as well as the message parameters associated with the message. In previous versions of Windows, an application that needed the return value had to set a WH_CALLWNDPROC hook and subclass the window when the hook was called. However, subclassing does not work for messages set between processes.

+$#A WH_CBT Hook

Windows calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the input focus; or before synchronizing with the system message queue. The value the hook procedure returns determines whether Windows allows or prevents one of these operations. The WH_CBT hook is intended primarily for Windows computer-based training (CBT) applications.

+$#A WH_DEBUG Hook

Windows calls a WH_DEBUG hook procedure before calling hook procedures associated with any other hook in the system. An application can use this hook to determine whether to allow the system to call hook procedures associated with other types of hooks.

+$#A WH_FOREGROUNDIDLE Hook

The WH_FOREGROUNDIDLE hook enables an application to peform low priority tasks during times when its foreground thread is idle. Windows calls a WH_FOREGROUNDIDLE hook procedure when the application's foreground thread is about to become idle.

+$#A WH_GETMESSAGE Hook

The WH_GETMESSAGE hook enables an application to monitor messages about to be returned by the GetMessage or PeekMessage function. An application uses the WH_GETMESSAGE hook to monitor mouse and keyboard input and other messages posted to a queue.

+$#A WH_JOURNALRECORD Hook

The WH_JOURNALRECORD hook enables an application to monitor and record input events. Typically, an application uses this hook to record a sequence of mouse and keyboard events it can play back later by using the WH_JOURNALPLAYBACK hook. The WH_JOURNALRECORD hook is a global hook ⎯ it cannot be used as a thread-specific hook.

+$#A WH_JOURNALPLAYBACK Hook

The WH_JOURNALPLAYBACK hook enables an application to insert messages into the system message queue. An application might use this hook to play back a series of mouse and keyboard events recorded earlier by using the WH_JOURNALRECORD hook. Regular mouse and keyboard input is disabled as long as a WH_JOURNALPLAYBACK hook is installed. A WH_JOURNALPLAYBACK hook is a global hook ⎯ it cannot be used as a thread-specific hook.

The WH_JOURNALPLAYBACK hook returns a time-out value. This value tells the system how many milliseconds to wait before processing the current message from the playback hook. This enables the hook to control the timing of the events it plays back.

+$#A WH_KEYBOARD Hook

The WH_KEYBOARD hook enables an application to monitor message traffic for WM_KEYDOWN and WM_KEYUP messages about to be returned by the GetMessage or PeekMessage function. An application uses the WH_KEYBOARD hook to monitor keyboard input posted to a message queue.

+$#A WH_MOUSE Hook

The WH_MOUSE hook enables an application to monitor mouse messages about to be returned by the GetMessage or PeekMessage function. An application uses the WH_MOUSE hook to monitor mouse input posted to a message queue.

+$#A WH_MSGFILTER and WH_SYSMSGFILTER Hooks

The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable an application to monitor messages about to be processed by a menu, scroll bar, message box, or dialog box, and to detect when a different window is about to be activated as a result of the user's pressing the ALT+TAB or ALT+ESC key combination. The WH_MSGFILTER hook can monitor only messages passed to a menu, scroll bar, message box, or dialog box created by the application that installed the hook procedure. The WH_SYSMSGFILTER hook monitors such messages for all applications.

The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable an application to perform message filtering during modal loops that is equivalent to the filtering done in the main message loop. For example, an application often examines a new message in the main loop between the time it retrieves the message from the queue and the time it dispatches the message, performing special processing as appropriate. However, during a modal loop, the system retrieves and dispatches messages without allowing an application the chance to filter the messages in its main message loop. If an application installs a WH_MSGFILTER or WH_SYSMSGFILTER hook procedure, the system calls the procedure during the modal loop. For more information about modal loops, see Windows.

An application can call the WH_MSGFILTER hook directly by calling the CallMsgFilter function. By using this function, the application can use the same code to filter messages during modal loops as it uses in the main message loop. To do so, the application encapsulates the filtering operations in a WH_MSGFILTER hook procedure and calls CallMsgFilter between the calls to the GetMessage and DispatchMessage functions.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

if (!CallMsgFilter(&qmsg, 0))

DispatchMessage(&qmsg);

}

The last argument of CallMsgFilter is simply passed to the hook procedure; the application can enter any value. The hook procedure, by defining a constant such as MSGF_MAINLOOP, can use this value to determine where the procedure was called from.

+$#A WH_SHELL Hook

A Windows shell application can use the WH_SHELL hook to receive important notifications. Windows calls a WH_SHELL hook procedure when the shell application is about to be activated and when a top-level window is created or destroyed.

+$#A Using Hooks

• Installing and Releasing Hook Procedures

• Monitoring System Events

+$#A Installing and Releasing Hook Procedures

You can install a hook procedure by calling the SetWindowsHookEx function and specifying the type of hook calling the procedure, whether the procedure should be associated with all threads or with a particular thread, and a pointer to a procedure entry point.

You must place a global hook procedure in a DLL separate from the application installing the hook procedure. The installing application must have the handle of the DLL module before it can install the hook procedure. The LoadLibrary function, when given the name of the DLL, returns the handle of the DLL module. After you have the handle, you can call the GetProcAddress function to retrieve the address of the hook procedure. Finally, you use SetWindowsHookEx to install the hook procedure address in the appropriate hook chain. SetWindowsHookEx passes the module handle, a pointer to the hook-procedure entry point, and 0 for the thread identifier, indicating that the hook procedure should be associated with all threads in the system. This sequence is shown in the following example.

HOOKPROC hkprcSysMsg;

static HINSTANCE hinstDLL;

static HHOOK hhookSysMsg;

.

.

.

hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll");

hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");

hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,

hkprcSysMsg, hinstDLL, 0);

.

.

.

You can release a thread-specific hook procedure (remove its address from the hook chain) by calling the UnhookWindowsHookEx function, specifying the handle of the hook procedure to release. Release a hook procedure as soon as your application no longer needs it.

You can release a global hook procedure by using UnhookWindowsHookEx, but this function does not free the DLL containing the hook procedure. This is because global hook procedures are called in the process context of every Windows-based application in the system, causing an implicit call to the LoadLibrary function for all of those processes. Because a call to the FreeLibrary function cannot be made for another process, there is then no way to free the DLL. Windows eventually frees the DLL after all processes explicitly linked to the DLL have either terminated or called FreeLibrary and all processes that called the hook procedure have resumed processing outside the DLL.

An alternative method for installing a global hook procedure is to provide an installation function in the DLL, along with the hook procedure. With this method, the installing application does not need the handle of the DLL module. By linking with the DLL, the application gains access to the installation function. The installation function can supply the DLL module handle and other details in the call to SetWindowsHookEx. The DLL can also contain a function that releases the global hook procedure; the application can call this hook-releasing function when terminating.

+$#A Monitoring System Events

The example in this section uses a variety of thread-specific hook procedures to monitor the system for events affecting a thread. It demonstrates how to process events for the following types of hook procedures:

WH_CALLWNDPROC
WH_CBT
WH_DEBUG
WH_GETMESSAGE
WH_KEYBOARD
WH_MOUSE
WH_MSGFILTER

In this example, the user can install and remove a hook procedure by using the menu. When a hook procedure is installed and an event that is monitored by the procedure occurs, the procedure writes information about the event to the client area of the application's main window.

#define NUMHOOKS 7

/* Global variables */

typedef struct _MYHOOKDATA {

int nType;

HOOKPROC hkprc;

HHOOK hhook;

} MYHOOKDATA;

MYHOOKDATA myhookdata[NUMHOOKS];

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

UINT wParam;

LONG lParam;

{

static BOOL afHooks[NUMHOOKS];

int index;

static HMENU hmenu;

switch (uMsg) {

case WM_CREATE:

/* Save the menu handle. */

hmenu = GetMenu(hwndMain);

/*

* Initialize structures with hook data. The menu-item

* identifiers are defined as 0 through 6 in the

* header file. They can be used to identify array

* elements both here and during the WM_COMMAND

* message.

*/

myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC;

myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc;

myhookdata[IDM_CBT].nType = WH_CBT;

myhookdata[IDM_CBT].hkprc = CBTProc;

myhookdata[IDM_DEBUG].nType = WH_DEBUG;

myhookdata[IDM_DEBUG].hkprc = DebugProc;

myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE;

myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc;

myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD;

myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc;

myhookdata[IDM_MOUSE].nType = WH_MOUSE;

myhookdata[IDM_MOUSE].hkprc = MouseProc;

myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER;

myhookdata[IDM_MSGFILTER].hkprc = MessageProc;

/* Initialize all flags in the array to FALSE. */

memset(afHooks, FALSE, sizeof(afHooks));

return 0;

case WM_COMMAND:

switch (LOWORD(wParam)) {

/*

* The user selected a hook command from the menu.

*/

case IDM_CALLWNDPROC:

case IDM_CBT:

case IDM_DEBUG:

case IDM_GETMESSAGE:

case IDM_KEYBOARD:

case IDM_MOUSE:

case IDM_MSGFILTER:

/*

* Use the menu-item identifier as an index

* into the array of structures with hook data.

*/

index = LOWORD(wParam);

/*

* If the selected type of hook procedure isn't

* installed yet, install it and check the

* associated menu item.

*/

if (!afHooks[index]) {

myhookdata[index].hhook = SetWindowsHookEx(

myhookdata[index].nType,

myhookdata[index].hkprc,

(HINSTANCE) NULL, GetCurrentThreadId());

CheckMenuItem(hmenu, index,

MF_BYCOMMAND | MF_CHECKED);

afHooks[index] = TRUE;

}

/*

* If the selected type of hook procedure is

* already installed, remove it and remove the

* check mark from the associated menu item.

*/

else {

UnhookWindowsHookEx(myhookdata[index].hhook);

CheckMenuItem(hmenu, index,

MF_BYCOMMAND | MF_UNCHECKED);

afHooks[index] = FALSE;

}

default:

return (DefWindowProc(hwndMain, uMsg, wParam,

lParam));

}

break;

.

. /* Process other messages. */

.

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}

/****************************************************************

WH_CALLWNDPROC hook procedure

****************************************************************/

LRESULT CALLBACK CallWndProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szCWPBuf[256];

CHAR szMsg[16];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,

wParam, lParam);

/*

* Call an application-defined function that converts a message

* constant to a string and copies it to a buffer.

*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);

switch (nCode) {

case HC_ACTION:

cch = wsprintf(szCWPBuf,

"CALLWNDPROC - tsk: %ld, msg: %s, %d times ",

wParam, szMsg, c++);

TextOut(hdc, 2, 15, szCWPBuf, cch);

break;

default:

break;

}

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,

wParam, lParam);

}

/****************************************************************

WH_GETMESSAGE hook procedure

****************************************************************/

LRESULT CALLBACK GetMsgProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szMSGBuf[256];

CHAR szRem[16];

CHAR szMsg[16];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,

wParam, lParam);

switch (nCode) {

case HC_ACTION:

switch (wParam) {

case PM_REMOVE:

lstrcpy(szRem, "PM_REMOVE");

break;

case PM_NOREMOVE:

lstrcpy(szRem, "PM_NOREMOVE");

break;

default:

lstrcpy(szRem, "Unknown");

break;

}

/*

* Call an application-defined function that converts a

* message constant to a string and copies it to a

* buffer.

*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);

cch = wsprintf(szMSGBuf,

"GETMESSAGE - wParam: %s, msg: %s, %d times ",

szRem, szMsg, c++);

TextOut(hdc, 2, 35, szMSGBuf, cch);

break;

default:

break;

}

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,

wParam, lParam);

}

/****************************************************************

WH_DEBUG hook procedure

****************************************************************/

LRESULT CALLBACK DebugProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szBuf[128];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[DEBUG].hhook, nCode,

wParam, lParam);

hdc = GetDC(hwndMain);

switch (nCode) {

case HC_ACTION:

cch = wsprintf(szBuf,

"DEBUG - nCode: %d, tsk: %ld, %d times ",

nCode,wParam, c++);

TextOut(hdc, 2, 55, szBuf, cch);

break;

default:

break;

}

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam,

lParam);

}

/****************************************************************

WH_CBT hook procedure

****************************************************************/

LRESULT CALLBACK CBTProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szBuf[128];

CHAR szCode[128];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,

lParam);

hdc = GetDC(hwndMain);

switch (nCode) {

case HCBT_ACTIVATE:

lstrcpy(szCode, "HCBT_ACTIVATE");

break;

case HCBT_CLICKSKIPPED:

lstrcpy(szCode, "HCBT_CLICKSKIPPED");

break;

case HCBT_CREATEWND:

lstrcpy(szCode, "HCBT_CREATEWND");

break;

case HCBT_DESTROYWND:

lstrcpy(szCode, "HCBT_DESTROYWND");

break;

case HCBT_KEYSKIPPED:

lstrcpy(szCode, "HCBT_KEYSKIPPED");

break;

case HCBT_MINMAX:

lstrcpy(szCode, "HCBT_MINMAX");

break;

case HCBT_MOVESIZE:

lstrcpy(szCode, "HCBT_MOVESIZE");

break;

case HCBT_QS:

lstrcpy(szCode, "HCBT_QS");

break;

case HCBT_SETFOCUS:

lstrcpy(szCode, "HCBT_SETFOCUS");

break;

case HCBT_SYSCOMMAND:

lstrcpy(szCode, "HCBT_SYSCOMMAND");

break;

default:

lstrcpy(szCode, "Unknown");

break;

}

cch = wsprintf(szBuf, "CBT - nCode: %s, tsk: %ld, %d times ",

szCode, wParam, c++);

TextOut(hdc, 2, 75, szBuf, cch);

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,

lParam);

}

/****************************************************************

WH_MOUSE hook procedure

****************************************************************/

LRESULT CALLBACK MouseProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szBuf[128];

CHAR szMsg[16];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process the message */

return CallNextHookEx(myhookdata[MOUSE].hhook, nCode,

wParam, lParam);

/*

* Call an application-defined function that converts a message

* constant to a string and copies it to a buffer.

*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);

cch = wsprintf(szBuf,

"MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times ",

nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++);

TextOut(hdc, 2, 95, szBuf, cch);

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam,

lParam);

}

/****************************************************************

WH_KEYBOARD hook procedure

****************************************************************/

LRESULT CALLBACK KeyboardProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szBuf[128];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode,

wParam, lParam);

hdc = GetDC(hwndMain);

cch = wsprintf(szBuf, "KEYBOARD - nCode: %d, vk: %d, %d times ",

nCode, wParam, c++);

TextOut(hdc, 2, 115, szBuf, cch);

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam,

lParam);

}

/****************************************************************

WH_MSGFILTER hook procedure

****************************************************************/

LRESULT CALLBACK MessageProc(nCode, wParam, lParam)

int nCode;

WPARAM wParam;

LPARAM lParam;

{

CHAR szBuf[128];

CHAR szMsg[16];

CHAR szCode[32];

HDC hdc;

static int c = 0;

int cch;

if (nCode < 0) /* do not process message */

return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,

wParam, lParam);

switch (nCode) {

case MSGF_DIALOGBOX:

lstrcpy(szCode, "MSGF_DIALOGBOX");

break;

case MSGF_MENU:

lstrcpy(szCode, "MSGF_MENU");

break;

case MSGF_SCROLLBAR:

lstrcpy(szCode, "MSGF_SCROLLBAR");

break;

case MSGF_NEXTWINDOW:

lstrcpy(szCode, "MSGF_NEXTWINDOW");

break;

default:

wsprintf(szCode, "Unknown: %d", nCode);

break;

}

/*

* Call an application-defined function that converts a message

* constant to a string and copies it to a buffer.

*/

LookUpTheMessage((PMSG) lParam, szMsg);

hdc = GetDC(hwndMain);

cch = wsprintf(szBuf,

"MSGFILTER nCode: %s, msg: %s, %d times ",

szCode, szMsg, c++);

TextOut(hdc, 2, 135, szBuf, cch);

ReleaseDC(hwndMain, hdc);

return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,

wParam, lParam);

}

+$#AK Hook Reference

The following functions and messages are associated with hooks.

+$#A Hook Functions

Following are the functions used with hooks.

CallMsgFilter
CallNextHookEx
CallWndProc
CBTProc
DebugProc
GetMsgProc
JournalPlaybackProc
JournalRecordProc
KeyboardProc
MessageProc
MouseProc
SetWindowsHookEx
ShellProc
SysMsgProc
UnhookWindowsHookEx

Obsolete or Deleted Functions

DefHookProc
SetWindowsHook
UnhookWindowsHook

+$#A Hook Messages

Following are the messages used with hooks.

WM_QUEUESYNC

Créé avec HelpNDoc Personal Edition: Éditeur complet de livres électroniques ePub


Hot-Key Controls.rtf

+$#AK Hot-Key Controls

A hot-key control is a window that enables the user to enter a combination of keystrokes to be used as a hot key. A hot key is a key combination that the user can press to perform an action quickly. (For example, a user can create a hot key that activates a given window and brings it to the top of the Z order.) The hot-key control displays the user's choices and ensures that the user selects a valid key combination.

+$#A About Hot-Key Controls

When the user enters a key combination to be used as a hot key, the names of the keys appear in the hot-key control. A key combination can consist of a modifier key (such as CTRL, ALT, or SHIFT) and an accompanying key (such as a character key, an arrow key, a function key, and so on).

After the user has chosen a key combination, the application retrieves the key combination from the hot-key control and uses it to set up a hot key in the system. The information retrieved from the hot-key control includes a flag indicating the modifier key and the virtual-key code of the accompanying key.

The application can use the information provided by a hot-key control to set up a global hot key or a thread-specific hot key. A global hot key is associated with a particular window; it allows the user to activate the window from any part of the system. An application sets a global hot key by using the WM_SETHOTKEY message. Whenever the user uses a global hot key, the window specified in WM_SETHOTKEY receives a WM_SYSCOMMAND message that specifies the SC_HOTKEY value. This message activates the window that receives it. The hot key remains valid until the application that called WM_SETHOTKEY exits.

A thread-specific hot key generates a WM_HOTKEY message that is posted to the beginning of a particular thread so that it is removed by the next iteration of the message loop. An application sets a thread-specific hot key by using the RegisterHotKey function.

+$#A Hot-Key Control Creation

You create a hot-key control by using the CreateWindowEx function, specifying the HOTKEY_CLASS window class. When the function returns a handle to the hot-key control, an application typically sets some rules about invalid hot-key combinations and provides perhaps a default key combination. If an application does not set any rules, the user can choose any key or key combination as a hot key. Most applications, however, do not allow the user to use a common key (for example, the letter A) as a hot key.

+$#A About Hot-Key Control Messages

After creating a hot-key control, an application interacts with it by using three messages: HKM_SETRULES, HKM_SETHOTKEY, and HKM_GETHOTKEY.

An application can send the HKM_SETRULES message to specify a set of CTRL, ALT, and SHIFT key combinations that are considered invalid hot keys. If the application specifies an invalid key combination, it should also specify a default modifier combination to use when the user selects the invalid combination. When the user enters the invalid combination, the system performs a logical-OR operation on the invalid combination entered by the user and the default combination. The result is considered a valid combination; it is converted to a string and displayed in the control.

The HKM_SETHOTKEY message allows an application to set the hot key combination for a hot-key control. This message is also typically used when the hot-key control is created.

Applications use the HKM_GETHOTKEY message to retrieve the virtual-key code and modifier flags of the hot key chosen by the user of a hot-key control.

+$#A Default Hot-Key Message Processing

This section describes the window messages handled by the window procedure for the predefined HOTKEY_CLASS window class used with hot-key controls.

Message | Processing performed
WM_CHAR | Retrieves the virtual-key code.
WM_CREATE | Initializes the hot-key control, sets no hot-key rules, and uses the system font.
WM_ERASEBKGND | Hides the caret, calls the DefWindowProc function, and shows the caret again.
WM_GETDLGCODE | Returns a combination of the DLGC_WANTCHARS and DLGC_WANTARROWS values.
WM_GETFONT | Retrieves the font.
WM_KEYDOWN | Calls the DefWindowProc function if the key is ENTER, TAB, SPACE BAR, DEL, ESC, or BACKSPACE. If the key is SHIFT, CTRL, or ALT, it checks whether the combination is valid and, if it is, sets the hot key using the combination. Other keys are set as hot keys without their validity being checked first.
WM_KEYUP | Retrieves the virtual-key code.
WM_KILLFOCUS | Destroys the caret.
WM_LBUTTONDOWN | Sets the focus to the window.
WM_NCCREATE | Sets the WS_EX_CLIENTEDGE window style.
WM_PAINT | Paints the hot-key control.
WM_SETFOCUS | Creates and shows the caret.
WM_SETFONT | Sets the font.
WM_SYSCHAR | Retrieves the virtual-key code.
WM_SYSKEYDOWN | Calls the DefWindowProc function if the key is ENTER, TAB, SPACE BAR, DEL, ESC, or BACKSPACE. If the key is SHIFT, CTRL, or ALT, it checks whether the combination is valid and, if it is, sets the hot key using the combination. Other keys are set as hot keys without their validity being checked first.
WM_SYSKEYUP | Retrieves the virtual-key code.

+$#A Using Hot-Key Controls

• Creating a hot-key control

• Retrieving and setting a hot key

+$#A Creating a Hot-Key Control

The following function creates a hot-key control, uses the HKM_SETRULES and HKM_SETHOTKEY messages to initialize it, and returns a handle to the control. This hot-key control does not allow the user to choose a hot key that is a single unmodified key, nor does it permit the user to choose only SHIFT and a key. (These rules effectively prevent the user from choosing a hot key that might be accidentally entered while typing text.)

// InitializeHotkey - creates a hot-key control and sets rules

// and default settings for it.

// Returns the handle of the hot-key control.

// hwndDlg - handle of the parent window (dialog box)

//

// Global variable

// g_hinst - handle of the application instance

extern HINSTANCE g_hinst;

HWND WINAPI InitializeHotkey(HWND hwndDlg)

{

// Ensure that the common control DLL is loaded.

InitCommonControls();

hwndHot = CreateWindowEx(

0, // no extended styles

HOTKEY_CLASS, // class name

"", // no title (caption)

WS_CHILD | WS_VISIBLE, // style

10, 10, // position

200, 20, // size

hwndDlg, // parent window

NULL, // uses class menu

g_hinst, // instance

NULL // no WM_CREATE parameter

);

SetFocus(hwndHot);

// Set rules for invalid key combinations. If the user

// does not supply a modifier key, use ALT as a modifier.

// If the user supplies SHIFT as a modifier key, use

// SHIFT + ALT instead.

SendMessage(hwndHot, HKM_SETRULES,

(WPARAM) HKCOMB_NONE | HKCOMB_S, // invalid key combinations

MAKELPARAM(HOTKEYF_ALT, 0)); // add ALT to invalid entries

// Set CTRL + ALT + A as the default hot key for this window.

// 0x41 is the virtual-key code for 'A'.

SendMessage(hwndHot, HKM_SETHOTKEY,

MAKEWORD(0x41, HOTKEYF_CONTROL | HOTKEYF_ALT), 0);

return hwndHot;

}

+$#A Retrieving and Setting a Hot Key

After the user has chosen a hot key, an application should retrieve the hot key from the hot-key control by using the HKM_GETHOTKEY message. This message retrieves a 16-bit value that contains the virtual-key code and modifier keys describing the hot key.

The following function retrieves a key combination from a hot-key control and then uses the WM_SETHOTKEY message to set a global hot key. Note that you cannot set a global hot key for a window that has the WS_CHILD window style.

// ProcessHotkey - retrieves the hot key from the hot-key control and

// sets it as the hot key for the application's main window.

// Returns TRUE if successful or FALSE otherwise.

// hwndHot - handle of the hot-key control

// hwndMain - handle of the main window

BOOL WINAPI ProcessHotkey(HWND hwndHot, HWND hwndMain)

{

WORD wHotkey;

UINT iSetResult;

// Retrieve the hot key (virtual-key code and modifiers).

wHotkey = SendMessage(hwndHot, HKM_GETHOTKEY, 0, 0);

// Use the result as wParam for WM_SETHOTKEY.

iSetResult = SendMessage(hwndMain, WM_SETHOTKEY, wHotkey, 0);

switch (iSetResult) {

case 2: // WM_SETHOTKEY succeeded

MessageBox(NULL, "Hot key previously assigned",

"Okay", MB_OK);

return TRUE;

case 1: // WM_SETHOTKEY succeeded

return TRUE;

case 0:

MessageBox(NULL, "Invalid window for hot key",

"Error", MB_OK);

return FALSE;

case -1:

MessageBox(NULL, "Invalid hot key",

"Error", MB_OK);

return FALSE;

default:

MessageBox(NULL, "Unknown error", "Error", MB_OK);

return FALSE;

}

}

+$#A Hot-Key Control Messages

The following messages are associated with hot-key controls.

HKM_GETHOTKEY
HKM_SETHOTKEY
HKM_SETRULES

Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub gratuit


Icons.rtf

+$#AK Icons

An icon is a picture that consists of a bitmapped image combined with a mask to create transparent areas in the picture. This overview describes creating, displaying, destroying, and duplicating icons.

+$#A About Icons

Windows uses icons throughout the user interface to represent objects such as files, printers, applications, and windows. The icon functions in the Microsoft® Win32® application programming interface (API) enable applications to create, load, display, arrange, and destroy icons.

The operating system provides a set of standard icons that are available for any application to use at any time. The Windows header files contain identifiers for the standard icons ⎯ the identifiers begin with the "IDI_" prefix.

Each standard icon has a corresponding default image associated with it. The user can replace the default image associated with any standard cursor at any time.

An application can retrieve the image for a cursor by using the GetIconInfo function, and can draw it by using the DrawIconEx function. To draw the default image for a cursor, specify the DI_COMPAT flag in the call to DrawIconEx. If you do not specify the DI_COMPAT flag, DrawIconEx draws the cursor using the image that the user specified.

Custom icons are designed for use in a particular application and can be any design the developer defines. Following are several custom icons.

{ewc msdncd, EWGraphic, group10802 0 /a "SDK_02.bmp"}

+$#A The Iconic Hot Spot

One of the pixels in an icon is designated as the hot spot, which is the point the system tracks and recognizes as the position of the icon. An icon's hot spot is typically the pixel located at the center of the icon. If you use the CreateIconIndirect function to create an icon, you can specify any pixel to be the hot spot.

+$#A Icon Creation

Standard icons are predefined in Win32, so it is not necessary to create them. To use a standard icon, an application can obtain its handle by using the LoadImage function. An icon handle is a unique value of the HICON type that identifies a standard or custom icon.

To create a custom icon for an application, developers typically use a graphics application and include the icon as a resource in the application's resource-definition file. At run time, an application can call LoadIcon or LoadImage to retrieve the handle of the icon. An icon resource contains data for several different display devices. LoadIcon and LoadImage automatically select the most appropriate data for the current display device.

An application can also create a custom icon at run time by using the CreateIconIndirect function, which creates an icon based on the contents of an ICONINFO structure. The GetIconInfo function fills the structure with the hot spot coordinates and information about the bitmask bitmap and color bitmap for the icon.

Applications should implement custom icons as resources and should use LoadIcon or LoadImage, rather than create the icon at run time. Using icon resources avoids device dependence, simplifies localization, and enables applications to share icon shapes.

The CreateIconFromResourceEx function enables an application to browse through the system's resources and create icons and cursors based on resource data. CreateIconFromResourceEx creates an icon based on binary resource data from other executable files or dynamic-link libraries (DLLs). An application must precede this function with calls to the LookupIconIdFromDirectoryEx function and several of the resource functions. LookupIconIdFromDirectoryEx returns the identifier of the most appropriate icon data for the current display device. For more information about the resource functions, see Resources.

+$#A Icon Display

Once an application has loaded an icon, it can use the DrawIcon or DrawIconEx functions to draw the icon.

When registering a window class, an application can assign one or two icons to the class, depending on the operating system version. A class icon is a picture that the system uses to represent a window of a particular class. For more information about class icons, see Window Classes.

+$#A Icon Destruction

When an application no longer needs an icon it created by using the CreateIconIndirect (or CreateIcon) function, it should destroy the icon. The DestroyIcon function destroys the icon handle and frees any memory used by the icon. Applications should use this function only for icons created with CreateIconIndirect; it is not necessary to destroy other icons.

+$#A Icon Duplication

The CopyIcon function copies an icon handle. This enables an application or DLL to get its own handle for an icon owned by another module. Then, if the other module is freed, the application that copied the icon will still be able to use the icon.

The CopyImage function creates a new icon based on the specified source icon. The new icon can be larger or smaller than the source icon.

For information about adding, removing, or replacing icon resources in executable (.EXE) files, see Resources.

+$#A Using Icons

• Creating an icon

• Displaying an icon

• Using icon functions to create a mousetrap

• Sharing icon resources

+$#A Creating an Icon

To use an icon, your application must get the handle of the icon. The following example shows how to create two different icon handles: one for the standard exclamation icon and one for a custom icon included as a resource in the application's resource-definition file.

HICON hIcon1; /* icon handle */

HICON hIcon2; /* icon handle */

/* Create a standard question icon. */

hIcon1 = LoadIcon(NULL, IDI_QUESTION);

/* Create a custom icon based on a resource. */

hIcon2 = LoadIcon(hinst, MAKEINTRESOURCE(460));

/* Create a custom icon at run time. */

An application should implement custom icons as resources and should use the LoadIcon or LoadImage function, rather than create the icons at run time. This approach avoids device dependence, simplifies localization, and enables applications to share icon bitmaps. However, the following example uses CreateIcon to create a custom icon at run time, based on bitmap bitmasks; it is included to illustrate how the system interprets icon bitmap bitmasks.

HICON hIcon3; /* icon handle */

/* Yang icon AND bitmask */

BYTE ANDmaskIcon[] = {0xFF, 0xFF, 0xFF, 0xFF, /* line 1 */

0xFF, 0xFF, 0xC3, 0xFF, /* line 2 */

0xFF, 0xFF, 0x00, 0xFF, /* line 3 */

0xFF, 0xFE, 0x00, 0x7F, /* line 4 */

0xFF, 0xFC, 0x00, 0x1F, /* line 5 */

0xFF, 0xF8, 0x00, 0x0F, /* line 6 */

0xFF, 0xF8, 0x00, 0x0F, /* line 7 */

0xFF, 0xF0, 0x00, 0x07, /* line 8 */

0xFF, 0xF0, 0x00, 0x03, /* line 9 */

0xFF, 0xE0, 0x00, 0x03, /* line 10 */

0xFF, 0xE0, 0x00, 0x01, /* line 11 */

0xFF, 0xE0, 0x00, 0x01, /* line 12 */

0xFF, 0xF0, 0x00, 0x01, /* line 13 */

0xFF, 0xF0, 0x00, 0x00, /* line 14 */

0xFF, 0xF8, 0x00, 0x00, /* line 15 */

0xFF, 0xFC, 0x00, 0x00, /* line 16 */

0xFF, 0xFF, 0x00, 0x00, /* line 17 */

0xFF, 0xFF, 0x80, 0x00, /* line 18 */

0xFF, 0xFF, 0xE0, 0x00, /* line 19 */

0xFF, 0xFF, 0xE0, 0x01, /* line 20 */

0xFF, 0xFF, 0xF0, 0x01, /* line 21 */

0xFF, 0xFF, 0xF0, 0x01, /* line 22 */

0xFF, 0xFF, 0xF0, 0x03, /* line 23 */

0xFF, 0xFF, 0xE0, 0x03, /* line 24 */

0xFF, 0xFF, 0xE0, 0x07, /* line 25 */

0xFF, 0xFF, 0xC0, 0x0F, /* line 26 */

0xFF, 0xFF, 0xC0, 0x0F, /* line 27 */

0xFF, 0xFF, 0x80, 0x1F, /* line 28 */

0xFF, 0xFF, 0x00, 0x7F, /* line 29 */

0xFF, 0xFC, 0x00, 0xFF, /* line 30 */

0xFF, 0xF8, 0x03, 0xFF, /* line 31 */

0xFF, 0xFC, 0x3F, 0xFF}; /* line 32 */

/* Yang icon XOR bitmask */

BYTE XORmaskIcon[] = {0x00, 0x00, 0x00, 0x00, /* line 1 */

0x00, 0x00, 0x00, 0x00, /* line 2 */

0x00, 0x00, 0x00, 0x00, /* line 3 */

0x00, 0x00, 0x00, 0x00, /* line 4 */

0x00, 0x00, 0x00, 0x00, /* line 5 */

0x00, 0x00, 0x00, 0x00, /* line 6 */

0x00, 0x00, 0x00, 0x00, /* line 7 */

0x00, 0x00, 0x38, 0x00, /* line 8 */

0x00, 0x00, 0x7C, 0x00, /* line 9 */

0x00, 0x00, 0x7C, 0x00, /* line 10 */

0x00, 0x00, 0x7C, 0x00, /* line 11 */

0x00, 0x00, 0x38, 0x00, /* line 12 */

0x00, 0x00, 0x00, 0x00, /* line 13 */

0x00, 0x00, 0x00, 0x00, /* line 14 */

0x00, 0x00, 0x00, 0x00, /* line 15 */

0x00, 0x00, 0x00, 0x00, /* line 16 */

0x00, 0x00, 0x00, 0x00, /* line 17 */

0x00, 0x00, 0x00, 0x00, /* line 18 */

0x00, 0x00, 0x00, 0x00, /* line 19 */

0x00, 0x00, 0x00, 0x00, /* line 20 */

0x00, 0x00, 0x00, 0x00, /* line 21 */

0x00, 0x00, 0x00, 0x00, /* line 22 */

0x00, 0x00, 0x00, 0x00, /* line 23 */

0x00, 0x00, 0x00, 0x00, /* line 24 */

0x00, 0x00, 0x00, 0x00, /* line 25 */

0x00, 0x00, 0x00, 0x00, /* line 26 */

0x00, 0x00, 0x00, 0x00, /* line 27 */

0x00, 0x00, 0x00, 0x00, /* line 28 */

0x00, 0x00, 0x00, 0x00, /* line 29 */

0x00, 0x00, 0x00, 0x00, /* line 30 */

0x00, 0x00, 0x00, 0x00, /* line 31 */

0x00, 0x00, 0x00, 0x00}; /* line 32 */

hIcon3 = CreateIcon(hinst, /* application instance */

32, /* icon width */

32, /* icon height */

1, /* number of XOR planes */

1, /* number of bits per pixel */

ANDmaskIcon, /* AND bitmask */

XORmaskIcon); /* XOR bitmask */

To create the icon, CreateIcon applies the following truth table to the AND and XOR bitmasks.

AND bitmask | XOR bitmask | Display
0 | 0 | Black
0 | 1 | White
1 | 0 | Screen
1 | 1 | Reverse screen

For information about full-color bitmaps, see Bitmaps and Colors.

Before closing, your application must use DestroyIcon to destroy any icon it created by using CreateIcon. It is not necessary to destroy icons created by other functions.

+$#A Displaying an Icon

Your application can load and create icons to display in the application's client area or child windows. The following example demonstrates how to draw an icon in the client area of the window whose display context (DC) is identified by the hdc parameter.

HICON hIcon1; /* icon handle */

HDC hdc; /* handle of display context */

DrawIcon(hdc, 10, 20, hIcon1);

Windows automatically displays the class icon(s) for a window. Your application can assign class icons while registering a window class. Your application can replace a class icon by using the SetClassLong function. This function changes the default window settings for all windows of a given class. The following example replaces a class icon with the icon whose resource identifier is 480.

HINSTANCE hinst; /* handle of current instance */

HWND hwnd; /* main window handle */

/* Change the icon for hwnd's window class. */

SetClassLong(hwnd, /* window handle */

GCL_HICON, /* changes icon */

(LONG) LoadIcon(hinst, MAKEINTRESOURCE(480))); /* new */

For more information about window classes, see Window Classes.

+$#A Using Icon Functions to Create a Mousetrap

The following example applies to operating system versions earlier than 4.0. It uses IsIconic, LoadIcon, and several cursor and timer functions to create a simple mousetrap. The example uses the cursor and timer functions to monitor the location of the cursor at 10-second intervals. If the cursor location has not changed in the last 10 seconds and the application's main window is minimized, the application changes the cursor shape and moves the cursor to the icon.

HICON hIcon1; /* icon handle */

POINT ptOld; /* previous cursor location */

HCURSOR hCurs1; /* cursor handle */

HINSTANCE hinstance; /* handle of current instance */

hCurs1 = LoadCursor(hinstance, MAKEINTRESOURCE(220));

.

. /* Fill in the window class structure. */

.

WNDCLASS wc;

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(470));

wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(240));

.

. /*

. * Register the window class and perform

. * other application initialization.

. */

.

/* Set a timer for the mousetrap. */

GetCursorPos(&ptOld);

SetTimer(hwnd, IDT_CURSOR, 10000, (TIMERPROC) NULL);

LONG APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

HDC hdc; /* device context handle */

POINT pt; /* current cursor location */

RECT rc; /* iconized window location */

switch (message) {

.

. /* Process other messages here. */

.

case WM_TIMER:

/*

* If the window is minimized, compare the current

* cursor position with the one from 10 seconds

* earlier. If the cursor position has not changed,

* move the cursor to the icon.

*/

if (IsIconic(hwnd)) {

GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {

GetWindowRect(hwnd, &rc);

SetCursorPos(rc.left, rc.top);

}

else {

ptOld.x = pt.x;

ptOld.y = pt.y;

}

}

return 0;

case WM_SETCURSOR:

/*

* If the window is minimized, draw hCurs1.

* If the window is not minimized, draw the default

* cursor (class cursor).

*/

if (IsIconic(hwnd)) {

SetCursor(hCurs1);

break;

}

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_CURSOR);

PostQuitMessage(0);

break;

.

. /* Process other messages here. */

.

} /* ends switch (message) */

An example for a similar mousetrap is included in Cursors. That example uses CreateIcon to create the class icon.

+$#A Sharing Icon Resources

The following code uses the functions CreateIconFromResourceEx, DrawIcon, and LookupIconIdFromDirectoryEx, and several of the resource functions, to create an icon handle based on icon data from another executable file. Then, it displays the icon in a window.

HICON hIcon1; /* icon handle */

HINSTANCE hExe; /* handle to loaded .EXE file */

HRSRC hResource; /* handle for FindResource */

HRSRC hMem; /* handle for LoadResource */

BYTE *lpResource; /* address of resource data */

int nID; /* ID of resource that best

fits current screen */

HDC hdc; /* handle of display context */

/* Load the file from which to copy the icon. */

hExe = LoadLibrary("myapp.exe");

/* Find the icon directory whose identifier is 440. */

hResource = FindResource(hExe,

MAKEINTRESOURCE(440),

RT_GROUP_ICON);

/* Load and lock the icon directory. */

hMem = LoadResource(hExe, hResource);

lpResource = LockResource(hMem);

/*

* Get the identifier of the icon that is most appropriate

* for the video display.

*/

nID = LookupIconIdFromDirectoryEx((PBYTE) lpResource, TRUE,

CXICON, CYICON, LR_DEFAULTCOLOR);

/* Find the bits for the nID icon. */

hResource = FindResource(hExe,

MAKEINTRESOURCE(nID),

MAKEINTRESOURCE(RT_ICON));

/* Load and lock the icon. */

hMem = LoadResource(hExe, hResource);

lpResource = LockResource(hMem);

/* Create a handle to the icon. */

hIcon1 = CreateIconFromResourceEx((PBYTE) lpResource,

SizeofResource(hExe, hResource), TRUE, 0x00030000,

CXICON, CYICON, LR_DEFAULTCOLOR);

/* Draw the icon in the client area. */

DrawIcon(hdc, 10, 20, hIcon1);

+$#AK Icon Reference

The following functions and messages are associated with icons.

+$#A Icon Functions

Following are the functions used with icons.

ArrangeIconicWindows
CopyIcon
CreateIcon
CreateIconFromResource
CreateIconIndirect
DestroyIcon
DrawIcon
DrawIconEx
GetIconInfo
IsIconic
LoadIcon
LookupIconIdFromDirectory
OpenIcon

+$#A Icon Messages

Following are the messages used with icons.

WM_ERASEBKGND
WM_ICONERASEBKGND
WM_PAINTICON

Créé avec HelpNDoc Personal Edition: Créer des aides HTML, DOC, PDF et des manuels depuis une même source


Image Color Matching.rtf

+$#AK Image Color Matching

Image color matching provides accurate and consistent reproductions of color images on all output devices. This overview describes image color matching and explains how to use the related functions. For general information about colors and how to specify colors when drawing, see Colors.

+$#A About Image Color Matching

Despite differences in imaging technologies and color capabilities between devices, image color matching ensures that a color image will look the same or as close as possible to the same when output to screen, paper, film, and other media. Applications typically use image color matching to let users create color images on the screen that can then be reproduced exactly on a printer or on other screens.

+$#A Logical Colors, Gamuts, and Gamut Matching

By default, Windows disables image color matching to ensure that existing applications continue to draw color images as originally intended. You can enable image color matching for a device context by using the SetICMMode function. You must enable image color matching for each device context you intend to use for output.

Once you enable image color matching, you can create a color space and select it into the device contexts of the devices you wish to use. The color space defines the set of logical colors that you may use to draw. For each device, the system or the device driver transforms the logical colors to device (or physical) colors in a way that ensures consistency across devices.

To use a color from the color space, you specify a logical color value. The format of the value depends on the color space type. In some color spaces, this is similar to RGB (red, green, blue) color value described in previous Windows documentation, but the red, green, and blue components of logical color values are defined in terms of independent colormetric measure rather than arbitrary color units imposed by a device driver. In other cases, you may use color values that actually contain CMYK (cyan, magenta, yellow, black) values.

Not all devices support all colors. You can determine which colors are supported by a given device by checking its gamut, the set of colors it can produce. Different devices have different gamuts. For example, the screen can display cyans that no printer can produce; likewise, there are some reds on a page that never appear on a display.

An important element of image color matching is gamut matching, the process of finding color matches in the gamuts of different devices. This can range from the simple (such as truncating to the surface of the printer gamut), to the sophisticated (such as moving all colors a bit to maintain contrasts). Matching is not simply the process of ensuring that each pixel is faithfully reproduced with a given color. Instead, image color matching works within the capabilities of the given output device to produce an image in which the colors have the same relation to one another as they do when produced on other devices.

You can determine whether given colors are within the gamut of a device by using the CheckColorsInGamut function. This is useful for determining whether the screen has a larger gamut than the current printer. In such cases, you can notify the user of which screen colors won't print. You can preview the colors of a print job on the screen by using the ColorMatchToTarget function. The function first uses the current color space and gamut-matching method of a given device context, such as for a printer, to convert the colors in an image. It then converts the converted image to display on the preview device.

Image color matching applies to any drawing operation in which colors are given as color values. It affects colors that you specify directly, such as when setting the color of individual pixels using the SetPixel function, or indirectly, when you set the colors in pens, brushes, fonts and other graphics objects. Image color matching does not apply to device-dependent bitmaps, such as those created by the CreateBitmap and CreateCompatibleBitmap functions.

+$#A Color Spaces

Image color matching uses color spaces to define the set of colors available for a device. To each device context, the system assigns a default color space whose attributes, such as color space type, gamut-matching method and color endpoints, fully define how the system transforms color values for the device. You can create your own color spaces and replace the default color space for a device context by using the CreateColorSpace and SetColorSpace functions. These functions let you explicitly set the format of the color values you use in your application and how the device interprets those values and generates colors.

To create a color space, you set the members of a LOGCOLORSPACE structure and pass the structure to the CreateColorSpace function. You can either set the members to the values you want or specify the filename of a color profile that contains the characterization of the device. For more information, see Color Profiles.

Every color space must have a color space type and a gamut matching method. Gamut matching specifies whether colors in a given image are transformed into an exact match or are modified to make best use of the gamut of the given device, and is described in Gamut Matching Method.

The color space types can be device RGB (red, green, blue), device CMYK (cyan, magenta, yellow, black), or calibrated RGB. Device RGB, the default color space for device contexts, consists of the arbitrary set of colors for the specified output device. Logical color values in this space are given as three 8-bit color values (in a 32-bit value) and are sent to the device driver without transformation. Device CMYK is similar to device RGB except that logical color values are given as four 8-bit values. You use this color space for devices, typically printers, that use CMYK color values. For example, you might use it to carry out color separation and pass color values directly to a CMYK printer.

Calibrated RGB consists of colors in the set defined by three color endpoints. The color endpoints are given as three 32-bit values and are interpreted as defined by the 1931 CIE (Commission Internationale de L'Eclairage) XYZ standard.

After you have created a color space, you can select it into a device context by using the SetColorSpace function. You can delete a color space if you no longer need it by using the DeleteColorSpace function.

You can retrieve the color space for a specified device context by using the GetColorSpace function. This is useful if you want to get the logical definition or color profile file associated with the color space. You can use the GetLogColorSpace function to copy this information to a LOGCOLORSPACE structure.

+$#AK Gamut Matching Method

The gamut matching method determines how colors on a given device are "matched" with colors on another device. The type of matching needed depends on the image. There are some images for which users rarely want image color matching. For example, users like saturated, undithered colors in bar charts and highlights used in presentations ⎯ the device colors are far superior to dithered patterns of primary colors.

Gamut-matching methods include the following:

• Business graphics

Preserves saturation (prevents dithering) if the requested color is not available. Useful for business charts and many computer-generated presentations.

• Colormetric

Produces exact colormetric matches. Dithers primary colors if necessary. Colormetric matching is important when named colors are wanted.

• Photographic

Produces the best perceptual match between devices. Takes advantage of the entire gamut of the device to maximize the number of distinct colors presented. This is best for photographic images.

You set the gamut-matching method for a device context by setting the lcsIntent member in the LOGCOLORSPACE structure when creating the color space. The matching choices for the display and printer are global settings; they apply to all applications at all times.

In some cases, a user may want to use different matching methods in the same document. For example, the user may want to use the colormetric method to draw the company logo but the business graphics method for bar charts. You can mix gamut-matching methods on the same page of output by creating appropriate color spaces and using SetColorSpace to switch between the spaces as needed.

+$#A Color Profiles

A color profile is a file that contains information that defines the color capabilities of a device. A color profile for a display monitor, for example, defines the chromaticities of the phosphors for that monitor; one for a printer defines the chromaticities of the inks on a given type of paper. The system uses this information when mapping logical colors to the physical colors available on the device.

Color profiles exist for display monitors, printers, and scanners. By default, the system uses the color profile that best matches a given device, based on information in the profile and provided by the device driver. If a color profile is not present for a given device, the system uses a default profile for that device.

In some cases, it may be important to let the user choose the color profile to use for a given device. This can occur when the device configuration has changed in a way that the system cannot detect. You can enumerate all color profiles for a device by using the EnumICMProfiles function. This identifies each color profile by passing its filename to an EnumICMProfilesProc callback function that you supply. You can set the color profile for the device by using the SetICMProfile function. You can determine the current color profile for the device by using the GetICMProfile function.

A color profile is available only if it is listed in the registry. You can install or remove color profiles in the registry by using the UpdateICMRegKey function. You can also use this function to query for printer profiles that match specified device settings, or to associate a color profile with a given color-matching DLL and output device.

+$#AK Device-Independent Bitmaps and Enhanced Metafiles

To provide for image color matching, color characterization is included in device-independent bitmaps (DIBs) and enhanced metafiles. In DIBs, the color characterization is placed in the DIB header. In enhanced metafiles, the characterization is in metafile records. Color characterization is not available for compatible bitmaps and standard (16-bit) metafiles.

You set the color characterization for a DIB by setting the appropriate members in the BITMAPV4HEADER structure for the DIB. This structure is identical to the BITMAPINFOHEADER structure except that it includes members that define the color space type, color endpoints, and other related values. Some operating systems support the device CMYK color space type in DIBs but only for 32-bit DIBs. In such cases, the output device must also accept this format. If a DIB does not contain color characterization information, the system uses a default characterization.

You set color characterization for an enhanced metafile by using the CreateColorSpace and SetColorSpace functions when recording the metafile. The system records information about the color spaces and carries out color matching with the intended output device when the metafile is played. The system also records the SetICMMode and DeleteColorSpace functions, but no other image color matching functions can be used with enhanced metafiles. If you specify a color profile in the LOGCOLORSPACE structure when recording an enhanced metafile, the system records the filename of the color profile but not the content of the profile. This is similar to the way fonts are handled in enhanced metafiles.

+$#A Gamma Correction

Some display adapters support downloadable gamma correction tables. These tables let you control the colors generated by the digital-to-analog converter (DAC) by mapping specific RGB values to DAC values. You can set the gamma ramp for any 24-bit display adapters that provide hardware gamma correction by using the SetDeviceGammaRamp function. The hardware gamma ramp is a global property that affects all running applications and is not changed on a per-device context basis. You can retrieve the current gamma ramp by using the GetDeviceGammaRamp function.

+$#A Image Color Matching Reference

This section provides a listing of the image color matching functions and structures.

+$#A Image Color Matching Functions

This following are the image color matching functions.

CheckColorsInGamut
ColorMatchToTarget
CreateColorSpace
DeleteColorSpace
EnumICMProfiles
EnumICMProfilesProc
GetColorSpace
GetDeviceGammaRamp
GetICMProfile
GetLogColorSpace
SetColorSpace
SetDeviceGammaRamp
SetICMMode
SetICMProfile
UpdateICMRegKey

+$#A Image Color Matching Macros

This following are the image color matching macros.

CMYK
GetCValue
GetKValue
GetMValue
GetYValue

+$#A Image Color Matching Structures

The following structures are used with the image color matching functions.

BITMAPV4HEADER
CIEXYZ
CIEXYZTRIPLE
LOGCOLORSPACE

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


Image Lists.rtf

+$#AK Image Lists

An image list is a collection of same-sized images, each of which can be referred to by its index. Image lists are used to efficiently manage large sets of icons or bitmaps. All images in an image list are contained in a single, wide bitmap in screen device format. An image list may also include a monochrome bitmap that contains masks used to draw images transparently (icon style).

+$#A About Image Lists

The Microsoft® Win32® application programming interface (API) provides image list functions, which enable you to draw images, create and destroy image lists, add and remove images, replace images, merge images, and drag images.

To use the image list functions, you must include the common control header file in your source code files and link with the common control export library. In addition, before calling any image list function, you must use the InitCommonControls function to ensure that the common control dynamic-link library (DLL) is loaded.

+$#A Types

There are two types of image lists: nonmasked and masked. A nonmasked image list consists of a color bitmap that contains one or more images. A masked image list consists of two bitmaps of equal size. The first is a color bitmap that contains the images, and the second is a monochrome bitmap that contains a series of masks ⎯ one for each image in the first bitmap.

When a nonmasked image is drawn, it is simply copied into the target device context; that is, it is drawn over the existing background color of the device context. When a masked image is drawn, the bits of the image are combined with the bits of the mask, typically producing transparent areas in the bitmap where the background color of the target device context shows through. There are several drawing styles that you can specify when drawing a masked image. For example, you can specify that the image be dithered to indicate a selected object. For more information about drawing images, see Drawing Images.

+$#A Image List Creation

You create an image list by calling the ImageList_Create function. The parameters include the type of image list to create, the dimensions of each image, and the number of images you intend to add to the list. For a nonmasked image list, the function creates a single bitmap large enough to hold the specified number of images of the given dimensions. Then it creates a screen-compatible device context and selects the bitmap into it. For a masked image list, the function creates two bitmaps and two screen-compatible device contexts. It selects the image bitmap into one device context and the mask bitmap into the other.

The initial size of an image list is determined by the size values you specify in ImageList_Create. If you attempt to add more images than you initially specified, the image list automatically grows to accommodate the additional images. In ImageList_Create, you specify the amount of images by which the image list can grow.

If ImageList_Create succeeds, it returns a handle of the HIMAGELIST type. You use this handle in other image list functions to access the image list and manage the images. You can add and remove images, copy images from one image list to another, and merge images from two different image lists. When you no longer need an image list, you can destroy it by specifying its handle in a call to the ImageList_Destroy function.

+$#A Adding and Removing Images

You can add bitmapped images, icons, or cursors to an image list. You add bitmapped images by specifying the handles of two bitmaps in a call to the ImageList_Add function. The first bitmap contains one or more images to add to the image bitmap, and the second bitmap contains the masks to add to the mask bitmap. For nonmasked image lists, the second bitmap handle is ignored; it can be set to NULL.

The ImageList_AddMasked function also adds bitmapped images to a masked image list. This function is similar to ImageList_Add, except that you do not specify a mask bitmap. Instead, you specify a color that the system combines with the image bitmap to automatically generate the masks. Each pixel of the specified color in the image bitmap is changed to black, and the corresponding bit in the mask is set to one. As a result, any pixel in the image that matches the specified color is transparent when the image is drawn.

The ImageList_AddIcon function adds an icon or cursor to an image list. If the image list is masked, ImageList_AddIcon adds the mask provided with the icon or cursor to the mask bitmap. If the image list is nonmasked, the mask for the icon or cursor is not used when drawing the image.

You can create an icon based on an image and mask in an image list by using the ImageList_GetIcon function. The function returns the handle of the new icon.

ImageList_Add, ImageList_AddMasked, and ImageList_AddIcon assign an index to each image as it is added to an image list. The indexes are zero-based; that is, the first image in the list has an index of zero, the next has an index of one, and so on. After adding a single image, the functions return the index of the image. When more than one image is added at a time, the functions return the index of the first image.

The ImageList_Remove function removes an image from an image list.

+$#A Replacing and Merging Images

The ImageList_Replace and ImageList_ReplaceIcon functions replace an image in an image list with a new image. ImageList_Replace replaces an image with a bitmapped image and mask, and ImageList_ReplaceIcon replaces an image with an icon or cursor.

The ImageList_Merge function merges two images, storing the new image in a new image list. The new image is created by drawing the second image transparently over the first. The mask for the new image is the result of performing a logical OR operation on the bits of the masks for the two existing images.

+$#A Drawing Images

To draw an image, you use the ImageList_Draw or ImageList_DrawEx function. You specify the handle of an image list, the index of the image to draw, the handle of the destination device context, a location within the device context, and one or more drawing styles.

When you specify the ILD_TRANSPARENT style, ImageList_Draw or ImageList_DrawEx uses a two-step process to draw a masked image. First, it performs a logical AND operation on the bits of the image and the bits of the mask. Then it performs a logical XOR operation on the results of the first operation and the background bits of the destination device context. This process creates transparent areas in the resulting image; that is, each white bit in the mask causes the corresponding bit in the resulting image to be transparent.

Before drawing a masked image on a solid color background, you should use the ImageList_SetBkColor function to set the background color of the image list to the same color as the destination. Setting the color eliminates the need to create transparent areas in the image and enables ImageList_Draw or ImageList_DrawEx to simply copy the image to the destination device context, resulting in a significant increase in performance. To draw the image, specify the ILD_NORMAL style in a call to ImageList_Draw or ImageList_DrawEx.

You can set the background color for a masked image list at any time so that it draws correctly on any solid background. Setting the background color to CLR_NONE causes images to be drawn transparently by default. To retrieve the background color of an image list, use the ImageList_GetBkColor function.

The ILD_BLEND25 and ILD_BLEND50 styles dither the image with the system highlight color. These styles are useful if you use a masked image to represent an object that the user can select. For example, you can use the ILD_BLEND50 style to draw the image when the user selects it.

A nonmasked image is copied to the destination device context using the SRCCOPY raster operation. The colors in the image appear the same regardless of the background color of the device context. The drawing styles specified in ImageList_Draw or ImageList_DrawEx also have no effect on the appearance of a nonmasked image.

+$#A Overlay Mask

Every image list includes a list of images to use as overlay masks. An overlay mask is an image drawn transparently over another image. Any image can be used as an overlay mask. You can specify up to four overlay masks per image list.

You add the index of an image to the list of overlay masks by using the ImageList_SetOverlayImage function, specifying the handle of the image list, the index of an image, and the index of an overlay mask. Note that the indices for the overlay masks are one-based rather than zero-based.

You draw an overlay mask over an image using a single call to the ImageList_Draw or ImageList_DrawEx function. The parameters include the index of the image to draw and the index of an overlay mask. You must use the INDEXTOOVERLAYMASK macro to specify the index of the overlay mask.

+$#A Dragging Images

The Win32 API includes functions for dragging an image on the screen. The dragging functions move an image smoothly, in color, and without any flashing of the cursor. Both masked and unmasked images can be dragged.

The ImageList_BeginDrag function begins a drag operation. The parameters include the handle of the image list, the index of the image to drag, and the location of the hot spot within the image. The hot spot is a single pixel that the dragging functions recognize as the exact screen location of the image. Typically, an application sets the hot spot so that it coincides with the hot spot of the mouse cursor. The ImageList_DragMove function moves the image to a new location.

The ImageList_DragEnter function sets the initial position of the drag image within a window and draws the image at the position. The parameters include the handle of the window in which to draw the image and the coordinates of the initial position within the window. The coordinates are relative to the window's upper-left corner, not the client area. The same is true for all of the image dragging functions that take coordinates as parameters. This means you must compensate for the widths of window elements, such as the border, title bar, and menu bar, when specifying the coordinates. If you specify a NULL window handle when calling ImageList_DragEnter, the dragging functions draw the image in the device context associated with the desktop window, and the coordinates are relative to the upper-left corner of the screen.

ImageList_DragEnter locks all other updates to the given window during the drag operation. If you need to do any drawing during a drag operation, such as highlighting the target of a drag and drop operation, you can temporarily hide the dragged image by using the ImageList_DragLeave function. Another method is to use the GetDCEx function with the DCX_LOCKWINDOWUPDATE value to retrieve a device context that allows you to draw. You must be careful, however, not to obliterate the dragged image.

The ImageList_SetDragCursorImage creates a new drag image by combining the given image (typically a mouse cursor image) with the current drag image. Because the dragging functions use the new image during a drag operation, you should use the ShowCursor function to hide the actual mouse cursor after calling ImageList_SetDragCursorImage. Otherwise, the system may appear to have two mouse cursors for the duration of the drag operation.

When an application calls ImageList_BeginDrag, the system creates an temporary, internal image list can copies the specified drag image to the internal list. You can retrieves the handle of the temporary drag image list by using the ImageList_GetDragImage function. The function also retrieves the current drag position, and the offset of the drag image relative to the drag position.

+$#A Image Information

The Win32 API includes a number of functions that retrieve information from an image list. The ImageList_GetImageInfo function fills an IMAGEINFO structure with information about a single image, including the handles of the image and mask bitmaps, the number of color planes and bits per pixel, and the bounding rectangle of the image within the image bitmap. You can use this information to directly manipulate the bitmaps for the image.

The ImageList_GetImageCount function retrieves the number of images in an image list.

+$#A Using Image Lists

• Creating an image list.

• Dragging an image.

+$#A Creating an Image List

To create an image list, use the ImageList_Create function, specifying the type of image list to create (unmasked or masked); the width and height, in pixels, of the images; the number of images you intend to add to the image list; and the amount of images by which the list can grow when it is resized to accommodate additional images. The common control DLL contains the executable code for the image list functions. You must ensure that the library is loaded by using the InitCommonControls function before making any calls to image list functions.

The following example creates a masked image list and uses the ImageList_AddIcon function to add two icons to the list.

// AddIconsToImageList - creates a masked image list and adds some

// icons to it.

// Returns the handle of the new image list.

// hinst - handle of the application instance

//

// Global variables and constants

// g_nBird and g_nTree - indexes of the images

// CX_ICON and CY_ICON - width and height of the icon

// NUM_ICONS - number of icons to add to the image list

extern int g_nBird, g_nTree;

#define CX_ICON 32

#define CY_ICON 32

#define NUM_ICONS 3

HIMAGELIST AddIconsToImageList(HINSTANCE hinst)

{

HIMAGELIST himlIcons; // handle of new image list

HICON hicon; // handle of icon

// Ensure that the common control DLL is loaded.

InitCommonControls();

// Create a masked image list large enough to hold the icons.

himlIcons = ImageList_Create(CX_ICON, CY_ICON, TRUE, NUM_ICONS, 0);

// Load the icon resources, and add the icons to the image list.

hicon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BIRD));

g_nBird = ImageList_AddIcon(himlIcons, hicon);

hicon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_TREE));

g_nTree = ImageList_AddIcon(himlIcons, hicon);

return himlIcons;

}

+$#A Dragging an Image

Dragging an image involves calls to the ImageList_BeginDrag, ImageList_DragMove, and ImageList_EndDrag functions. ImageList_BeginDrag begins a drag operation by combining an image with the current mouse cursor, setting a hot spot in the image, and drawing the image in its initial position. The function also prevents the system from updating any other parts of the display. ImageList_DragMove drags the image to a new location. ImageList_EndDrag ends a drag operation, permitting the system to update any part of the display.

The remainder of this section provides an example using four functions that demonstrates how to drag an image. The first function shows how to draw an image in a window's client area, and subsequent functions show how to drag the image.

+$#A Drawing the Image

The following function draws an image and saves the client coordinates of the image's bounding rectangle. A subsequent function uses the bounding rectangle to determine whether the user has clicked the image.

// DrawTheImage - draws an image transparently and saves

// the bounding rectangle of the drawn image

// Returns TRUE if successful or FALSE otherwise.

// hwnd - handle of the window in which to draw the image

// himl - handle of the image list that contains the image

// cx and cy - client coordinates for the upper-left corner of the image

//

// Global variables and constants

// g_nImage - index of the image to draw

// g_rcImage - bounding rectangle of the image

// CX_IMAGE and CY_IMAGE - width and height of the image

extern int g_nImage;

extern RECT g_rcImage;

#define CX_IMAGE 32

#define CY_IMAGE 32

BOOL DrawTheImage(HWND hwnd, HIMAGELIST himl, int cx, int cy)

{

HDC hdc;

if ((hdc = GetDC(hwnd)) == NULL)

return FALSE;

if (!ImageList_Draw(himl, g_nImage, hdc, cx, cy, ILD_TRANSPARENT))

return FALSE;

ReleaseDC(hwnd, hdc);

SetRect(&g_rcImage, cx, cy, CX_IMAGE + cx, CY_IMAGE + cy);

return TRUE;

}

+$#A Beginning the Drag Operation

The following function is intended to be called in response to a mouse button-down message, such as WM_LBUTTONDOWN. The function determines whether the user has clicked within the bounding rectangle of the image. If the user has clicked, the function captures the mouse input, erases the image from the client area, and calculates the position for the hot spot within the image. The function sets the hot spot to coincide with the hot spot of the mouse cursor. Then the function begins the drag operation by calling ImageList_BeginDrag.

// StartDragging - begins dragging a bitmap.

// Returns TRUE if successful or FALSE otherwise.

// hwnd - handle of the window in which the bitmap is dragged

// ptCur - coordinates of the cursor

// himl - handle of the image list

//

// Global variables

// g_rcImage - bounding rectangle of the image to drag

// g_nImage - index of the image

// g_ptHotSpot - location of the image's hot spot

// g_cxBorder and g_cyBorder - width and height of border

// g_cyCaption and g_cyMenu - height of title bar and menu bar

extern RECT g_rcImage;

extern int g_nImage;

extern POINT g_ptHotSpot;

BOOL StartDragging(HWND hwnd, POINT ptCur, HIMAGELIST himl)

{

// Return if the cursor is not in the bounding rectangle of

// the image.

if (!PtInRect(&g_rcImage, ptCur))

return FALSE;

// Capture the mouse input.

SetCapture(hwnd);

// Erase the image from the client area.

InvalidateRect(hwnd, &g_rcImage, TRUE);

UpdateWindow(hwnd);

// Calculate the location of the hot spot, and save it.

g_ptHotSpot.x = ptCur.x - g_rcImage.left;

g_ptHotSpot.y = ptCur.y - g_rcImage.top;

// Begin the drag operation.

if (!ImageList_BeginDrag(himl, g_nImage,

g_ptHotSpot.x, g_ptHotSpot.y))

return FALSE;

// Set the initial location of the image, and make it visible.

// Because the ImageList_DragEnter function expects coordinates to

// be relative to the upper-left corner of the given window, the

// width of the border, title bar, and menu bar need to be taken

// into account.

ImageList_DragEnter(hwnd, ptCur.x + g_cxBorder,

ptCur.y + g_cyBorder + g_cyCaption + g_cyMenu);

g_fDragging = TRUE;

return TRUE;

}

+$#A Moving the Image

The following function, which drags the image to a new location, is intended to be called in response to the WM_MOUSEMOVE message.

// MoveTheImage - drags an image to the specified coordinates.

// Returns TRUE if successful or FALSE otherwise.

// ptCur - new coordinates for the image

BOOL MoveTheImage(POINT ptCur)

{

if (!ImageList_DragMove(ptCur.x, ptCur.y))

return FALSE;

return TRUE;

}

+$#A Ending the Drag Operation

The following function ends the drag operation and draws the image in its final location. It also releases the mouse capture.

// StopDragging - ends a drag operation and draws the image

// at its final location.

// Returns TRUE if successful or FALSE otherwise.

// hwnd - handle of the window in which the bitmap is dragged

// himl - handle of the image list

// ptCur - coordinates of the cursor

//

// Global variable

// g_ptHotSpot - location of the image's hot spot

extern POINT g_ptHotSpot;

BOOL StopDragging(HWND hwnd, HIMAGELIST himl, POINT ptCur)

{

ImageList_EndDrag();

ImageList_DragLeave(hwnd)

g_fDragging = FALSE;

DrawTheImage(hwnd, himl, ptCur.x - g_ptHotSpot.x,

ptCur.y - g_ptHotSpot.y);

ReleaseCapture();

return TRUE;

}

+$#A Image List Reference

The following functions, macros, and structures are associated with image lists. These elements can be grouped as follows.

Creating Image Lists

ImageList_Create
ImageList_Destroy
ImageList_LoadBitmap
ImageList_LoadImage

Adding and Removing Images

ImageList_Add
ImageList_AddIcon
ImageList_AddMasked
ImageList_Remove

Replacing and Merging Images

ImageList_Replace
ImageList_ReplaceIcon
ImageList_Merge

Drawing Images

ImageList_Draw
ImageList_DrawEx
ImageList_ExtractIcon
ImageList_GetBkColor
ImageList_GetIcon
ImageList_SetBkColor
INDEXTOOVERLAYMASK

Dragging Images

ImageList_BeginDrag
ImageList_DragEnter
ImageList_DragLeave
ImageList_DragMove
ImageList_DragShowNolock
ImageList_EndDrag
ImageList_GetDragImage
ImageList_SetDragCursorImage
ImageList_SetOverlayImage

Image Information

ImageList_GetIconSize
ImageList_GetImageCount
ImageList_GetImageInfo
ImageList_SetIconSize
IMAGEINFO

Storing Image Lists

ImageList_Read
ImageList_Write

+$#A Image List Functions

The following functions are used with image lists.

ImageList_Add
ImageList_AddMasked
ImageList_BeginDrag
ImageList_Create
ImageList_Destroy
ImageList_DragEnter
ImageList_DragLeave
ImageList_DragMove
ImageList_DragShowNolock
ImageList_Draw
ImageList_DrawEx
ImageList_EndDrag
ImageList_ExtractIcon
ImageList_GetBkColor
ImageList_GetDragImage
ImageList_GetIcon
ImageList_GetIconSize
ImageList_GetImageCount
ImageList_GetImageInfo
ImageList_LoadBitmap
ImageList_LoadImage
ImageList_Merge
ImageList_Read
ImageList_Remove
ImageList_Replace
ImageList_ReplaceIcon
ImageList_SetBkColor
ImageList_SetDragCursorImage
ImageList_SetIconSize
ImageList_SetOverlayImage
ImageList_Write

+$#A Image List Macro

The following macros are used with image lists.

ImageList_AddIcon
INDEXTOOVERLAYMASK

+$#A Image List Structure

The following structure is used with image lists.

IMAGEINFO

Créé avec HelpNDoc Personal Edition: Générateur d'aide complet


Input Method Editor.rtf

+$#AK Input Method Editor

The input method editor (IME), available in Asian versions of the Microsoft Windows operating system, helps minimize the effort needed by users to enter text containing characters from Unicode and double-byte character sets.

This overview describes the IME and explains how to use the input method manager functions to create and manage IME windows.

+$#A About Input Method Editor

The input method editor relieves users of the need to remember all possible character values. Instead, the IME monitors the user's keystrokes, anticipates the characters the user may want, and presents a list of candidate characters from which to choose.

By default, the IME provides an IME window through which users enter keystrokes and view and select candidates. Applications developed for the Win32 application programming interface (API) can use the input method manager (IMM) functions and messages to create and manage their own IME windows, providing a custom interface while using the conversion capabilities of the IME.

+$#A Status, Composition, and Candidates Windows

The status, composition, and candidates windows form the user interface for the IME. The status window indicates that the IME is open and provides the user the means to set the conversion modes. The composition window appears when the user enters text and, depending on the conversion mode, either displays the text as entered or displays converted text. The candidates window appears in conjunction with the composition window. It contains a list of "candidates" (alternative characters) for the selected character or characters in the composition window. The user can scroll through the candidates list and select the desired character(s), then return to the composition window. The user can compose the desired text in this way until the composition string is finalized and the window is closed. The IME sends the composed characters to the application in the form of WM_IME_CHAR or WM_IME_COMPOSITION/GCS_RESULT messages. If the application does not process these messages, the DefWindowProc function translates them into one or more WM_CHAR messages.

By default, the system automatically creates and manages status, composition, and candidates windows for all windows that require text input. For many applications, this default processing is sufficient. These applications rely entirely on the system for IME support and are said to be IME-unaware because they are unaware of the many tasks the system carries out to manage the IME windows.

An IME-aware application, on the other hand, participates in the creation and management of IME windows. Such applications control the operation, position, and appearance of the default windows by sending messages to and by intercepting and processing messages intended for these windows. In some cases, applications create their own IME windows and provide complete processing for their custom status, composition and candidates windows.

+$#A IME Window Class

The "IME" window class is a predefined system global class that defines the appearance and behavior of the standard IME windows. The class is similar to common control classes in that you create a window of this class by using the CreateWindowEx function. Like static controls, an IME window does not respond to user input by itself. Instead, it notifies the IME of user input actions and processes control messages sent to it by the IME or applications to carry out a response to the user action.

IME-aware applications sometimes create their own IME windows using the IME class. This allows the application to take advantage of the default processing of the IME window while having control of the positioning of the window.

+$#A IME Messages

The system sends IME window messages to the window procedure of the application when certain event occur that affect the IME windows. For example, the system sends the WM_IME_SETCONTEXT message to the application when a window is activated. IME-unaware application pass these messages to the DefWindowProc function which sends them to the corresponding default IME window. IME-aware applications either process these messages or forward them the their own IME windows.

You can direct an IME window to carry out an action, such as change the position of composition window, by using the WM_IME_CONTROL message. The IME notifies the application about changes to the composition string by using the WM_IME_COMPOSITION message and about general changes to the status of the IME windows by sending the WM_IME_NOTIFY message.

+$#A Input Context

An input context is an internal structure, maintained by the IME, that contains information about the status of the IME and is used by IME windows. By default, the system creates and assigns an input context to each thread. Within the thread, this default input context is a shared resource and is associated with each newly created window.

To retrieve or set information in the IME, an application must first retrieve a handle to the input context associated with a given window. You retrieve the handle by using the ImmGetContext function. You can use the retrieved handle in subsequent calls to the input method manager functions to retrieve and set IME values, such as the composition window style, the composition style, and the status window position. Once you have finished using the context, you must release it using the ImmReleaseContext function.

Because the default input context is a shared resource, any changes you make to it apply to all windows in the thread. However, you can override this default behavior by creating and associating your own input context to one or more window of the thread. The changes you make to your own input context apply only to the windows with which it is associated.

You can create an input context by using the ImmCreateContext function. You assign the context to a window by using the ImmAssociateContext function. This function returns the handle of the previously associated input context. If you have not associated an input context with the window before, the returned handle is for the default input context. You should save this handle and later reassociate with the window if you no longer want to use your own input context.

Once an input context is associated with a window, the system automatically selects that context when the window is activated and receives the input focus. The style and other information in the input context affects subsequent keyboard input for that window, determining whether and how the IME operates.

You must destroy any input context you create before terminating your application. You destroy an input context by using the ImmDestroyContext function. Before destroying it, you must remove the input context from any association it has with windows in the thread by using the ImmAssociateContext function.

+$#A Composition String

The composition string is the current text in the composition window. This is the text that the IME converts to final characters. Each composition string consists of one or more clauses, where a clause is the smallest combination of characters that the IME can convert to a final character. You can get and set the composition string and get and set the composition string by using the ImmGetCompositionString and ImmSetCompositionString functions.

As the user enters text in the composition window, the IME tracks the status of the composition string. This status includes attribute information, clause information, typing information, and cursor position. You can retrieve the composition status by using the ImmGetCompositionString function.

In the attribute information array, all characters of one clause must have the same attribute. The attribute information is an array of 8-bit values that specifies the status of characters in the composition string. There is one value for each byte in the string, including one byte each for the lead and second bytes of any double-byte characters in the string. For each value in the array, bits 0 through 3 can be one combination of these values:

ATTR_INPUT
 | Character being entered by the user. It is yet to be converted by the IME.
ATTR_INPUT_ERROR
 | Character is the error character and cannot be converted by the IME.
ATTR_TARGET_CONVERTED
 | Character converted by the IME. The user has selected this character and the IME has converted it.
ATTR_CONVERTED
 | A converted character. The IME has already converted this character.
ATTR_TARGET_NOTCONVERTED
 | Character being converted. The user has selected this character but the IME has not yet converted it.

All other values are reserved. In Japanese, any unconverted character having the ATTR_INPUT attribute is a Hiragana, Katakana, or alphanumeric character. In Korean, this character is a Hangeul character that is not converted by IME yet. In Traditional and Simplified Chinese, each IME may limit its character in some range.

You can retrieve the composition status by using the ImmGetCompositionString function.

The clause information is an array of 32-bit values that specify the positions of the clauses in the composition string. There is one value for each clause and a final value that specifies the length of the full string. Each value in the array specifies the offset, in bytes, from the beginning of the string to the clause. The first value is always 0 because the first clause always starts at the beginning of the string. For example, if a string has two clauses, the clause information has three values: the first value is 0, the second value is the offset of the second clause, and the third value is the length of the string.

The typing information a null-terminated character string representing the characters entered at the keyboard.

The cursor position is a value indicating the position of the cursor relative to the characters in the composition string. The value is the offset, in bytes, from the beginning of the string. If this value is 0, the cursor is immediately before the first character in the string. If the value is equal to the length of the string, the cursor is immediately after the last character. If -1, the cursor is not present.

You can set the composition string or elements of the composition status by using the ImmSetCompositionString function. To ensure that the composition window updates it appearance based on these changes, the function allows for generating a notification message to be sent to the window. Applications that set a combination of composition status elements typically set the fNotify parameter to FALSE for all but the last call to this function so that only one notification message is generated for the composition window.

+$#A Candidate Lists

A candidate list is a CANDIDATELIST structure consisting of an array of strings that specify the characters or character strings that the user may choose from. You can retrieve the candidate lists by using the ImmGetCandidateListCount and ImmGetCandidateList functions.

+$#A Hot Keys

Hot keys give the user a way to quickly change the input mode of the IME or to switch to another IME. Although applications cannot add hot keys to the system, they can initiate the same action as a hot key by using the ImmSimulateHotKey function.

+$#A Using Input Method Editor

• Processing the WM_IME_COMPOSITION message

+$#A Processing the WM_IME_COMPOSITION Message

Applications that process the WM_IME_COMPOSITION message test the bits in lParam parameter and call the ImmGetCompositionString function to retrieve the indicated string or data. The following example checks for the result string, allocates sufficient memory for the string, and retrieves the result string from the IME:

HIMC hIMC;
HWND hWnd;
DWORD dwSize;
HGLOBAL hstr;
LPSTR lpstr;

case WM_IME_COMPOSITION:
if (lParam & GCS_RESULTSTR) {
hIMC = ImmGetContext(hWnd);

If (!hIMC)
MyError(ERROR_NULLCONTEXT);

// Get the size of the result string.
dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);

// increase buffer size for NULL terminator,
// maybe it is in UNICODE
dwSize += sizeof(WCHAR);

hstr = GlobalAlloc(GHND,dwSize);
if (hstr == NULL)
MyError(ERROR_GLOBALALLOC);

lpstr = GlobalLock(hstr);
if (lpstr == NULL)
MyError(ERROR_GLOBALLOCK);

// Get the result strings that is generated by IME into lpstr.
ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize);
ImmReleaseContext(hWnd, hIMC);

// add this string into text buffer of application

GlobalUnlock(hstr);
GlobalFree(hstr);
}

+$#A Input Method EditorReference

This section contains an alphabetical list of the input method editor functions, messages, and structures.

+$#A Input Method Editor Functions

This section describes the input method editor functions.

EnumRegisterWordProc
ImmAssociateContext
ImmConfigureIME
ImmCreateContext
ImmDestroyContext
ImmEnumRegisterWord
ImmEscape
ImmGetCandidateList
ImmGetCandidateListCount
ImmGetCandidateWindow
ImmGetCompositionFont
ImmGetCompositionString
ImmGetCompositionWindow
ImmGetContext
ImmGetConversionList
ImmGetConversionStatus
ImmGetDefaultIMEWnd
ImmGetDescription
ImmGetGuideLine
ImmGetIMEFileName
ImmGetOpenStatus
ImmGetProperty
ImmGetRegisterWordStyle
ImmGetStatusWindowPos
ImmGetVirtualKey
ImmInstallIME
ImmIsIME
ImmIsUIMessage
ImmNotifyIME
ImmRegisterWord
ImmReleaseContext
ImmSetCandidateWindow
ImmSetCompositionFont
ImmSetCompositionString
ImmSetCompositionWindow
ImmSetConversionStatus
ImmSetOpenStatus
ImmSetStatusWindowPos
ImmSimulateHotKey
ImmUnregisterWord

+$#A Input Method Editor Messages

This section describes the input method editor messages.

IMC_GETCANDIDATEPOS
IMC_GETCOMPOSITIONFONT
IMC_GETCOMPOSITIONWINDOW
IMC_GETCONVERSIONMODE
IMC_GETOPENSTATUS
IMC_GETSENTENCEMODE
IMC_GETSTATUSWINDOWPOS
IMC_SETCANDIDATEPOS
IMC_SETCOMPOSITIONFONT
IMC_SETCOMPOSITIONWINDOW
IMC_SETCONVERSIONMODE
IMC_SETOPENSTATUS
IMC_SETSENTENCEMODE
IMC_SETSTATUSWINDOWPOS
IMN_CHANGECANDIDATE
IMN_CLOSECANDIDATE
IMN_CLOSESTATUSWINDOW
IMN_GUIDELINE
IMN_OPENCANDIDATE
IMN_OPENSTATUSWINDOW
IMN_SETCANDIDATEPOS
IMN_SETCOMPOSITIONFONT
IMN_SETCOMPOSITIONWINDOW
IMN_SETCONVERSIONMODE
IMN_SETOPENSTATUS
IMN_SETSENTENCEMODE
IMN_SETSTATUSWINDOWPOS
WM_IME_CHAR
WM_IME_COMPOSITION
WM_IME_COMPOSITIONFULL
WM_IME_CONTROL
WM_IME_ENDCOMPOSITION
WM_IME_NOTIFY
WM_IME_SELECT
WM_IME_SETCONTEXT
WM_IME_STARTCOMPOSITION

+$#A Input Method Editor Structures

This section describes the structures used with input method editor functions and messages.

CANDIDATEFORM
CANDIDATELIST
COMPOSITIONFORM
STYLEBUF

+$#A Input Method Editor Constants

This section describes the constants used by the input method editor functions and messages.

+$#A IME Conversion Mode Values

Bit | Meaning
IME_CMODE_CHARCODE | Set to 1 if character code input mode; 0 if not.
IME_CMODE_EUDC | Set to 1 if EUDC conversion mode; 0 if not.
IME_CMODE_FULLSHAPE | Set to 1 if full shape mode; 0 if half shape mode.
IME_CMODE_HANJACONVERT | Set to 1 if HANJA convert mode; 0 if not.
IME_CMODE_KATAKANA | Set to 1 if KATAKANA mode; 0 if HIRAGANA mode.
IME_CMODE_NATIVE | Set to 1 if NATIVE mode; 0 if ALPHANUMERIC mode.
IME_CMODE_NOCONVERSION | Set to 1 to prevent processing of conversions by IME; 0 if not.
IME_CMODE_ROMAN | Set to 1 if ROMAN input mode; 0 if not.
IME_CMODE_SOFTKBD | Set to 1 if Soft Keyboard mode; 0 if not.

All other bits are reserved.

+$#A IME Composition String Values

Value | Description
GCR_ERRORSTR | Retrieves or updates the error string.
GCR_INFORMATIONSTR | Retrieves or updates the information string.
GCS_COMPATTR | Retrieves or updates the attribute of the composition string.
GCS_COMPCLAUSE | Retrieves or updates clause information of the composition string.
GCS_COMPREADATTR | Retrieves or updates the attributes of the reading string of the current composition.
GCS_COMPREADCLAUSE | Retrieves or updates the clause information of the reading string of the composition string.
GCS_COMPREADSTR | Retrieves or updates the reading string of the current composition.
GCS_COMPSTR | Retrieves or updates the current composition string.
GCS_CURSORPOS | Retrieves or updates the cursor position in composition string.
GCS_DELTASTART | Retrieves or updates the starting position of any changes in composition string.
GCS_RESULTCLAUSE | Retrieves or updates clause information of the result string.
GCS_RESULTREADCLAUSE | Retrieves or updates clause information of the reading string.
GCS_RESULTREADSTR | Retrieves or updates the reading string.
GCS_RESULTSTR | Retrieves or updates the string of the composition result.
GCS_TYPINGINFO | Retrieves or updates the typing information of reading string. In contrast to reading information, this shows raw key strokes that the end user types.

+$#A IME Hot Key Identifiers

IME_CHOTKEY_IME_NONIME_TOGGLE | (Simplified Chinese) Toggles between IME and non-IME operation.
IME_CHOTKEY_SHAPE_TOGGLE | (Simplified Chinese) Toggles the shape conversion mode of IME.
IME_CHOTKEY_SYMBOL_TOGGLE | (Simplified Chinese) Toggles the symbol conversion mode of IME. Symbol mode indicates that the user can input Chinese punctuation and symbols by mapping to the punctuation and symbols on the keyboard.
IME_JHOTKEY_CLOSE_OPEN | (Japanese) Alternately opens and closes the IME.
IME_KHOTKEY_ENGLISH | (Korean) Switches to English.
IME_KHOTKEY_SHAPE_TOGGLE | (Korean) Toggles the shape conversion mode of IME.
IME_KHOTKEY_HANJACONVERT | (Korean) Switches to Hanja converision.
IME_THOTKEY_IME_NONIME_TOGGLE | (Traditional Chinese) Toggles between IME and non-IME operation.
IME_THOTKEY_SHAPE_TOGGLE | (Traditional Chinese) Toggles the shape conversion mode of IME.
IME_THOTKEY_SYMBOL_TOGGLE | (Traditional Chinese) Toggles the symbol conversion mode of IME.

+$#A IME Sentence Mode Values

Constant | Definition
IME_SMODE_AUTOMATIC | The IME carries out conversion processing in automatic mode.
IME_SMODE_NONE | No information for sentence.
IME_SMODE_PHRASEPREDICT | The IME uses phrase information to predict the next character.
IME_SMODE_PLURALCLAUSE | The IME uses plural clause information to carry out conversion processing.
IME_SMODE_SINGLECONVERT | The IME carries out conversion processing in single character mode.

Bits 16 through 31 are reserved for IME use.

+$#A IME Escapes

Escape | Description
IME_ESC_GET_EUDC_DICTIONARY | Retrieves the path of the EUDC dictionary file. On input, the lpData parameter must be the pointer to the buffer to receive the path. This buffer must be no less than 80 characters in length. On return, the buffer contains the null-terminated string specifying the full path. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_HANJA_MODE | Converts from Hangeul to Hanja. On input, lpData must be the pointer to the buffer that contains the Hangeul character to convert; on output, it receives the converted Hanja as a null-terminated string. For use by the Korean editor; do not use in other applications.
IME_ESC_IME_NAME | Retrieves the name of the IME. On input, the lpData parameter must be the pointer to the buffer to receive the name. This buffer must be no less than 64 characters in length. On return, the buffer contains the null-terminated string specifying the IME name. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_MAX_KEY | Returns the maximum number of key stokes for an EUDC character. For use by the Chinese EUDC editor; do not use in other applications.
IME_ESC_QUERY_SUPPORT | Checks for implementation. Returns zero if the escape is not implemented.
IME_ESC_SEQUENCE_TO_INTERNAL | Returns the character code that matches the given sequence code. On input, the lpData parameter is the pointer to a 32-bit variable that contains the sequence code. For use by the Chinese EUDC editor; do not use in other applications. Normally, the Chinese IME will encode its reading character codes into sequence 1 to n.
IME_ESC_SET_EUDC_DICTIONARY | Sets the EUDC dictionary file. On input, the lpData parameter is the pointer to a null-terminated string specifying the full path. For use by the Chinese EUDC editor; do not use in other applications.

The system reserves the escapes in the range IME_ESC_RESERVED_FIRST to IME_ESC_RESERVED_LAST for its own use.

The system reserves the escapes in the range IME_ESC_PRIVATE_FIRST to IME_ESC_PRIVATE_LAST for private use by IMEs.

Créé avec HelpNDoc Personal Edition: Produire des livres EPub gratuitement


Interprocess Communications.rtf

+$#AK Interprocess Communications

The Microsoft® Win32® application programming interface (API) provides a rich set of mechanisms for facilitating communications and data sharing between applications. Collectively, the activities enabled by these mechanisms are called interprocess communications (IPC). In addition to facilitating the division of labor among several specialized processes, some forms of IPC can distribute the computational load among cooperating computers on a network.

+$#A About Interprocess Communications

As computer users become more sophisticated, they demand more power from the applications they use. To meet this demand, developers add more features to their applications, and the applications become larger. These large applications can eventually become unmanageable, both from a development standpoint and from a user-interface point of view. Therefore, developers now tend to produce highly focused applications that do a good job on a limited number of features and then to enable those applications to communicate and share data with other specialized applications. No longer can any one application meet all user expectations; the age of cooperating and communicating applications has arrived.

Typically, cooperating and communicating applications can be categorized as clients or servers. A client is an application or a process that requests a service from some other process. A server is an application or a process that responds to a client request. Many applications act as both a client and a server, depending on the situation. For example, a word processing application might act as a client in requesting a summary table of manufacturing costs from a spreadsheet application acting as a server. The spreadsheet application, in turn, might act as a client in requesting the latest inventory levels from an automated inventory control application.

+$#A Development Considerations

If a developer decides that an application would benefit from IPC, the developer must consider some of the following questions before deciding which of the available IPC methods to use.

• Should the application be able to communicate with other applications running on other computers on a network, or is it sufficient for the application to communicate only with applications on the local computer? In other words, does the application need to be networkable? Some IPC methods work either on the local computer or over a network; others work only on the local computer.

• Should the application be able to communicate with applications running on other computers that may be running under different operating systems (that is, MS-DOS®, Microsoft® Windows® Version 3.x, UNIX®)? In other words, must the application be interoperable?

• Should the user of the application have to choose the other application(s) with which the application communicates, or can the application implicitly find its cooperating partners?

• Should the application communicate with many different applications in a general way, such as allowing cut and paste operations with any other application, or should its communications requirements be limited to a restricted set of interactions with specific other applications? Applications that communicate in a general way are called loosely coupled; applications that have a more strictly defined interaction are called tightly coupled.

• Is performance a critical aspect of the application? All IPC mechanisms include some amount of communications overhead.

• Should the application be a Windows-based application, or will character-mode functionalities be sufficient? Some IPC mechanisms discussed in this topic do not work in character-mode−only applications. The clipboard, dynamic data exchange (DDE), and object linking and embedding (OLE) all require that the application have at least one window.

The answers to these questions determine whether an application can benefit by using one or more of the IPC mechanisms available in the Win32 API. This topic discusses the strengths and weaknesses of each of the Win32 IPC mechanisms.

+$#A File Mapping and IPC

File mapping enables a process to treat the content of a file as if it were a block of memory in the process's address space. Therefore, instead of using file input and output (I/O) operations, the process can use simple pointer operations to examine and modify the contents of the file.

The Win32 API enables two or more processes to access the same file-mapping object. Each process receives a pointer to memory in its own address space. With this pointer, the process can read or modify the contents of the file.

There are three ways an application can share a file-mapping object created in one process with another process:

• Inheritance. The first process creates the file-mapping object and then allows the handle of the object to be inherited by a child process.

• Named file mapping. The first process creates the file-mapping object with a well-known name (which can be different from the filename). The second process opens the file-mapping object by specifying the well-known name. Alternatively, the first process can create a file-mapping object with a unique name and communicate that name to the second process through some other IPC mechanism (named pipe, mailslot, and so on).

• Handle duplication. The first process creates the file-mapping object and then passes the handle of the object to the second process. The second process then duplicates the handle to gain access to the shared memory. The original process can communicate the file-mapping handle to the second process through one of the other IPC mechanisms described in this topic (named pipe, mailslot, and so on). For more information about duplicating handles, see Synchronization.

When two or more processes have read-write access to a shared memory block, they must use some sort of synchronization object, such as a semaphore, to prevent data corruption in a multitasking environment.

File mapping is quite efficient and also provides operating-system−supported security attributes that can help prevent unauthorized data corruption. File mapping can be used only between processes on a local computer; it cannot be used over a network. An application can, however, create a file-mapping object to a file on a remotely mounted volume. For example, if a remote server is mounted as the F drive, an application can create a file-mapping object to a file on that volume. However, a process running on a remote server cannot share a file-mapping object with a process running on a local computer.

Key Point File mapping is an efficient way for two or more processes on the same computer to share data, but the developer must provide synchronization between the processes. For more information, see File Mapping, and Synchronization.

+$#A Shared Memory and IPC

The Win32 API uses a special case of file mapping to provide shared memory access between processes. If you specify the system swapping file when creating a file-mapping object, the file-mapping object is treated as a shared memory block. Other processes can access the same block of memory by opening the same file-mapping object, as described in File Mapping.

Because shared memory is implemented with file mapping, it supports security access attributes and can operate only between processes running on the same computer.

Key Point Shared memory in the Win32 API is implemented by using file mapping. All characteristics of file mapping apply to shared memory. For more information, see File Mapping.

+$#A Anonymous Pipes and IPC

An anonymous (or unnamed) pipe enables related processes to transfer information back and forth as if they were reading from and writing to a file. Typically, anonymous pipes are used for redirecting the standard input and output (I/O) of a child process so that it can exchange data with its parent process.

To use an anonymous pipe, the parent process typically creates the pipe and then allows its read and write handles to be inherited by a child process. The parent process writes data to the pipe; the child process can read the data from the other end of the pipe. Likewise, the child process can write data to the pipe and the parent process can read the data from its end of the pipe. A parent process can also create two or more child processes that inherit the read and write handles to an anonymous pipe. Those child processes can use that pipe to communicate between each other directly, without going through the parent process.

Anonymous pipes cannot be used over a network, nor can they be used between unrelated processes. For information about a pipe mechanism that can be used with unrelated processes and over a network, see Named Pipes.

Key Point Anonymous pipes provide an efficient way to redirect standard I/O to child processes on the same computer. For more information, see Pipes.

+$#A Named Pipes and IPC

Like anonymous pipes, named pipes are used to transfer data back and forth between processes. Unlike anonymous pipes, however, named pipes can operate between unrelated processes and across a network between computers. Typically, a server process creates a named pipe with a well-known name. Client processes that can get the name of the pipe can open the other end of the pipe, subject to access restrictions specified by the pipe's creator. After they are connected, the server and client can exchange data by performing read and write operations on the pipe. Alternatively, the pipe creator can create a pipe and let a child process inherit the handle to the pipe, or it can create a pipe with a unique name and communicate that name to the client through some other IPC mechanism (such as a mailslot maintained by the client).

Key Point Named pipes provide a relatively simple programming interface that makes transferring data across a network no more difficult than transferring data between two processes on the same computer. For more information, see Pipes.

+$#A IPC Mailslots

Mailslots provide a one-way interprocess communications capability. Any process can create a mailslot and become a mailslot server. Other processes, called mailslot clients, can gain access to the mailslot by its name and send messages to the mailslot server process. A process can be both a mailslot server and a mailslot client, so two-way IPC is possible with multiple mailslots.

Incoming messages are always appended to the mailslot. The mailslot saves the messages until the creating process has a chance to read them.

Mailslots are similar to named pipes, but with a somewhat simplified programming interface and the added ability to broadcast messages to all computers in a specified network domain. A mailslot client can send a message to a mailslot on its local computer, to a mailslot on another computer, or to all mailslots with the same name on all computers in a specified network domain. Messages broadcast to a domain can be no longer than 400 bytes; messages sent to a single mailslot are limited only by the maximum message size specified by the creator of the mailslot (which can be unlimited).

Key Point Mailslots offer an easy way for applications to send and receive short messages. They also provide the ability to broadcast messages across all computers in a network domain. For more information, see Mailslots.

+$#A Using the Clipboard for IPC

The clipboard provides a mechanism for the well-established cut-copy-paste model for simple data sharing between Win32-based applications. It enables an application to read or write data in many different standard and application-defined formats. It also works between applications on the same computer or on different computers on a network.

The clipboard acts as a central depository for data sharing among applications. When a user performs a cut or copy operation in an application, the application puts the selected data on the clipboard in one or more formats. Any other application can then retrieve the data from the clipboard, choosing from the available formats. The clipboard is a very loosely coupled exchange medium, wherein the two applications need only agree on the data format.

Key Point All Win32-based applications should support the clipboard for those data formats that they understand. For example, a text editor or word processor should at least be able to produce and accept clipboard data in pure text format. For more information, see Clipboard.

+$#A Dynamic Data Exchange and IPC

Dynamic data exchange (DDE) is a protocol for interprocess communications that enables applications to exchange data in a variety of formats. Applications can use DDE for one-time data exchanges or for ongoing exchanges in which the applications update one another as new data becomes available.

The data formats used by DDE are the same as those used for the Windows clipboard IPC mechanism. DDE can be thought of as an extension of the clipboard mechanism. The clipboard is almost always used for a one-time response to a user command, such as choosing the Paste command from a menu. DDE is also usually initiated by a user command, but it often continues to function without further user interaction.

Three types of data exchange are possible with DDE:

• Cold link. The exchange is a one-time data transfer, like the clipboard.

• Warm link. A server notifies the client when data changes, and the client must then request new data.

• Hot link. A server sends data updates to the client when data changes.

DDE exchanges can occur between applications running on the same computer or on different computers on a network.

Key Point Most major Windows-based applications support DDE. Like the clipboard, DDE support enables an application to exchange data in a variety of standard formats with other Windows-based applications that support DDE. A developer can also define custom DDE data formats for special-purpose IPC between applications with more tightly coupled communications requirements. For more information, see Dynamic Data Exchange and Dynamic Data Exchange Management Library.

+$#A Object Linking and Embedding

Object linking and embedding (OLE) applications manage compound documents ⎯ that is, documents made up of data from a variety of different applications. OLE provides services that make it easy for applications to call on other applications for data editing. For example, an OLE-aware word processor could embed a graph from a spreadsheet. The user could start the spreadsheet automatically from within the word processor by choosing the embedded chart for editing. The OLE libraries would take care of starting the spreadsheet and presenting the graph for editing. When the user quit the spreadsheet, the graph would be updated in the original word processor document. Contrast this with a DDE link involving a spreadsheet graph in a word processor document. With DDE, the user would have to explicitly start the spreadsheet and open the graph document in order to make changes. With OLE, the spreadsheet appears to be an extension of the word processor.

Like an application using DDE, an OLE-aware application can communicate with a wide variety of other Windows-based applications. Because the OLE protocol includes all necessary context information, the application will be able to hold OLE conversations with all other OLE-aware applications ⎯ even those that have yet to be written.

Key Point OLE supports compound documents and enables an application to include embedded or linked data that, when chosen, automatically starts another application for data editing. This enables the application to be extended by any other OLE-aware application.

+$#A Dynamic-Link Libraries and IPC

It is possible to build a Win32-based dynamic-link library (DLL) so that its global data is shared with all processes that call the DLL. Therefore, cooperating processes can call the DLL to examine and modify global data owned by the DLL. For example, process A calls a DLL function with data that the DLL stores in its global data space. Process B calls another DLL function that retrieves that data. Because of the multitasking nature of the Win32 API, the DLL would have to use a semaphore or another synchronization object to control access to the shared memory.

Although shared global data can be used in a DLL, Win32 file mapping is recommended for shared memory. File mapping is more efficient and provides the additional benefit of access protection. (For example, a client can be limited to read-only access to a file-mapping object.)

Key Point Although a developer can use shared global data segments in a DLL to allow two or more applications to share data, it is preferable to use Win32 file-mapping functions to create shared memory. For more information, see Dynamic-Link Libraries.

+$#A Remote Procedure Call

The Win32 API provides remote procedure calls (RPC) to enable applications to call functions remotely. With RPC, communication with other processes becomes as easy as calling a function. RPC operates between processes on a single computer or on different computers on a network. One way to think of RPC is as a DLL that works on a network.

The RPC provided by the Win32 API is compliant with the Open Software Foundation (OSF) Distributed Computing Environment (DCE). This means that RPC applications written by using the Win32 API are able to communicate with other RPC applications running with other operating systems that support DCE. RPC automatically supports data conversion to account for different hardware architectures and for byte-ordering between dissimilar environments.

The Win32 software development kit includes RPC libraries that support MS-DOS−hosted RPC clients. With this facility, a Windows NT server, for example, can provide service to many MS-DOS clients through RPC.

RPC clients and servers are tightly coupled but still maintain high performance. Windows makes extensive use of RPC to facilitate a client-server relationship between different parts of the operating system.

Key Point RPC provides IPC with a function interface, with support for automatic data conversion and for communications with other operating systems. Using RPC, a developer can create high-performance, tightly coupled distributed applications. For more information, see the Microsoft RPC Components.

+$#A The Netbios Function and IPC

The Win32 API provides the Netbios function to process low-level network control functions. This capability is provided primarily for applications written using the IBM NetBIOS system that must be ported to Windows. It is highly recommended that a developer writing a new application use the other IPC mechanisms described in this topic rather than the low-level Netbios function. The IPC mechanisms provided in the Win32 API encapsulate and hide an enormous amount of raw NetBIOS functionality that the developer would otherwise have to implement.

Consider, for example, the following list of NetBIOS actions that would be necessary to emulate a Win32-based mailslot:

On the server end

• Add the name to the local table.

• Listen for the session connection.

• Read the data upon connection.

• Append the data to the message queue.

• Close the session.

• Listen for another session connection.

• Allow the queued messages to be retrieved.

On the client side

• Open the session.

• Write the data.

• Close the session.

Key Point The Netbios function is provided only for porting an existing application written using the IBM NetBIOS system or for those applications that need specialized access to low-level network functionality. For most applications, it is better to use the higher-level IPC mechanisms available in the Win32 API. For more information, see Networks.

+$#A Summary

It is likely that an application will support IPC by using several of the mechanisms described in this topic. For example, all Win32-based applications should provide at least minimal support for the clipboard. In addition, DDE and OLE may offer the application an opportunity to communicate in a loosely coupled way with a wide variety of applications that support these protocols. The great strength of these loosely coupled mechanisms is that a developer can enable an application to share data with other applications without knowing anything about the applications themselves. By supporting the protocols for the clipboard, DDE, and OLE, the developer is assured of a growing number of applications with which the application can share data. As new applications are written that support these protocols, the developer's application will be ready to communicate with them.

As an application becomes more sophisticated, a developer may find it advantageous to break up the application into tightly coupled cooperating processes that use shared memory, pipes, or RPC to communicate. These tightly coupled mechanisms can provide high-performance extensions to the application. But when adding these more specialized IPC methods, the developer should not abandon the more loosely coupled IPC methods that allow the application to share data in a general way with most other Win32-based applications.

Créé avec HelpNDoc Personal Edition: Éditeur de documentation CHM facile


Keyboard Accelerators.rtf

+$#AK Keyboard Accelerators

In Microsoft® Windows®, a keyboard accelerator (or, simply, accelerator) is a keystroke or combination of keystrokes that generates a WM_COMMAND or WM_SYSCOMMAND message for an application.

+$#A About Keyboard Accelerators

Accelerators are closely related to menus ⎯ both provide the user with access to an application's command set. Typically, users rely on an application's menus to learn the command set and then switch over to using accelerators as they become more proficient with the application. Accelerators provide faster, more direct access to commands than menus do. At a minimum, an application should provide accelerators for the more commonly used commands. Although accelerators typically generate commands that exist as menu items, they can also generate commands that have no equivalent menu items.

+$#A Accelerator Tables

An accelerator table consists of an array of ACCEL structures, each defining an individual accelerator. Each ACCEL structure includes the following information:

• The accelerator's keystroke combination.

• The accelerator's identifier.

• Various flags. This includes one that specifies whether Windows is to provide visual feedback by highlighting the corresponding menu item, if any, when the accelerator is used

To process accelerator keystrokes for a given thread, the developer must call the TranslateAccelerator function in the message loop associated with the thread's message queue. The TranslateAccelerator function monitors keyboard input to the message queue, checking for key combinations that match an entry in the accelerator table. When TranslateAccelerator finds a match, it translates the keyboard input (that is, the WM_KEYUP and WM_KEYDOWN messages) into a WM_COMMAND or WM_SYSCOMMAND message and then sends the message to the window procedure of the specified window. The following illustration shows how accelerators are processed.

{ewc msdncd, EWGraphic, group10803 0 /a "SDK_01.bmp"}

The WM_COMMAND message includes the identifier of the accelerator that caused TranslateAccelerator to generate the message. The window procedure examines the identifier to determine the source of the message and then processes the message accordingly.

Accelerator tables exist at two different levels in Windows. Windows maintains a single, system-wide accelerator table that applies to all applications. An application cannot modify the system accelerator table. For a description of the accelerators provided by the system accelerator table, see Accelerator Keystroke Assignments.

Windows also maintains accelerator tables for each application. An application can define any number of accelerator tables for use with its own windows. A unique 32-bit handle (HACCEL) identifies each table. However, only one accelerator table can be active at a time for a given thread. The handle of the accelerator table passed to the TranslateAccelerator function determines which accelerator table is active for a thread. The active accelerator table can be changed at any time by passing a different accelerator-table handle to TranslateAccelerator.

+$#A Accelerator-Table Creation

Several steps are required to create an accelerator table for an application. First, a resource compiler is used to create accelerator-table resources and to add them to the application's executable file. At run time, the LoadAccelerators function is used to load the accelerator table into memory and retrieve the handle of the accelerator table. This handle is passed to the TranslateAccelerator function to activate the accelerator table.

An accelerator table can also be created for an application at run time by passing an array of ACCEL structures to the CreateAcceleratorTable function. This method supports user-defined accelerators in the application. Like the LoadAccelerators function, CreateAcceleratorTable returns an accelerator-table handle that can be passed to TranslateAccelerator to activate the accelerator table.

Windows automatically destroys accelerator tables loaded by LoadAccelerators. An accelerator table created by CreateAcceleratorTable must be destroyed before an application closes; otherwise, the table continues to exist in memory after the application has closed. An accelerator table is destroyed by calling the DestroyAcceleratorTable function.

An existing accelerator table can be copied and modified. The existing accelerator table is copied by using the CopyAcceleratorTable function. After the copy is modified, a handle of the new accelerator table is retrieved by calling CreateAcceleratorTable. Finally, the handle is passed to TranslateAccelerator to activate the new table.

+$#A Accelerator Keystroke Assignments

An ASCII character code or a virtual-key code can be used to define the accelerator. An ASCII character code makes the accelerator case sensitive. The ASCII "C" character can define the accelerator as ALT+c rather than ALT+C. Case-sensitive accelerators can, however, be confusing to use. For example, the ALT+C accelerator will be generated if the CAPS LOCK key is down or if the SHIFT key is down, but not if both are down.

Typically, accelerators don't need to be case sensitive, so most applications use virtual-key codes for accelerators rather than ASCII character codes. .

Avoid accelerators that conflict with an application's menu mnemonics, because the accelerator overrides the mnemonic, which can confuse the user. For more information about menu mnemonics, see Menus (Windows NT).

If an application defines an accelerator that is also defined in the system accelerator table, the application-defined accelerator overrides the system accelerator, but only within the context of the application. Avoid this practice, however, because it prevents the system accelerator from performing its standard role in the Windows user interface. The system-wide accelerators are described in the following list:

Accelerator | Description
ALT+ESC | Switches to the next application.
ALT+F4 | Closes an application or a window.
ALT+HYPHEN | Opens the System menu for a document window.
ALT+PRINT SCREEN | Copies an image in the active window onto the clipboard.
ALT+SPACEBAR | Opens the System menu for an application window.
ALT+TAB | Switches to the next application.
CTRL+ESC | Switches to Windows Task List.
CTRL+F4 | Closes the active group or document window.
F1 | Starts Help if the application has Help.
PRINT SCREEN | Copies an image on the screen onto the clipboard.
SHIFT+ALT+TAB | Switches to the previous application. The user must press and hold down ALT+SHIFT while pressing TAB.

+$#A Accelerators and Menus

Using an accelerator is the same as choosing a menu item: Both actions cause Windows to send a WM_COMMAND or WM_SYSCOMMAND message to the corresponding window procedure. The WM_COMMAND message includes an identifier that the window procedure examines to determine the source of the message. If an accelerator generated the WM_COMMAND message, the identifier is that of the accelerator. Similarly, if a menu item generated the WM_COMMAND message, the identifier is that of the menu item. Because an accelerator provides a shortcut for choosing a command from a menu, an application usually assigns the same identifier to the accelerator and the corresponding menu item.

An application processes an accelerator WM_COMMAND message in exactly the same way as the corresponding menu item WM_COMMAND message. However, the WM_COMMAND message contains a flag that specifies whether the message originated from an accelerator or a menu item, in case accelerators must be processed differently from their corresponding menu items. The WM_SYSCOMMAND message does not contain this flag.

The identifier determines whether an accelerator generates a WM_COMMAND or WM_SYSCOMMAND message. If the identifier has the same value as a menu item in the System menu, the accelerator generates a WM_SYSCOMMAND message. Otherwise, the accelerator generates a WM_COMMAND message.

If an accelerator has the same identifier as a menu item and the menu item is grayed or disabled, the accelerator is disabled and does not generate a WM_COMMAND or WM_SYSCOMMAND message. Also, an accelerator does not generate a command message if the corresponding window is minimized.

When the user uses an accelerator that corresponds to a menu item, the window procedure receives the WM_INITMENU and WM_INITMENUPOPUP messages as though the user had selected the menu item. For information about how to process these messages, see Menus (Windows NT).

An accelerator that corresponds to a menu item should be included in the text of the menu item.

+$#A Using Keyboard Accelerators

• Using an accelerator-table resource

• Using an accelerator table created at run time

+$#A Using an Accelerator-Table Resource

The most common way to add accelerator support to a Windows-based application is to include an accelerator-table resource with the application's executable file and then load the resource at run time. The steps involved in using an accelerator-table resource are as follows:

• Create an accelerator table in a resource-definition file. Compile it and add the resulting resource to your application's executable file.

• Include a call to the LoadAccelerators function to load the accelerator-table resource and receive a handle of the accelerator table.

• Add the TranslateAccelerator function to the message loop associated with the accelerator table.

• Process the WM_COMMAND messages generated when the user uses the accelerator.

+$#A Creating the Accelerator-Table Resource

You create an accelerator-table resource by using the ACCELERATORS statement in your application's resource-definition file. You must assign a name or resource identifier to the accelerator table, preferably unlike that of any other resource. Windows uses this identifier to load the resource at run time.

Each accelerator you define requires a separate entry in the accelerator table. In each entry, you define the keystroke (either an ASCII character code or virtual-key code) that generates the accelerator and the accelerator's identifier. You must also specify whether the keystroke must be used in some combination with the ALT, SHIFT, or CTRL keys. For more information about virtual keys, see Keyboard Input.

An ASCII keystroke is specified either by enclosing the ASCII character in double quotation marks or by using the integer value of the character in combination with the ASCII flag. The following examples show how to define ASCII accelerators.

"A", ID_ACCEL1 ; SHIFT+A

65, ID_ACCEL2, ASCII ; SHIFT+A

A virtual-key code keystroke is specified differently depending on whether the keystroke is an alphanumeric key or a non-alphanumeric key. For an alphanumeric key, the key's letter or number, enclosed in double quotation marks, is combined with the VIRTKEY flag. For a non-alphanumeric key, the Windows virtual-key code for the specific key is combined with the VIRTKEY flag. The following examples show how to define virtual-key code accelerators.

"a", ID_ACCEL3, VIRTKEY ; A (caps-lock on) or a

VK_INSERT, ID_ACCEL4, VIRTKEY ; INSERT key

The following example shows an accelerator-table resource that defines accelerators for file operations. The name of the resource is FileAccel.

FileAccel ACCELERATORS

BEGIN

VK_F12, IDM_OPEN, CONTROL, VIRTKEY ; CTRL+F12

VK_F4, IDM_CLOSE, ALT, VIRTKEY ; ALT+F4

VK_F12, IDM_SAVE, SHIFT, VIRTKEY ; SHIFT+F12

VK_F12, IDM_SAVEAS, VIRTKEY ; F12

END

If you want the user to press the ALT, SHIFT, or CTRL keys in some combination with the accelerator keystroke, specify the ALT, SHIFT, and CONTROL flags in the accelerator's definition. Following are some examples.

"B", ID_ACCEL5, ALT ; ALT_SHIFT+B

"I", ID_ACCEL6, CONTROL, VIRTKEY ; CTRL+I

VK_F5, ID_ACCEL7, CONTROL, ALT, VIRTKEY ; CTRL+ALT+F5

By default, when an accelerator key corresponds to a menu item, Windows highlights the menu item. You can use the NOINVERT flag to prevent highlighting for an individual accelerator. The following example shows how to use the NOINVERT flag.

VK_DELETE, ID_ACCEL8, VIRTKEY, SHIFT, NOINVERT ; SHIFT+DELETE

To define accelerators that correspond to menu items in your application, include the accelerators in the text of the menu items. The following example shows how to include accelerators in menu-item text in a resource-definition file.

FilePopup MENU

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&New..", IDM_NEW

MENUITEM "&Open\tCtrl+F12", IDM_OPEN

MENUITEM "&Close\tAlt+F4" IDM_CLOSE

MENUITEM "&Save\tShift+F12", IDM_SAVE

MENUITEM "Save &As...\tF12", IDM_SAVEAS

END

END

+$#A Loading the Accelerator-Table Resource

An application loads an accelerator-table resource by calling the LoadAccelerators function and specifying the instance handle of the application whose executable file contains the resource and the name or identifier of the resource. LoadAccelerators loads the specified accelerator table into memory and returns the handle of the accelerator table.

An application can load an accelerator-table resource at any time. Usually, a single-threaded application loads its accelerator table before entering its main message loop. An application that uses multiple threads typically loads the accelerator-table resource for a thread before entering the message loop for the thread. An application or thread might also use multiple accelerator tables, each associated with a particular window in the application. Such an application would load the accelerator table for the window each time the user activated the window. For more information about threads, see Processes and Threads.

+$#A Calling the TranslateAccelerator Function

To process accelerators, an application's (or thread's) message loop must contain a call to the TranslateAccelerator function. TranslateAccelerator compares keystrokes to an accelerator table and, if it finds a match, translates the keystrokes into a WM_COMMAND (or WM_SYSCOMMAND) message. The function then sends the message to a window procedure. The parameters of the TranslateAccelerator function include the handle of the window that is to receive the WM_COMMAND messages, the handle of the accelerator table used to translate accelerators, and a pointer to an MSG structure containing a message from the queue. The following example shows how to call TranslateAccelerator from within a message loop.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(

hwndMain, /* handle of receiving window */

haccel, /* handle of active accel. table */

&msg)) { /* address of message data */

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

+$#A Processing WM_COMMAND Messages

When an accelerator is used, the window specified in the TranslateAccelerator function receives a WM_COMMAND or WM_SYSCOMMAND message. The low-order word of the wParam parameter contains the identifier of the accelerator. The window procedure examines the identifier to determine the source of the WM_COMMAND message and process the message accordingly.

Typically, if an accelerator corresponds to a menu item in the application, the accelerator and menu item are assigned the same identifier. If you need to know whether a given WM_COMMAND message was generated by an accelerator or by a menu item, you can examine the high-order word of the wParam parameter. If an accelerator generated the message, the high-order word is 1; if a menu item generated the message, the high-order word is 0.

+$#A Destroying the Accelerator-Table Resource

Windows automatically destroys accelerator-table resources loaded by the LoadAccelerators function, removing the resource from memory after the application closes.

+$#A Creating Accelerators for Font Attributes

The example in this section shows how to perform the following tasks:

• Create an accelerator-table resource.

• Load the accelerator table at run time.

• Translate accelerators in a message loop.

• Process WM_COMMAND messages generated by the accelerators.

These tasks are demonstrated in the context of an application that includes a Character menu and corresponding accelerators that allow the user to select attributes of the current font.

The following portion of a resource-definition file defines the Character menu and the associated accelerator table. Note that the menu items show the accelerator keystrokes and that each accelerator has the same identifier as its associated menu item.

#include <windows.h>

#include "acc.h"

MainMenu MENU

BEGIN

POPUP "&Character"

BEGIN

MENUITEM "&Regular\tF5", IDM_REGULAR

MENUITEM "&Bold\tCtrl+B", IDM_BOLD

MENUITEM "&Italic\tCtrl+I", IDM_ITALIC

MENUITEM "&Underline\tCtrl+U", IDM_ULINE

END

END

FontAccel ACCELERATORS

BEGIN

VK_F5, IDM_REGULAR, VIRTKEY

"B", IDM_BOLD, CONTROL, VIRTKEY

"I", IDM_ITALIC, CONTROL, VIRTKEY

"U", IDM_ULINE, CONTROL, VIRTKEY

END

The following sections from the application's source file show how to implement the accelerators.

HWND hwndMain; /* handle of main window */

HANDLE hinstAcc; /* handle of application instance */

int WINAPI WinMain(hinst, hinstPrev, lpCmdLine, nCmdShow)

HINSTANCE hinst;

HINSTANCE hinstPrev;

LPSTR lpCmdLine;

int nCmdShow;

{

MSG msg; /* application messages */

HACCEL haccel; /* handle of accelerator table */

.

. /* Perform the initialization procedure. */

.

/* Create a main window for this application instance. */

hwndMain = CreateWindowEx(0L, "MainWindowClass",

"Sample Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,

hinst, NULL );

/* If a window cannot be created, return "failure." */

if (!hwndMain)

return FALSE;

/* Make the window visible and update its client area. */

ShowWindow(hwndMain, nCmdShow);

UpdateWindow(hwndMain);

/* Load the accelerator table. */

haccel = LoadAccelerators(hinstAcc, "FontAccel");

if (haccel == NULL)

HandleAccelErr(ERR_LOADING); /* application defined */

/*

* Get and dispatch messages until a WM_QUIT message is

* received.

*/

while (GetMessage(&msg, NULL, NULL, NULL)) {

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(

hwndMain, /* handle of receiving window */

haccel, /* handle of active accel. table */

&msg)) { /* address of message data */

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

return msg.wParam;

}

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

BYTE fbFontAttrib; /* array of font-attribute flags */

static HMENU hmenu; /* handle of main menu */

switch (uMsg) {

case WM_CREATE:

/*

* Add a check mark to the Regular menu item to

* indicate that it is the default.

*/

hmenu = GetMenu(hwndMain);

CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |

MF_CHECKED);

return 0;

case WM_COMMAND:

switch (LOWORD(wParam)) {

/* Process the accelerator and menu commands. */

case IDM_REGULAR:

case IDM_BOLD:

case IDM_ITALIC:

case IDM_ULINE:

/*

* GetFontAttributes is an application-defined

* function that sets the menu-item check marks

* and returns the user-selected font attributes.

*/

fbFontAttrib = GetFontAttributes(

(BYTE) LOWORD(wParam), hmenu);

/*

* SetFontAttributes is an application-defined

* function that creates a font with the

* user-specified attributes the font with

* the main window's device context.

*/

SetFontAttributes(fbFontAttrib);

break;

default:

break;

}

break;

.

. /* Process other messages. */

.

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}

+$#A Using an Accelerator Table Created at Run Time

The Win32 application programming interface (API) allows you to create accelerator tables at run time. The steps involved in creating and using an accelerator table at run time are as follows:

• Define the accelerators by filling an array of ACCEL structures, and then create an accelerator table by passing the array to the CreateAcceleratorTable function.

• Activate the accelerator table and process WM_COMMAND messages generated by the accelerators.

• Destroy the accelerator table before the application closes.

+$#A Creating the Accelerator Table

The first step in creating an accelerator table at run time is filling an array of ACCEL structures. Each structure in the array defines an accelerator in the table. An accelerator's definition includes its flags, its key, and its identifier. The ACCEL structure has the following form.

typedef struct tagACCEL { // accl

BYTE fVirt;

WORD key;

WORD cmd;

} ACCEL;

You define an accelerator's keystroke by specifying an ASCII character code or a virtual-key code in the key member of the ACCEL structure. If you specify a virtual-key code, you must first include the FVIRTKEY flag in the fVirt member; otherwise, Windows interprets the code as an ASCII character code. You can include the FCONTROL, FALT, or FSHIFT flag, or all three, to combine the CTRL, ALT, or SHIFT key with the keystroke.

To create the accelerator table, pass the address of the array of ACCEL structures to the CreateAcceleratorTable function. CreateAcceleratorTable creates the accelerator table and returns the handle of the table.

+$#A Processing Accelerators

The process of loading and calling accelerators provided by an accelerator table created at run time is the same as processing those provided by an accelerator-table resource. For more information, see Loading the Accelerator-Table Resource through Processing WM_COMMAND Messages.

+$#A Destroying the Accelerator Table

Before an application closes, it must destroy accelerator tables created at run time. You can destroy an accelerator table and remove it from memory by passing the table's handle to the DestroyAcceleratorTable function.

+$#A Creating User-Editable Accelerators

This example shows how to construct a dialog box that allows the user to change the accelerator associated with a menu item. The dialog box consists of a combo box containing menu items, a combo box containing the names of keys, and check boxes for selecting the CTRL, ALT, and SHIFT keys. The following illustration shows the dialog box.

{ewc msdncd, EWGraphic, group10803 1 /a "SDK_02.bmp"}

The following example shows how the dialog box is defined in the resource-definition file.

EdAccelBox DIALOG 5, 17, 193, 114

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION

CAPTION "Edit Accelerators"

BEGIN

COMBOBOX IDD_MENUITEMS, 10, 22, 52, 53,

CBS_SIMPLE | CBS_SORT | WS_VSCROLL |

WS_TABSTOP

CONTROL "Control", IDD_CNTRL, "Button",

BS_AUTOCHECKBOX | WS_TABSTOP,

76, 35, 40, 10

CONTROL "Alt", IDD_ALT, "Button",

BS_AUTOCHECKBOX | WS_TABSTOP,

76, 48, 40, 10

CONTROL "Shift", IDD_SHIFT, "Button",

BS_AUTOCHECKBOX | WS_TABSTOP,

76, 61, 40, 10

COMBOBOX IDD_KEYSTROKES, 124, 22, 58, 58,

CBS_SIMPLE | CBS_SORT | WS_VSCROLL |

WS_TABSTOP

PUSHBUTTON "Ok", IDOK, 43, 92, 40, 14

PUSHBUTTON "Cancel", IDCANCEL, 103, 92, 40, 14

LTEXT "Select Item:", 101, 10, 12, 43, 8

LTEXT "Select Keystroke:", 102, 123, 12, 60, 8

END

The dialog box uses an array of application-defined VKEY structures, each containing a keystroke-text string and an accelerator-text string. When the dialog box is created, it parses the array and adds each keystroke-text string to the Select Keystroke combo box. When the user clicks the Ok button, the dialog box looks up the selected keystroke-text string and retrieves the corresponding accelerator-text string. The dialog box appends the accelerator-text string to the text of the menu item that the user selected. The following example shows the array of VKEY structures:

/* VKey Lookup Support */

#define MAXKEYS 26

typedef struct _VKEYS {

char *pKeyName;

char *pKeyString;

} VKEYS;

VKEYS vkeys[MAXKEYS] = {

"BkSp", "Back Space",

"PgUp", "Page Up",

"PgDn", "Page Down",

"End", "End",

"Home", "Home",

"Lft", "Left",

"Up", "Up",

"Rgt", "Right",

"Dn", "Down",

"Ins", "Insert",

"Del", "Delete",

"Mult", "Multiply",

"Add", "Add",

"Sub", "Subtract",

"DecPt", "Decimal Point",

"Div", "Divide",

"F2", "F2",

"F3", "F3",

"F5", "F5",

"F6", "F6",

"F7", "F7",

"F8", "F8",

"F9", "F9",

"F11", "F11",

"F12", "F12"

};

The dialog box's initialization procedure fills the Select Item and Select Keystroke combo boxes. After the user selects a menu item and associated accelerator, the dialog box examines the controls in the dialog box to get the user's selection, updates the text of the menu item, and then creates a new accelerator table that contains the user-defined new accelerator. The following example shows the dialog-box procedure.

/* Global variables */

HWND hwndMain; /* handle of main window */

HANDLE hinstAcc; /* handle of application instance */

HACCEL haccel; /* handle of accelerator table */

.

.

.

/* Dialog-box procedure */

LRESULT CALLBACK EdAccelProc(hwndDlg, uMsg, wParam, lParam)

HWND hwndDlg;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

int nCurSel; /* index of list box item */

UINT idItem; /* menu-item identifier */

UINT uItemPos; /* menu-item position */

UINT i, j = 0; /* loop counters */

static UINT cItems; /* count of items in menu */

char szTemp[32]; /* temporary buffer */

char szAccelText[32]; /* buffer for accelerator text */

char szKeyStroke[16]; /* buffer for keystroke text */

static char szItem[32]; /* buffer for menu-item text */

HWND hwndCtl; /* handle of control window */

static HMENU hmenu; /* handle of "Character" menu */

PCHAR pch, pch2; /* pointers for string copying */

WORD wVKCode; /* accelerator virtual-key code */

BYTE fAccelFlags; /* fVirt flags for ACCEL structure */

LPACCEL lpaccelNew; /* address of new accel. table */

HACCEL haccelOld; /* handle of old accel. table */

int cAccelerators; /* number of accelerators in table */

static BOOL fItemSelected = FALSE; /* item selection flag */

static BOOL fKeySelected = FALSE; /* key selection flag */

switch (uMsg) {

case WM_INITDIALOG:

/* Get the handle of the menu-item combo box. */

hwndCtl = GetDlgItem(hwndDlg, IDD_MENUITEMS);

/*

* The application's menu bar contains a "Character"

* submenu whose items have accelerators associated

* with them. Get the handle of the "Character"

* submenu (its position within the main menu is 2),

* and count the number of items it has.

*/

hmenu = GetSubMenu(GetMenu(hwndMain), 2);

cItems = GetMenuItemCount(hmenu);

/*

* Get the text of each item, strip out the '&' and

* the accelerator text, and add the text to the

* menu-item combo box.

*/

for (i = 0; i < cItems; i++) {

if (!(GetMenuString(hmenu, i, szTemp,

sizeof(szTemp), MF_BYPOSITION)))

continue;

for (pch = szTemp, pch2 = szItem;

*pch != '\0'; ) {

if (*pch != '&') {

if (*pch == '\t') {

*pch = '\0';

*pch2 = '\0';

}

else

*pch2++ = *pch++;

}

else

pch++;

}

SendMessage(hwndCtl, CB_ADDSTRING, 0,

(LONG) (LPSTR) szItem);

}

/*

* Now fill the keystroke combo box with the list of

* keystrokes that will be allowed for accelerators.

* The list of keystrokes is in the application-defined

* structure called "vkeys".

*/

hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);

for (i = 0; i < MAXKEYS; i++)

SendMessage(hwndCtl, CB_ADDSTRING, 0,

(LONG) (LPSTR) vkeys[i].pKeyString);

return TRUE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDD_MENUITEMS:

/*

* The user must select an item from the menu-

* item combo box. This flag is checked during

* IDOK processing to be sure a selection was made.

*/

fItemSelected = TRUE;

return 0;

case IDD_KEYSTROKES:

/*

* The user must select an item from the menu-

* item combo box. This flag is checked during

* IDOK processing to be sure a selection was made.

*/

fKeySelected = TRUE;

return 0;

case IDOK:

/*

* If the user has not selected a menu item

* and a keystroke, display a reminder in a

* message box.

*/

if (!fItemSelected || !fKeySelected) {

MessageBox(hwndDlg,

"Item or key not selected.", NULL,

MB_OK);

return 0;

}

/*

* Determine whether the CTRL, ALT, and SHIFT

* keys are selected. Concatenate the

* appropriate strings to the accelerator-

* text buffer, and set the appropriate

* accelerator flags.

*/

szAccelText[0] = '\0';

hwndCtl = GetDlgItem(hwndDlg, IDD_CNTRL);

if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)

== 1) {

lstrcat(szAccelText, "Ctl+");

fAccelFlags |= FCONTROL;

}

hwndCtl = GetDlgItem(hwndDlg, IDD_ALT);

if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)

== 1) {

lstrcat(szAccelText, "Alt+");

fAccelFlags |= FALT;

}

hwndCtl = GetDlgItem(hwndDlg, IDD_SHIFT);

if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0)

== 1) {

lstrcat(szAccelText, "Shft+");

fAccelFlags |= FSHIFT;

}

/*

* Get the selected keystroke, and look up the

* accelerator text and the virtual-key code

* for the keystroke in the vkeys structure.

*/

hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);

nCurSel = (int) SendMessage(hwndCtl,

CB_GETCURSEL, 0, 0);

SendMessage(hwndCtl, CB_GETLBTEXT,

nCurSel, (LONG) (LPSTR) szKeyStroke);

for (i = 0; i < MAXKEYS; i++) {

if(lstrcmp(vkeys[i].pKeyString,

szKeyStroke) == 0) {

lstrcpy(szKeyStroke,

vkeys[i].pKeyName);

break;

}

}

/*

* Concatenate the keystroke text to the

* "Ctl+","Alt+", or "Shft+" string.

*/

lstrcat(szAccelText, szKeyStroke);

/*

* Determine the position in the menu of the

* selected menu item. Menu items in the

* "Character" menu have positions 0,2,3, and 4.

*/

if (lstrcmp(szItem, "Regular") == 0)

uItemPos = 0;

else if (lstrcmp(szItem, "Bold") == 0)

uItemPos = 2;

else if (lstrcmp(szItem, "Italic") == 0)

uItemPos = 3;

else if (lstrcmp(szItem, "Underline") == 0)

uItemPos = 4;

/*

* Get the string that corresponds to the

* selected item.

*/

GetMenuString(hmenu, uItemPos, szItem,

sizeof(szItem), MF_BYPOSITION);

/*

* Append the new accelerator text to the

* menu-item text.

*/

for (pch = szItem; *pch != '\t'; pch++);

++pch;

for (pch2 = szAccelText; *pch2 != '\0';

pch2++)

*pch++ = *pch2;

*pch = '\0';

/*

* Modify the menu item to reflect the new

* accelerator text.

*/

idItem = GetMenuItemID(hmenu, uItemPos);

ModifyMenu(hmenu, idItem, MF_BYCOMMAND |

MF_STRING, idItem, szItem);

/* Reset the selection flags. */

fItemSelected = FALSE;

fKeySelected = FALSE;

/* Save the current accelerator table. */

haccelOld = haccel;

/*

* Count the number of entries in the current

* table, allocate a buffer for the table, and

* then copy the table into the buffer.

*/

cAccelerators = CopyAcceleratorTable(

haccelOld, NULL, 0);

lpaccelNew = (LPACCEL) LocalAlloc(LPTR,

cAccelerators * sizeof(ACCEL));

if (lpaccelNew != NULL)

CopyAcceleratorTable(haccel, lpaccelNew,

cAccelerators);

/*

* Find the accelerator that the user modified

* and change its flags and virtual-key code

* as appropriate.

*/

for (i = 0; (lpaccelNew[i].cmd ==

(WORD) idItem)

&& (i < (UINT) cAccelerators); i++) {

lpaccelNew[i].fVirt = fAccelFlags;

lpaccelNew[i].key = wVKCode;

}

/*

* Create the new accelerator table, and

* destroy the old one.

*/

DestroyAcceleratorTable(haccelOld);

haccel = CreateAcceleratorTable(lpaccelNew,

cAccelerators);

/* Destroy the dialog box. */

EndDialog(hwndDlg, TRUE);

return 0;

case IDCANCEL:

EndDialog(hwndDlg, TRUE);

return TRUE;

default:

break;

}

default:

break;

}

return FALSE;

}

+$#AK Keyboard Accelerator Reference

The following functions and messages are associated with keyboard accelerators.

+$#A Keyboard Accelerator Functions

Following are the functions used with accelerators.

CopyAcceleratorTable
CreateAcceleratorTable
DestroyAcceleratorTable
LoadAccelerators
TranslateAccelerator

+$#A Keyboard Accelerator Messages

Following are the messages used with accelerators.

WM_COMMAND
WM_INITMENU
WM_INITMENUPOPUP
WM_MENUCHAR
WM_MENUSELECT
WM_SYSCHAR
WM_SYSCOMMAND

Créé avec HelpNDoc Personal Edition: Générateur d'aides Web gratuit


Keyboard Input.rtf

+$#AK Keyboard Input

This overview describes how Windows generates keyboard input and how an application receives and processes that input.

+$#A About Keyboard Input

All applications written for Microsoft® Windows® should accept user input from the keyboard as well as from the mouse. A Windows-based application receives keyboard input in the form of messages posted to its windows.

+$#A Keyboard Input Model

Windows provides device-independent keyboard support for applications by installing a keyboard device driver appropriate for the current keyboard. Windows provides language-independent keyboard support by using the language-specific keyboard layout currently selected by the user or the application. The keyboard device driver receives scan codes from the keyboard, which are sent to the keyboard layout where they are translated into messages and posted to the appropriate windows in your application.

Assigned to each key on a keyboard is a unique value called a scan code, a device-dependent identifier for the key on the keyboard. A keyboard generates two scan codes when the user types a key ⎯ one when the user presses the key and another when the user releases the key.

The keyboard device driver interprets a scan code and translates (maps) it to a virtual-key code, a device-independent value defined by Windows that identifies the purpose of a key. After translating a scan code, the keyboard layout creates a message that includes the scan code, the virtual-key code, and other information about the keystroke, and then places the message in the system message queue. Windows removes the message from the system message queue and posts it to the message queue of the appropriate thread. Eventually, the thread's message loop removes the message and passes it to the appropriate window procedure for processing. The following figure illustrates the keyboard input model for Windows.

{ewc msdncd, EWGraphic, group10804 0 /a "SDK_01.bmp"}

+$#A Keyboard Focus and Activation

Windows posts keyboard messages to the message queue of the foreground thread that created the window with the keyboard focus. The keyboard focus is a temporary property of a window. Windows shares the keyboard among all windows on the display by shifting the keyboard focus, at the user's direction, from one window to another. The window that has the keyboard focus receives (from the message queue of the thread that created it) all keyboard messages until the focus changes to a different window.

A thread can call the GetFocus function to determine which of its windows (if any) currently has the keyboard focus. A thread can give the keyboard focus to one of its windows by calling the SetFocus function. When the keyboard focus changes from one window to another, the system sends a WM_KILLFOCUS message to the window that has lost the focus, and then sends a WM_SETFOCUS message to the window that has gained the focus.

The concept of keyboard focus is related to that of the active window. The active window is the top-level window the user is currently working with. The window with the keyboard focus is either the active window, or a child window of the active window. So the user can easily identify the active window, the system places it at the top of the Z order and makes its title bar (if it has one) and border highlighted.

The user can activate a top-level window by clicking it, selecting it using the ALT+TAB or ALT+ESC key combination, or selecting if from Task List. A thread can activate a top-level window by using the SetActiveWindow function. It can determine whether a top-level window it created is active by using the GetActiveWindow function.

When one window is deactivated and another activated, Windows sends the WM_ACTIVATE message first to the window being deactivated, then to the window being activated. The low-order word of the wParam parameter is zero if the window is being deactivated and nonzero if it is being activated. When the default window procedure receives the WM_ACTIVATE message, it sets the keyboard focus to the active window.

+$#A Keystroke Messages

Pressing a key results in a WM_KEYDOWN or WM_SYSKEYDOWN message being placed in the thread message queue associated with the window that has the keyboard focus. Releasing a key results in a WM_KEYUP or WM_SYSKEYUP message being placed in the queue.

Key-up and key-down messages typically occur in pairs, but if the user holds down a key long enough to start the keyboard's automatic repeat feature, the system generates a number of WM_KEYDOWN or WM_SYSKEYDOWN messages in a row. It then generates a single WM_KEYUP or WM_SYSKEYUP message when the user releases the key.

+$#A System and Nonsystem Keystrokes

Windows makes a distinction between system keystrokes and nonsystem keystrokes. System keystrokes produce system keystroke messages, WM_SYSKEYDOWN and WM_SYSKEYUP. Nonsystem keystrokes produce nonsystem keystroke messages, WM_KEYDOWN and WM_KEYUP.

If your window procedure must process a system keystroke message, make sure that after processing the message the procedure passes it to the DefWindowProc function. Otherwise, all system operations involving the ALT key will be disabled whenever the window has the keyboard focus. That is, the user won't be able to access the window's menus or System menu, or use the ALT+ESC or ALT+TAB key combination to activate a different window.

System keystroke messages are primarily for use by Windows rather than by an application. Windows uses them to provide its built-in keyboard interface to menus and to allow the user to control which window is active. System keystroke messages are generated when the user types a key in combination with the ALT key, or when the user types and no window has the keyboard focus (for example, when the active application is minimized). In this case, the messages are posted to the message queue associated with the active window.

Nonsystem keystroke messages are for use by application windows; the DefWindowProc function does nothing with them. A window procedure can discard any nonsystem keystroke messages that it does not need.

+$#A Virtual-Key Codes Described

The wParam parameter of a keystroke message contains the virtual-key code of the key that was pressed or released. A window procedure processes or ignores a keystroke message, depending on the value of the virtual-key code.

A typical window procedure processes only a small subset of the keystroke messages that it receives and ignores the rest. For example, a window procedure might process only WM_KEYDOWN keystroke messages, and only those that contain virtual-key codes for the cursor movement keys, shift keys (also called control keys), and function keys. A typical window procedure does not process keystroke messages from character keys. Instead, it uses the TranslateMessage function to convert the message into character messages. For more information about TranslateMessage and character messages, see Character Messages.

+$#A Keystroke Message Flags

The lParam parameter of a keystroke message contains additional information about the keystroke that generated the message. This information includes the repeat count, the scan code, the extended-key flag, the context code, the previous key-state flag, and the transition-state flag. The following illustration shows the locations of these flags and values in the lParam parameter:

{ewc msdncd, EWGraphic, group10804 1 /a "SDK_02.bmp"}

An application can use the following values to manipulate the keystroke flags:

Value | Meaning
KF_ALTDOWN | Manipulates the ALT key flag, which indicated if the ALT key is pressed.
KF_DLGMODE | Manipulates the dialog mode flag, which indicates whether a dialog box is active.
KF_EXTENDED | Manipulates the extended key flag.
KF_MENUMODE | Manipulates the menu mode flag, which indicates whether a menu is active.
KF_REPEAT | Manipulates the repeat count.
KF_UP | Manipulates the transition state flag.

Repeat Count

You can check the repeat count to determine whether a keystroke message represents more than one keystroke. The system increments the count when the keyboard generates WM_KEYDOWN or WM_SYSKEYDOWN messages faster than an application can process them. This often occurs when the user holds down a key long enough to start the keyboard's automatic repeat feature. Instead of filling the system message queue with the resulting key-down messages, the system combines the messages into a single key down message and increments the repeat count. Releasing a key cannot start the automatic repeat feature, so the repeat count for WM_KEYUP and WM_SYSKEYUP messages is always set to 1.

Scan Code

The scan code is the value that the keyboard hardware generates when the user presses a key. It is a device-dependent value that identifies the key pressed, as opposed to the character represented by the key. An application typically ignores scan codes. Instead, it uses the device-independent virtual-key codes to interpret keystroke messages.

Extended-Key Flag

The extended-key flag indicates whether the keystroke message originated from one of the additional keys on the enhanced keyboard. The extended keys consist of the ALT and CTRL keys on the right-hand side of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE DOWN and arrow keys in the clusters to the left of the numeric keypad; the NUM LOCK key; the BREAK (CTRL+PAUSE) key; the PRINT SCRN key; and the divide (/) and ENTER keys in the numeric keypad. The extended-key flag is set if the key is an extended key.

Context Code

The context code indicates whether the ALT key was down when the keystroke message was generated. The code is 1 if the ALT key was down and 0 if it was up.

Previous Key-State Flag

The previous key-state flag indicates whether the key that generated the keystroke message was previously up or down. It is 1 if the key was previously down and 0 if the key was previously up. You can use this flag to identify keystroke messages generated by the keyboard's automatic repeat feature. This flag is set to 1 for WM_KEYDOWN and WM_SYSKEYDOWN keystroke messages generated by the automatic repeat feature. It is always set to 0 for WM_KEYUP and WM_SYSKEYUP messages.

Transition-State Flag

The transition-state flag indicates whether pressing a key or releasing a key generated the keystroke message. This flag is always set to 0 for WM_KEYDOWN and WM_SYSKEYDOWN messages; it is always set to 1 for WM_KEYUP and WM_SYSKEYUP messages.

+$#A Character Messages

Keystroke messages provide a lot of information about keystrokes, but they don't provide character codes for character keystrokes. To retrieve character codes, an application must include the TranslateMessage function in its thread message loop. TranslateMessage passes a WM_KEYDOWN or WM_SYSKEYDOWN message to the keyboard layout. The layout examines the message's virtual-key code and, if it corresponds to a character key, provides the character code equivalent (taking into account the state of the SHIFT and CAPS LOCK keys). It then generates a character message that includes the character code and places the message at the top of the message queue. The next iteration of the message loop removes the character message from the queue and dispatches the message to the appropriate window procedure.

+$#A Nonsystem Character Messages

A window procedure can receive four different character messages, including WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, and WM_SYSDEADCHAR. The TranslateMessage function generates a WM_CHAR or WM_DEADCHAR message when it processes a WM_KEYDOWN message. Similarly, it generates a WM_SYSCHAR or WM_SYSDEADCHAR message when it processes a WM_SYSKEYDOWN message.

An application that processes keyboard input typically ignores all but the WM_CHAR message, passing any other messages to the DefWindowProc function. Windows uses the WM_SYSCHAR and WM_SYSDEADCHAR messages to implement menu mnemonics.

The wParam parameter of all character messages contains the character code of the character key that was pressed. The value of the character code depends on the window class of the window receiving the message. If the Unicode™ version of the RegisterClass function was used to register the window class, the system provides Unicode characters to all windows of that class. Otherwise, the system provides ASCII character codes from the Windows character set. For more information about Unicode, see Unicode and Character Sets.

The contents of the lParam parameter of a character message are identical to the contents of the lParam parameter of the key-down message that was translated to produce the character message. For information about the contents of the lParam parameter, see Keystroke Message Flags.

+$#A Dead-Character Messages

Some non-English keyboards contain character keys that are not expected to produce characters by themselves. Instead, they are used to add a diacritic to the character produced by the subsequent keystroke. These keys are called dead keys. The circumflex key on a German keyboard is an example of a dead key. To enter the character consisting of an "o" with a circumflex, a German user would type the circumflex key followed by the "o" key. The window with the keyboard focus would receive the following sequence of messages:

WM_KEYDOWN
WM_DEADCHAR
WM_KEYUP
WM_KEYDOWN
WM_CHAR
WM_KEYUP

TranslateMessage generates the WM_DEADCHAR message when it processes the WM_KEYDOWN message from a dead key. Although the wParam parameter of the WM_DEADCHAR message contains the character code of the diacritic for the dead key, an application typically ignores the message. Instead, it processes the WM_CHAR message generated by the subsequent keystroke. The wParam parameter of the WM_CHAR message contains the character code of the letter with the diacritic. If the subsequent keystroke generates a character that cannot be combined with a diacritic, Windows generates two WM_CHAR messages. The wParam parameter of the first contains the character code of the diacritic; the wParam parameter of the second contains the character code of the subsequent character key.

The TranslateMessage function generates the WM_SYSDEADCHAR message when it processes the WM_SYSKEYDOWN message from a system dead key (a dead key that is pressed in combination with the ALT key). An application typically ignores the WM_SYSDEADCHAR message.

+$#A Key Status

While processing a keyboard message, an application may need to determine the status of another key besides the one that generated the current message. For example, a word-processing application that allows the user to press SHIFT+END to select a block of text must check the status of the SHIFT key whenever it receives a keystroke message from the END key. The application can use the GetKeyState function to determine the status of a virtual key at the time the current message was generated; it can use the GetAsyncKeyState function to retrieve the current status of a virtual key.

The keyboard layout maintains a list of names. The name of a key that produces a single character is the same as the character produced by the key. The name of a noncharacter key such as TAB and ENTER is stored as a character string. An application can retrieve the name of any key from the device driver by calling the GetKeyNameText function.

+$#A Keystroke and Character Translations

Windows includes several special purpose functions that translate scan codes, character codes, and virtual-key codes provided by various keystroke messages. These functions include MapVirtualKey, ToAscii, ToUnicode, and VkKeyScan.

+$#A Hot-Key Support

Windows provides a set of functions that applications can use to define hot keys. A hot key is a key combination that generates a WM_HOTKEY message, a message the system places at the top of a thread's message queue, bypassing any existing messages in the queue. Applications use hot keys to obtain high-priority keyboard input from the user. For example, by defining a hot key consisting of the CTRL+C key combination, an application can allow the user to cancel a lengthy operation.

To define a hot key, an application calls the RegisterHotKey function, specifying the combination of keys that generates the WM_HOTKEY message, the handle of the window to receive the message, and the identifier of the hot key. When the user presses the hot key, a WM_HOTKEY message is placed in the message queue of the thread that created the given window. The wParam parameter of the message contains the identifier of the hot key. The application can define multiple hot keys for a thread, but each hot key in the thread must have a unique identifier. Before the application terminates, it should use the UnregisterHotKey function to destroy the hot key.

Applications can use a hot key control to make it easy for the user to choose a hot key. Hot key controls are typically used to define a hot key that activates a window; they do not use the RegisterHotKey and UnregisterHotKey functions. Instead, an application that uses a hot key control typically sends the WM_SETHOTKEY message to set the hot key. Whenever the user presses the hot key, the system sends a WM_SYSCOMMAND message specifying SC_HOTKEY. For more information about hot key controls, see Hot-Key Controls.

+$#A Languages, Locales, and Keyboard Layouts

A language is a natural language, such as English, French, and Japanese. A sublanguange is a variant of a natural language that is spoken in a specific geographical region, such as the English sublanguages spoken in Great Britian and the United States. Win32-based applications use values, called locales, to uniquely identify languages and sublanguages. Applications typically use locales to set the language in which input and output is processed. Setting the locale for the keyboard, for example, affects the character values generated by the keyboard. Setting the locale for the display or printer affects the glyphs displayed or printed. Applications set the locale for a keyboard by loading and using keyboard layouts. They set the locale for a display or printer by selecting a font that supports the given locale.

A keyboard layout not only specifies the physical position of the keys on the keyboard but also determines the character values generated by pressing those keys. Each layout has an associated locale which identifies the current input language and determines which character values are generated by which keys and key combinations.

Every keyboard layout has a corresponding handle which identifies of the layout and language. The low word of the handle is a language identifier. The high word is a device handle, specifying the physical layout, or is zero, indicating a default physical layout. The user can associate any input language with a given physical layout. For example, an English-speaking user who very occasionally works in French can set the input language of the keyboard to French without changing the physical layout of the keyboard. This means the user can enter text in French using the familiar English layout.

Applications are generally not expected to manipulate input languages directly. Instead, the user sets up language and layout combinations, then switches among them. When the user clicks into text marked with a different language, the application calls the ActivateKeyboardLayout function to activate the user's default layout for that language. If the user edits text in a language which is not in the active list, the application can call the LoadKeyboardLayout function with the language to get a layout based on that language.

The ActivateKeyboardLayout function sets the input language for the current task. The hkl parameter can be either the handle of the keyboard layout or a zero-extended language identifier. Keyboard layout handles can be obtained from the LoadKeyboardLayout or GetKeyboardLayoutList function. The HKL_NEXT and HKL_PREV values can also be used to select the next or previous keyboard. For some operating systems, the KLF_UNLOADPREVIOUS value has no meaning and is ignored.

The GetKeyboardLayoutName function retrieves the name of the active keyboard layout for the calling thread. If an application creates the active layout using the LoadKeyboardLayout function, GetKeyboardLayoutName retrieves the same string used to create the layout. Otherwise, the string is the primary language identifier correspoding to the locale of the active layout. This means the function may not necessarily differentiate among different layouts with the same primary language, so cannot return specific information about the input language. The GetKeyboardLayout function, however, can be used to determine the input language.

The LoadKeyboardLayout function loads a keyboard layout and makes the layout available to the user. Applications can use the make the layout immediately active for the current thread by using the KLF_ACTIVATE value. An application can use the KLF_REORDER value to reorder the layouts without also specifying the KLF_ACTIVATE value. Applications should always use the KLF_SUBSTITUTE_OK value when loading keyboard layouts to ensure that the user's preference, if any, is selected. For some operating systems, the KLF_UNLOADPREVIOUS value is ignored.

For multilingual support, the LoadKeyboardLayout function provides the KLF_REPLACELANG and KLF_NOTELLSHELL flags. The KLF_REPLACELANG flag directs the function to replace an existing keyboard layout without changing associated the language. Attempting to replace an existing layout using the same language identifier but without specifying KLF_REPLACELANG is an error. The KLF_NOTELLSHELL flag prevents the function from notifying the shell when a keyboard layout is added or replaced. This is useful for applications that add multiple layouts in a consecutive series of calls. This flag should be used in all but the last call.

The UnloadKeyboardLayout function is restricted in that it cannot unload the system default input language. This ensures that the user always has one layout available for enter text using the same character set as used by the shell and file system.

+$#A Using Keyboard Input

A window receives keyboard input in the form of keystroke messages and character messages. The message loop associated with the window must include code to translate keystroke messages into the corresponding character messages. If the window displays keyboard input in its client area, it should create and display a caret to indicate the position where the next character will be entered. The following sections describe the code involved in receiving, processing, and displaying keyboard input.

• Processing Keystroke Messages

• Translating Character Messages

• Processing Character Messages

• Using the Caret

• Displaying Keyboard Input

+$#A Processing Keystroke Messages

The window procedure of the window that has the keyboard focus receives keystroke messages when the user types at the keyboard. The keystroke messages are WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, and WM_SYSKEYUP. A typical window procedure ignores all keystroke messages except WM_KEYDOWN. Windows posts the WM_KEYDOWN message when the user presses a key.

When the window procedure receives the WM_KEYDOWN message, it should examine the virtual-key code that accompanies the message to determine how to process the keystroke. The virtual-key code is in the message's wParam parameter. Typically, an application processes only keystrokes generated by noncharacter keys, including the function keys, the cursor movement keys, and the special-purpose keys such as INS, DEL, HOME and END.

The following example shows the window procedure framework that a typical application uses to receive and process keystroke messages.

case WM_KEYDOWN:

switch (wParam) {

case VK_LEFT:

.

. /* Process the LEFT ARROW key. */

.

break;

case VK_RIGHT:

.

. /* Process the RIGHT ARROW key. */

.

break;

case VK_UP:

.

. /* Process the UP ARROW key. */

.

break;

case VK_DOWN:

.

. /* Process the DOWN ARROW key. */

.

break;

case VK_HOME:

.

. /* Process the HOME key. */

.

break;

case VK_END:

.

. /* Process the END key. */

.

break;

case VK_INSERT:

.

. /* Process the INS key. */

.

break;

case VK_DELETE:

.

. /* Process the DEL key. */

.

break;

case VK_F2:

.

. /* Process the F2 key. */

.

break;

.

. /* Process other noncharacter keystrokes. */

.

default:

break;

}

+$#A Translating Character Messages

Any thread that receives character input from the user must include the TranslateMessage function in its message loop. This function examines the virtual-key code of a keystroke message and, if the code corresponds to a character, places a character message into the message queue. The character message is removed and dispatched on the next iteration of the message loop; the wParam parameter of the message contains the character code.

In general, a thread's message loop should use the TranslateMessage function to translate every message, not just virtual-key messages. Although TranslateMessage has no effect on other types of messages, it guarantees that keyboard input is translated correctly. The following example shows how to include the TranslateMessage function in a typical thread message loop.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

+$#A Processing Character Messages

A window procedure receives a character message when the TranslateMessage function translates a virtual-key code corresponding to a character key. The character messages are WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, and WM_SYSDEADCHAR. A typical window procedure ignores all character messages except WM_CHAR. The TranslateMessage function generates a WM_CHAR message when the user presses any of the following keys.

• Any character key

• BACKSPACE

• ENTER (carriage return)

• ESC

• SHIFT+ENTER (linefeed)

• TAB

When a window procedure receives the WM_CHAR message, it should examine the character code that accompanies the message to determine how to process the character. The character code is in the message's wParam parameter.

The following example shows the window procedure framework that a typical application uses to receive and process character messages.

case WM_CHAR:

switch (wParam) {

case 0x08:

.

. /* Process a backspace. */

.

break;

case 0x0A:

.

. /* Process a linefeed. */

.

break;

case 0x1B:

.

. /* Process an escape. */

.

break;

case 0x09:

.

. /* Process a tab. */

.

break;

case 0x0D:

.

. /* Process a carriage return. */

.

break;

default:

.

. /* Process displayable characters. */

.

break;

}

+$#A Using the Caret

A window that receives keyboard input typically displays the characters the user types in the window's client area. A window should use a caret to indicate the position in the client area where the next character will appear. The window should also create and display the caret when it receives the keyboard focus, and hide and destroy the caret when it loses the focus. A window can perform these operations in the processing of the WM_SETFOCUS and WM_KILLFOCUS messages. For more information about carets, see Carets.

+$#A Displaying Keyboard Input

The example in this section shows how an application can receive characters from the keyboard, display them in the client area of a window, and update the position of the caret with each character typed. It also demonstrates how to move the caret in response to the LEFT ARROW, RIGHT ARROW, HOME and END keystrokes, and shows how to highlight selected text in response to the SHIFT+RIGHT ARROW key combination.

During processing of the WM_CREATE message, the window procedure shown in the example allocates a 64K buffer for storing keyboard input. It also retrieves the metrics of the currently loaded font, saving the height and average width of characters in the font. The height and width are used in processing the WM_SIZE message to calculate the line length and maximum number of lines, based on the size of the client area.

The window procedure creates and displays the caret when processing the WM_SETFOCUS message. It hides and deletes the caret when processing the WM_KILLFOCUS message.

When processing the WM_CHAR message, the window procedure displays characters, stores them in the input buffer, and updates the caret position. The window procedure also converts tab characters to four consecutive space characters. Backspace, linefeed, and escape characters generate a beep, but are not otherwise processed.

The window procedure performs the left, right, end, and home caret movements when processing the WM_KEYDOWN message. While processing the action of the RIGHT ARROW key, the window procedure checks the state of the SHIFT key and, if it is down, selects the character to the right of the caret as the caret is moved.

Note that the following code is written so that it can be compiled either as Unicode™ or as ANSI. If the source code defines Unicode, strings are handled as Unicode characters; otherwise, they are handled as ANSI characters.

#define BUFSIZE 65535

#define SHIFTED 0x8000

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

UINT wParam;

LONG lParam;

{

HDC hdc; /* handle of device context */

TEXTMETRIC tm; /* structure for text metrics */

static DWORD dwCharX; /* average width of characters */

static DWORD dwCharY; /* height of characters */

static DWORD dwClientX; /* width of client area */

static DWORD dwClientY; /* height of client area */

static DWORD dwLineLen; /* line length */

static DWORD dwLines; /* text lines in client area */

static int nCaretPosX = 0; /* horizontal position of caret */

static int nCaretPosY = 0; /* vertical position of caret */

static int nCharWidth = 0; /* width of a character */

static int cch = 0; /* characters in buffer */

static int nCurChar = 0; /* index of current character */

static PTCHAR pchInputBuf; /* address of input buffer */

int i, j; /* loop counters */

int cCR = 0; /* count of carriage returns */

int nCRIndex = 0; /* index of last carriage return */

int nVirtKey; /* virtual-key code */

TCHAR szBuf[128]; /* temporary buffer */

TCHAR ch; /* current character */

PAINTSTRUCT ps; /* required by BeginPaint */

RECT rc; /* output rectangle for DrawText */

SIZE sz; /* string dimensions */

COLORREF crPrevText; /* previous text color */

COLORREF crPrevBk; /* previous background color */

switch (uMsg) {

case WM_CREATE:

/* Get the metrics of the current font. */

hdc = GetDC(hwndMain);

GetTextMetrics(hdc, &tm);

ReleaseDC(hwndMain, hdc);

/* Save the average character width and height. */

dwCharX = tm.tmAveCharWidth;

dwCharY = tm.tmHeight;

/* Allocate a buffer to store keyboard input. */

pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,

BUFSIZE * sizeof(TCHAR));

return 0;

case WM_SIZE:

/* Save the new width and height of the client area. */

dwClientX = LOWORD(lParam);

dwClientY = HIWORD(lParam);

/*

* Calculate the maximum width of a line and the

* maximum number of lines in the client area.

*/

dwLineLen = dwClientX - dwCharX;

dwLines = dwClientY / dwCharY;

break;

case WM_SETFOCUS:

/*

* Create, position, and display the caret when the

* window receives the keyboard focus.

*/

CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);

SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);

ShowCaret(hwndMain);

break;

case WM_KILLFOCUS:

/*

* Hide and destroy the caret when the window loses the

* keyboard focus.

*/

HideCaret(hwndMain);

DestroyCaret();

break;

case WM_CHAR:

switch (wParam) {

case 0x08: /* backspace */

case 0x0A: /* linefeed */

case 0x1B: /* escape */

MessageBeep(0xFFFFFFFF);

return 0;

case 0x09: /* tab */

/* Convert tabs to four consecutive spaces. */

for (i = 0; i < 4; i++)

SendMessage(hwndMain, WM_CHAR, 0x20, 0);

return 0;

case 0x0D: /* carriage return */

/*

* Record the carriage return and position the

* caret at the beginning of the new line.

*/

pchInputBuf[cch++] = 0x0D;

nCaretPosX = 0;

nCaretPosY += 1;

break;

default: /* displayable character */

ch = (TCHAR) wParam;

HideCaret(hwndMain);

/*

* Retrieve the character's width and output

* the character.

*/

hdc = GetDC(hwndMain);

GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,

&nCharWidth);

TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY,

&ch, 1);

ReleaseDC(hwndMain, hdc);

/* Store the character in the buffer. */

pchInputBuf[cch++] = ch;

/*

* Calculate the new horizontal position of the

* caret. If the position exceeds the maximum,

* insert a carriage return and move the caret

* to the beginning of the next line.

*/

nCaretPosX += nCharWidth;

if ((DWORD) nCaretPosX > dwLineLen) {

nCaretPosX = 0;

pchInputBuf[cch++] = 0x0D;

++nCaretPosY;

}

nCurChar = cch;

ShowCaret(hwndMain);

break;

}

SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);

break;

case WM_KEYDOWN:

switch (wParam) {

case VK_LEFT: /* LEFT ARROW */

/*

* The caret can move only to the beginning of

* the current line.

*/

if (nCaretPosX > 0) {

HideCaret(hwndMain);

/*

* Retrieve the character to the left of

* the caret, calculate the character's

* width, then subtract the width from the

* current horizontal position of the caret

* to obtain the new position.

*/

ch = pchInputBuf[--nCurChar];

hdc = GetDC(hwndMain);

GetCharWidth32(hdc, ch, ch, &nCharWidth);

ReleaseDC(hwndMain, hdc);

nCaretPosX = max(nCaretPosX - nCharWidth,

0);

ShowCaret(hwndMain);

}

break;

case VK_RIGHT: /* RIGHT ARROW */

/*

* Caret moves to the right or, when a carriage

* return is encountered, to the beginning of

* the next line.

*/

if (nCurChar < cch) {

HideCaret(hwndMain);

/*

* Retrieve the character to the right of

* the caret. If it's a carriage return,

* position the caret at the beginning of

* the next line.

*/

ch = pchInputBuf[nCurChar];

if (ch == 0x0D) {

nCaretPosX = 0;

nCaretPosY++;

}

/*

* If the character isn't a carriage

* return, check to see whether the SHIFT

* key is down. If it is, invert the text

* colors and output the character.

*/

else {

hdc = GetDC(hwndMain);

nVirtKey = GetKeyState(VK_SHIFT);

if (nVirtKey & SHIFTED) {

crPrevText = SetTextColor(hdc,

RGB(255, 255, 255));

crPrevBk = SetBkColor(hdc,

RGB(0,0,0));

TextOut(hdc, nCaretPosX,

nCaretPosY * dwCharY,

&ch, 1);

SetTextColor(hdc, crPrevText);

SetBkColor(hdc, crPrevBk);

}

/*

* Get the width of the character and

* calculate the new horizontal

* position of the caret.

*/

GetCharWidth32(hdc, ch, ch, &nCharWidth);

ReleaseDC(hwndMain, hdc);

nCaretPosX = nCaretPosX + nCharWidth;

}

nCurChar++;

ShowCaret(hwndMain);

break;

}

break;

case VK_UP: /* UP ARROW */

case VK_DOWN: /* DOWN ARROW */

MessageBeep(0xFFFFFFFF);

return 0;

case VK_HOME: /* HOME */

/*

* Set the caret's position to the upper left

* corner of the client area.

*/

nCaretPosX = nCaretPosY = 0;

nCurChar = 0;

break;

case VK_END: /* END */

/* Move the caret to the end of the text. */

for (i=0; i < cch; i++) {

/*

* Count the carriage returns and save the

* index of the last one.

*/

if (pchInputBuf[i] == 0x0D) {

cCR++;

nCRIndex = i + 1;

}

}

nCaretPosY = cCR;

/*

* Copy all text between the last carriage

* return and the end of the keyboard input

* buffer to a temporary buffer.

*/

for (i = nCRIndex, j = 0; i < cch; i++, j++)

szBuf[j] = pchInputBuf[i];

szBuf[j] = TEXT('\0');

/*

* Retrieve the text extent and use it

* to set the horizontal position of the

* caret.

*/

hdc = GetDC(hwndMain);

GetTextExtentPoint32(hdc, szBuf, lstrlen(szBuf),

&sz);

nCaretPosX = sz.cx;

ReleaseDC(hwndMain, hdc);

nCurChar = cch;

break;

default:

break;

}

SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);

break;

case WM_PAINT:

if (cch == 0) /* nothing in input buffer */

break;

hdc = BeginPaint(hwndMain, &ps);

HideCaret(hwndMain);

/*

* Set the clipping rectangle, and then draw the text

* into it.

*/

SetRect(&rc, 0, 0, dwLineLen, dwClientY);

DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);

ShowCaret(hwndMain);

EndPaint(hwndMain, &ps);

break;

.

. /* Process other messages. */

.

case WM_DESTROY:

PostQuitMessage(0);

/* Free the input buffer. */

GlobalFree((HGLOBAL) pchInputBuf);

UnregisterHotKey(hwndMain, 0xAAAA);

break;

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}

+$#AK Keyboard Input Reference

The following functions and messages are associated with keyboard input.

+$#A Keyboard Input Functions

Following are the functions used to receive and process keyboard input.

ActivateKeyboardLayout
EnableWindow
GetActiveWindow
GetAsyncKeyState
GetFocus
GetKeyboardLayoutName
GetKeyboardState
GetKeyNameText
GetKeyState
IsWindowEnabled
keybd_event
LoadKeyboardLayout
MapVirtualKey
OemKeyScan
RegisterHotKey
SetActiveWindow
SetFocus
SetKeyboardState
ToAscii
ToUnicode
UnloadKeyboardLayout
UnregisterHotKey
VkKeyScan

Obsolete or Deleted Functions

GetKBCodePage

+$#A Keyboard Input Messages

Following are the messages used to receive and process keyboard input.

WM_ACTIVATE
WM_CHAR
WM_DEADCHAR
WM_HOTKEY
WM_KEYDOWN
WM_KEYUP
WM_KILLFOCUS
WM_SETFOCUS
WM_SYSCHAR
WM_SYSDEADCHAR
WM_SYSKEYDOWN
WM_SYSKEYUP

Créé avec HelpNDoc Personal Edition: Produire des livres Kindle gratuitement


LAN Manager Functions.rtf

+$#AK LAN Manager Functions

#Overview of Windows Networking Functions

For OS/2®-based servers the Microsoft® LAN Manager functions provided much of the functionality required for a network operating system; this functionality was missing from the local operating system. The Microsoft® Windows NT™ operating system will provide this missing functionality in the base. However, in order to make the migration of existing 16-bit applications as easy as possible, it is necessary to still define 32-bit equivalents of some of the LAN Manager functions.

The Windows networking functions specified in this online help are designed to provide some of the API functionality that was available in LAN Manager 2.x. They are not the base Windows NT networking functions. Windows NT takes some of the functionality that was previously supplied by the networking software, and moves this into the base functions (such as error and audit logging, printing). Windows NT also provides a network-independent set of network functions (the WNet functions) that allow network functions to work across different network vendors' products. If a base function or WNet function exists that could be used by your application, you should convert from the Windows networking function to the public Windows NT equivalent. There are at least three reasons to make the change now:

1. The WNet functions are network independent, while the Windows networking functions only work on LAN Manager networks.

2. Some of the Windows networking functions specified in this document may not be supported in future releases of Windows NT if they have been superseded by base functions or WNet functions. (Of course Microsoft does not plan to remove specific Windows networking functions unless equivalent or better functionality is available from other functions.)

3. The Windows networking functions may not be supported on some other future Microsoft platforms, since they are not part of the Microsoft® Win32® application programming interface (API).

In this documentation, equivalent Win32 functions will be listed that could be used in place of the Windows networking function. If at all possible, this is the function that your application should use.

This documentation assumes that you are familiar with the existing set of 16-bit LAN Manager functions. The 32-bit widening of the API data structures is specified here, but the description of the functionality for each field is not covered unless the functionality has changed from that specified in the LAN Manager 2.x Programmer's Reference.

+$#A API Buffers

The RPC run times will allocate the buffers required to remote the APIs. This is a requirement for both efficiency and interoperability. Using the RPC run times to allocate the API transmission buffers results in the two significant differences between a LAN Manager 2.x API and windows networking function.

• For a set type function (data to the server) the function caller specifies a buffer containing the information structure relevant to the function level but does not specify the buffer length.

• For a get type function (data returned from the server) the caller does not pre-allocate a buffer for the return information. The caller passes a LPBYTE * to the function on input. On successful return the buffer pointer will contain a pointer to a buffer containing the return information. When the caller has finished processing the returned information NetApiBufferFree must be called. This simplifies the calling code, because the caller does not need to guess at the size of the buffer required and will not need to resize and reissue the function as was the case with the LAN Manager 2.x functions.

+$#A API Data Alignment

All data structures specified for the Windows networking functions will be 32-bit word aligned. The base size for an API structure element will be a DWORD.

+$#A Enumeration Buffer Lengths

Enumeration functions will take an advisory maximum data length parameter, prefmaxlen, which allows a control on the number of bytes returned from an enumeration call. The prefered length is specified in units of 8-bit bytes. The actual function may return more than the prefered maximum length.

A Windows networking function enumeration call will not return partial entries.

+$#A Parameter Error Reporting

Add and SetInfo functions will return an index for a parameter in error. The caller may pass a NULL pointer for the parm_err parameter indicating that the field should not be set by the function. For remoted functions to downlevel servers this field will be returned as PARM_ERROR_UNKNOWN by the RpcXlate conversion layer.

+$#A Parmnum for SetInfo

To reduce the number of RPC interfaces we need for each SetInfo function, there will be no Parmnum parameter for the 32-bit SetInfo functions. Instead, the fields that can be set in the information structures will be set using additional infolevels. Although this will increase the number of information levels for a specified function, it will be easier to program to them. In an attempt to keep the mapping to and from downlevel calls simple, the new information levels will be easily mapped to the old parmnum values. By subtracting a specific number like PARMNUM_BASE_INFOLEVEL (whose value will be 1000).

+$#A Parmnum for GetInfo

There will be no addition of a parmnum field for the GetInfo function.

+$#A Embedded Strings

Windows networking function info structures will not contain embedded strings. This improves the alignability of the info structures and allows for OEM flexibility in the core functions. This does not change the feel of the LAN Manager 2.x functions that support string pointers. Code porting merely requires a pointer assignment rather than a string copy.

Any API information field that is returned in an enumeration call that can be subsequently used as a key for a GetInfo call is guaranteed to be present in the enumeration buffer. If the variable length information string that would specify the key field value will not fit then the entire fixed length structure for the entry is not returned. Other variable-length fields will be returned as a NULL pointer for the case where the string will not fit as with LAN Manager 2.x.

+$#A Enumeration Resume Handles

Enumeration resume handles will be identifiers for the actual resume key contained in the instance data for the function. This is required for security, interoperabilty and to simplify the caller code for the function.

If a NULL is passed for the pointer to the resume handle, then no handle is stored and the ennumeration search cannot be continued. This is useful in cases where the application does not want to enumerate all the items.

If an error is returned from an enumeration call, the resume handle must be treated as invalid and not used for any subsequent enumeration calls. For example, the enumeration should be restarted from the beginning.

+$#A User-Specific Information

The LAN Manager 2.x functions assumed that there would only be one user per computer. Therefore, some functions have that assumption built in, such as the NetSession functions. Since this assumption is not valid on Windows NT, the functions have had to be modified to take a user name as an additional parameter in order to be able to uniquely identify the information being queried or set.

+$#A Enumeration Functions

The enumeration functions all return the number of entries read, the total entries that could have been returned if the buffer was big enough, a buffer with the entries, and a return code. The following table details what an application can expect for the various parameters (M is less than N):

Condition | entriesread | totalentries | buffer | returncode
No entries | 0 | 0 | NULL | SUCCESS
>= 1 entry | N | N | NOT NULL | SUCCESS
>= 1 entry | M | N | NOT NULL ERROR_MORE_DATA |
Failure | ? | ? | ? | Error Code

The caller must first examine the returned status before proceeding to examine any of the other parameters for data.

Note that "totalentries" is the total number of entries that would have been returned if the return buffer was big enough. For example, the total number of entries that were available for entries, using the current resume handle, if any, when the function was called. The one exception to this occurs in the NetUserEnum function when receiving data from a downlevel (OS/2 LANMAN) computer. In this case, the total entries always reflects the total number of entries in the database, no matter where it is in the resume sequence.

+$#A SetInfo Functions

All the SetInfo functions take a structure that contains the new information to be set. If any of the pointer fields in the structure are NULL pointers, then the corresponding field is not changed by the call. This is consistent with LAN Manager 2.x functionality.

In addition, the information levels that allow GUEST access to query information do not allow callers with GUEST access to set the fields. For example, a caller with GUEST that calls the SetInfo function will probably get an error back from the SetInfo call.

+$#A Windows Networking Functions

Function Status

Windows networking functions will use the LAN Manager 2.x error reporting convention. For example, a successful function call returns 0, a nonzero return code indicates an error in the range of the LAN Manager 2.x API/OS2 errors.

An extended error range will be added for Windows NT specific errors.

Since the Windows networking functions will use RPC, the API error definition will also be extended to include RPC error codes.

+$#A RPC Buffer Allocation Errors

Since the RPC run time will allocate memory for send and receive buffers, the function should expect to see RPC allocation errors. In the event of an RPC allocation error, a resumable API handle is invalidated. This is a requirement since resumable functions are not rewindable.

+$#A Obsolete Information Fields

Many of the information fields in the core information structures will be obsolete in the Windows NT service implementation. These fields will remain in the information structure for compatibility with 16-bit versions of Windows and will be returned with an intelligent default on Windows NT systems.

+$#A NLS Support

There are only Unicode versions of the Windows networking functions. If you use these functions, you MUST define Unicode in your source file before including any of the LM include files.

+$#A Windows Networking Function Definitions

The Windows networking functions are 'stdcall' calling convention functions that return a DWORD API status:

#define NET_API_STATUS DWORD

#define NET_API_FUNCTION __stdcall

+$#A Buffer Manipulation Functions

For remotable functions that return information to the caller, the RPC run time will allocate the buffer containing the return information. When the caller has finished processing the returned information, NetApiBufferFree must be called. Additional buffer manipulation functions are also supported.

The buffer manipulation functions are:

NetApiBufferAllocate

NetApiBufferFree

NetApiBufferReallocate

NetApiBufferSize

+$#A Service Functions

Note A complete set of service functions are provided in the Win32 API. These should be used in place of the NetService functions, unless you have a requirement to control services on a downlevel server. For more details, see Services.

Service functions control services. A service is an application that an administrator can control using the Service Controller interfaces.

Services allow administrators to control applications on the network and maintain the integrity of users' data. On a typical network, applications are shared by many users. If an administrator terminates an application running on a server, a user who has not finished working with that application can lose important data. When an application is implemented as a service, the service controller checks the status before changing the state of the service. Windows NT Networking provides several standard services, such as the Workstation, Server, and Messenger services.

A service can be started using the service functions. At startup time, the service defines whether it can be stopped, paused, and continued.

NetServiceControl controls the operations of network services, and it can provide time hints to controlling applications. NetServiceEnum retrieves information about all started services. NetServiceGetInfo retrieves information about a particular started service. NetServiceInstall starts a network service. NetServiceStatus sets status and code information for a network service.

The service functions provide service information at three levels:

typedef struct _SERVICE_INFO_0 {
LPWSTR svci0_name; /* service name */
} SERVICE_INFO_0, *PSERVICE_INFO_0, *LPSERVICE_INFO_0;

typedef struct _SERVICE_INFO_1 {
LPWSTR svci1_name;
DWORD svci1_status;
DWORD svci1_code;
DWORD svci1_pid;
} SERVICE_INFO_1, *PSERVICE_INFO_1, *LPSERVICE_INFO_1;

typedef struct _SERVICE_INFO_2 {
LPWSTR svci2_name;
DWORD svci2_status;
DWORD svci2_code;
DWORD svci2_pid;
LPWSTR svci2_text;

DWORD svci2_specific_error;
} SERVICE_INFO_2, *PSERVICE_INFO_2, *LPSERVICE_INFO_2;

The service functions are:

NetServiceControl

NetServiceEnum

NetServiceGetInfo

NetServiceInstall

+$#A Server Functions

Server functions perform administrative tasks on a local or remote server. Any user or application with admin group membership on a local or remote server can perform administrative tasks on that server to control its operation, user access, and resource sharing. The low-level parameters that affect a server's operation can be examined and modified by calling NetServerGetInfo and NetServerSetInfo.

The LAN Manager 2.x server API included several fields that logically belong to other LAN Manager services and core Windows NT components. For this reason the server information levels available in LAN Manager 2.x are no longer available in Windows networking functions. The server specific information is available in 3 levels starting at base level 100.

+$#A Server System Information

Level 100 available with guest access:

typedef struct _SERVER_INFO_100 {
DWORD sv100_platform_id;
LPWSTR sv100_name;
} SERVER_INFO_100, *PSERVER_INFO_100, *LPSERVER_INFO_100;

Level 101 available with User access:

typedef struct _SERVER_INFO_101 {
DWORD sv101_platform_id;
LPWSTR sv101_name;
DWORD sv101_version_major;
DWORD sv101_version_minor;
DWORD sv101_type;
LPWSTR sv101_comment;
} SERVER_INFO_101, *PSERVER_INFO_101, *LPSERVER_INFO_101;

Level 102 available with Admin access:

typedef struct SERVER_INFO_102 {
DWORD sv102_platform_id;
LPWSTR sv102_name;
DWORD sv102_version_major;
DWORD sv102_version_minor;
DWORD sv102_type;
LPWSTR sv102_comment;
DWORD sv102_users;
DWORD sv102_disc;
BOOL sv102_hidden;
DWORD sv102_announce;
DWORD sv102_anndelta;
LPWSTR sv102_userpath;
} SERVER_INFO_102, *PSERVER_INFO_102, *LPSERVER_INFO_102;

Level 400-499 OS/2 Server information.

Level 402 available with Admin access:

typedef struct _SERVER_INFO_402 {
DWORD sv402_ulist_mtime;
DWORD sv402_glist_mtime;
DWORD sv402_alist_mtime;
LPWSTR sv402_alerts;
DWORD sv402_security;
DWORD sv402_numadmin;
DWORD sv402_lanmask;
LPWSTR sv402_guestacct;
DWORD sv402_chdevs;
DWORD sv402_chdevq;
DWORD sv402_chdevjobs;
DWORD sv402_connections;
DWORD sv402_shares;
DWORD sv402_openfiles;
DWORD sv402_sessopens;
DWORD sv402_sessvcs;
DWORD sv402_sessreqs;
DWORD sv402_opensearch;
DWORD sv402_activelocks;
DWORD sv402_numreqbuf;
DWORD sv402_sizreqbuf;
DWORD sv402_numbigbuf;
DWORD sv402_numfiletasks;
DWORD sv402_alertsched;
DWORD sv402_erroralert;
DWORD sv402_logonalert;
DWORD sv402_accessalert;
DWORD sv402_diskalert;
DWORD sv402_netioalert;
DWORD sv402_maxauditsz;
LPWSTR sv402_srvheuristics;
} SERVER_INFO_402, *PSERVER_INFO_402, *LPSERVER_INFO_402;

Level 403 available with Admin access:

typedef struct _SERVER_INFO_403 {
DWORD sv403_ulist_mtime;
DWORD sv403_glist_mtime;
DWORD sv403_alist_mtime;
LPWSTR sv403_alerts;
DWORD sv403_security;
DWORD sv403_numadmin;
DWORD sv403_lanmask;
LPWSTR sv403_guestacct;
DWORD sv403_chdevs;
DWORD sv403_chdevq;
DWORD sv403_chdevjobs;
DWORD sv403_connections;
DWORD sv403_shares;
DWORD sv403_openfiles;
DWORD sv403_sessopens;
DWORD sv403_sessvcs;
DWORD sv403_sessreqs;
DWORD sv403_opensearch;
DWORD sv403_activelocks;
DWORD sv403_numreqbuf;
DWORD sv403_sizreqbuf;
DWORD sv403_numbigbuf;
DWORD sv403_numfiletasks;
DWORD sv403_alertsched;
DWORD sv403_erroralert;
DWORD sv403_logonalert;
DWORD sv403_accessalert;
DWORD sv403_diskalert;
DWORD sv403_netioalert;
DWORD sv403_maxauditsz;
LPWSTR sv403_srvheuristics;
DWORD sv403_auditedevents;
DWORD sv403_autoprofile;
LPWSTR sv403_autopath;
} SERVER_INFO_403, *PSERVER_INFO_403, *LPSERVER_INFO_403;

Level 500-599 Windows NT Server information.

Level 502 available with Admin access:

typedef struct _SERVER_INFO_502 {
DWORD sv502_sessopens;
DWORD sv502_sessvcs;
DWORD sv502_opensearch;
DWORD sv502_sizreqbuf;
DWORD sv502_initworkitems;
DWORD sv502_maxworkitems;
DWORD sv502_rawworkitems;
DWORD sv502_irpstacksize;
DWORD sv502_maxrawbuflen;
DWORD sv502_sessusers;
DWORD sv502_sessconns;
DWORD sv502_maxpagedmemoryusage;
DWORD sv502_maxnonpagedmemoryusage;
BOOL sv502_enablesoftcompat;
BOOL sv502_enableforcedlogoff;
BOOL sv502_timesource;
BOOL sv502_acceptdownlevelapis;
BOOL sv502_lmannounce;
} SERVER_INFO_502, *PSERVER_INFO_502, *LPSERVER_INFO_502;

Level 503 available with Admin access:

typedef struct _SERVER_INFO_503 {
DWORD sv503_sessopens;
DWORD sv503_sessvcs;
DWORD sv503_opensearch;
DWORD sv503_sizreqbuf;
DWORD sv503_initworkitems;
DWORD sv503_maxworkitems;
DWORD sv503_rawworkitems;
DWORD sv503_irpstacksize;
DWORD sv503_maxrawbuflen;
DWORD sv503_sessusers;
DWORD sv503_sessconns;
DWORD sv503_maxpagedmemoryusage;
DWORD sv503_maxnonpagedmemoryusage;
BOOL sv503_enablesoftcompat;
BOOL sv503_enableforcedlogoff;
BOOL sv503_timesource;
BOOL sv503_acceptdownlevelapis;
BOOL sv503_lmannounce;
LPTSTR sv503_domain;
DWORD sv503_maxcopyreadlen;
DWORD sv503_maxcopywritelen;
DWORD sv503_minkeepsearch;
DWORD sv503_maxkeepsearch;
DWORD sv503_minkeepcomplsearch;
DWORD sv503_maxkeepcomplsearch;
DWORD sv503_threadcountadd;
DWORD sv503_numblockthreads;
DWORD sv503_scavtimeout;
DWORD sv503_minrcvqueue;
DWORD sv503_minfreeworkitems;
DWORD sv503_xactmemsize;
DWORD sv503_threadpriority;
DWORD sv503_maxmpxct;
DWORD sv503_oplockbreakwait;
DWORD sv503_oplockbreakresponsewait;
BOOL sv503_enableoplocks;
BOOL sv503_enableoplockforceclose;
BOOL sv503_enablefcbopens;
BOOL sv503_enableraw;
BOOL sv503_enabledpc;
BOOL sv503_enablemdlio;
BOOL sv503_enablefastio;
} SERVER_INFO_503, *PSERVER_INFO_503, *LPSERVER_INFO_503;

The following information levels are only valid for NetServerSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _SERVER_INFO_1005 {
DWORD sv1005_comment;
} SERVER_INFO_1005, *PSERVER_INFO_1005, *LPSERVER_INFO_1005;

typedef struct _SERVER_INFO_1107 {
DWORD sv1107_users;
} SERVER_INFO_1107, *PSERVER_INFO_1107, *LPSERVER_INFO_1107;

typedef struct _SERVER_INFO_1010 {
DWORD sv1010_disc;
} SERVER_INFO_1010, *PSERVER_INFO_1010, *LPSERVER_INFO_1010;

typedef struct _SERVER_INFO_1016 {
BOOL sv1016_hidden;
} SERVER_INFO_1016, *PSERVER_INFO_1016, *LPSERVER_INFO_1016;

typedef struct _SERVER_INFO_1017 {
DWORD sv1017_announce;
} SERVER_INFO_1017, *PSERVER_INFO_1017, *LPSERVER_INFO_1017;

typedef struct _SERVER_INFO_1018 {
DWORD sv1018_anndelta;
} SERVER_INFO_1018, *PSERVER_INFO_1018, *LPSERVER_INFO_1018;

typedef struct _SERVER_INFO_1037 {
DWORD sv1037_alertsched;
} SERVER_INFO_1037, *PSERVER_INFO_1037, *LPSERVER_INFO_1037;

typedef struct _SERVER_INFO_1038 {
DWORD sv1038_erroralert;
} SERVER_INFO_1038, *PSERVER_INFO_1038, *LPSERVER_INFO_1038;

typedef struct _SERVER_INFO_1039 {
DWORD sv1039_logonalert;
} SERVER_INFO_1039, *PSERVER_INFO_1039, *LPSERVER_INFO_1039;

typedef struct _SERVER_INFO_1040 {
DWORD sv1040_accessalert;
} SERVER_INFO_1040, *PSERVER_INFO_1040, *LPSERVER_INFO_1040;

typedef struct _SERVER_INFO_1041 {
DWORD sv1041_diskalert;
} SERVER_INFO_1041, *PSERVER_INFO_1041, *LPSERVER_INFO_1041;

typedef struct _SERVER_INFO_1042 {
DWORD sv1042_netioalert;
} SERVER_INFO_1042, *PSERVER_INFO_1042, *LPSERVER_INFO_1042;

typedef struct _SERVER_INFO_1043 {
DWORD sv1043_maxauditsz;
} SERVER_INFO_1043, *PSERVER_INFO_1043, *LPSERVER_INFO_1043;

The following do not do anything on downlevel systems (for example, LAN Manager 2.x) since they cannot be modified on the fly on those systems:

typedef struct _SERVER_INFO_1009 {
DWORD sv1009_users;
} SERVER_INFO_1009, *PSERVER_INFO_1009, *LPSERVER_INFO_1009;

typedef struct _SERVER_INFO_1021 {
LPWSTR sv1021_userpath;
} SERVER_INFO_1021, *PSERVER_INFO_1021, *LPSERVER_INFO_1021;

typedef struct _SERVER_INFO_1022 {
DWORD sv1022_chdevs;
} SERVER_INFO_1022, *PSERVER_INFO_1022, *LPSERVER_INFO_1022;

typedef struct _SERVER_INFO_1028 {
DWORD sv1028_sessopens;
} SERVER_INFO_1028, *PSERVER_INFO_1028, *LPSERVER_INFO_1028;

typedef struct _SERVER_INFO_1029 {
DWORD sv1029_opensearch;
} SERVER_INFO_1029, *PSERVER_INFO_1029, *LPSERVER_INFO_1029;

The following are not supported on downlevel systems (for example, LAN Manager 2.x) since these are Windows NT − only fields:

typedef struct _SERVER_INFO_1001 {
DWORD sv1001_maxworkitems;
} SERVER_INFO_1001, *PSERVER_INFO_1001, *LPSERVER_INFO_1001;

typedef struct _SERVER_INFO_1002 {
DWORD sv1002_maxrawbuflen;
} SERVER_INFO_1002, *PSERVER_INFO_1002, *LPSERVER_INFO_1002;

typedef struct _SERVER_INFO_1003 {
DWORD sv1003_sessusers;
} SERVER_INFO_1003, *PSERVER_INFO_1003, *LPSERVER_INFO_1003;

typedef struct _SERVER_INFO_1004 {
DWORD sv1004_sesscons;
} SERVER_INFO_1004, *PSERVER_INFO_1004, *LPSERVER_INFO_1004;

typedef struct _SERVER_INFO_1006 {
DWORD sv1006_enablesoftcompat;
} SERVER_INFO_1006, *PSERVER_INFO_1006, *LPSERVER_INFO_1006;

For NetServerSetInfo, parmnum values refer to the fields in the _SERVER_INFO structures as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in server_info struct
SV_NAME_PARMNUM | sv_name
SV_VERSION_MAJOR_PARMNUM | sv_version_major
SV_VERSION_MINOR_PARMNUM | sv_version_minor
SV_TYPE_PARMNUM | sv_type
SV_COMMENT_PARMNUM | sv_comment
SV_USERS_PARMNUM | sv_users
SV_DISC_PARMNUM | sv_disc
SV_HIDDEN_PARMNUM | sv_hidden
SV_ANNOUNCE_PARMNUM | sv_announce
SV_ANNDELTA_PARMNUM | sv_anndelta
SV_USERPATH_PARMNUM | sv_userpath
SV_ULIST_MTIME_PARMNUM | sv_ulist_mtime
SV_GLIST_MTIME_PARMNUM | sv_glist_mtime
SV_ALIST_MTIME_PARMNUM | sv_alist_mtime
SV_ALERTS_PARMNUM | sv_alerts
SV_SECURITY_PARMNUM | sv_security
SV_NUMADMIN_PARMNUM | sv_numadmin
SV_LANMASK_PARMNUM | sv_lanmask
SV_GUESTACC_PARMNUM | sv_guestacc
SV_CHDEVS_PARMNUM | sv_chdevs
SV_CHDEVQ_PARMNUM | sv_chdevq
SV_CHDEVJOBS_PARMNUM | sv_chdevjobs
SV_CONNECTIONS_PARMNUM | sv_connections
SV_SHARES_PARMNUM | sv_shares
SV_OPENFILES_PARMNUM | sv_openfiles
SV_SESSOPENS_PARMNUM | sv_sessopens
SV_SESSVCS_PARMNUM | sv_sessvcs
SV_SESSREQS_PARMNUM | sv_sessreqs
SV_OPENSEARCH_PARMNUM | sv_opensearch
SV_ACTIVELOCKS_PARMNUM | sv_activelocks
SV_NUMREQBUF_PARMNUM | sv_numreqbuf
SV_SIZREQBUF_PARMNUM | sv_sizreqbuf
SV_NUMBIGBUF_PARMNUM | sv_numbigbuf
SV_NUMFILETASKS_PARMNUM | sv_numfiletasks
SV_ALERTSCHED_PARMNUM | sv_alertsched
SV_ERRORALERT_PARMNUM | sv_erroralert
SV_LOGONALERT_PARMNUM | sv_logonalert
SV_ACCESSALERT_PARMNUM | sv_accessalert
SV_DISKALERT_PARMNUM | sv_diskalert
SV_NETIOALERT_PARMNUM | sv_netioalert
SV_MAXAUDITSZ_PARMNUM | sv_maxauditsz
SV_SRVHEURISTICS_PARMNUM | sv_srvheuristics
SV_AUDITEDEVENTS_PARMNUM | sv_auditedevents
SV_AUTOPROFILE_PARMNUM | sv_autoprofile
SV_MAXWORKITEMS_PARMNUM | sv_maxworkitems
SV_RAWWORKITEMS_PARMNUM | sv_rawworkitems
SV_IRPSTACKSIZE_PARMNUM | sv_irpstacksize
SV_SESSUSERS_PARMNUM | sv_sessusers
SV_SESSCONNS_PARMNUM | sv_sessconns
SV_MAXNONPAGEDMEMORYUSAGE_PARMNUM | sv_maxnonpagedmemoryusage
SV_MAXPAGEDMEMORYUSAGE_PARMNUM | sv_maxpagedmmeoryusage
SV_ENABLEOFTCOMPAT_PARMNUM | sv_enablesoftcompat

The server functions are:

NetServerGetInfo

NetServerSetInfo.

+$#A Workstation and Workstation User Functions

Workstation functions perform administrative tasks on a local or remote workstation. Any user or application with admin group membership on a local or remote server can perform administrative tasks on that workstation to control its operation, user access, and resource sharing. The low-level parameters that affect a workstation's operation can be examined and modified by calling NetWkstaGetInfo and NetWkstaSetInfo.

The workstation API data structures are restructured from those of LAN Man 2.x to allow the information to be grouped by type and security accesses. The LAN Manager 2.x workstation information format is discontinued due to the following problems:

• The base level (0 and 1) were not grouped by accessibility such that a non superset level (level 10) was required to allow guest access to the information.

• Platform specific implementation information was included in the base levels such that every platform had to return all information including a default for non-relevant fields. This grew the size of the information structures unneccessarily, making the function cumbersome to use.

The workstation functions allow access to two discrete groups of workstation information:

• System information.

• Platform specific information (Windows NT, OS/2, MS-DOS, and so on)

The Workstation User functions allow access to user-specific information. The user-specific information is separated from the workstation information because there can be more than one user on a workstation.

Within each group the fields are categorized by security access such that the guest accessible fields are a subset of the user accessible fields that are a subset of the admin accessible fields.

The system information structure contains a platform base number that identifies the levels and format of the platform specific information structures.

The workstation function information structures are:

System Information −

Level 100 available with guest access −

typedef struct _WKSTA_INFO_100 {
DWORD wki100_platform_id;
LPWSTR wki100_computername;
LPWSTR wki100_langroup;
DWORD wki100_ver_major;
DWORD wki100_ver_minor;
} WKSTA_INFO_100, *PWKSTA_INFO_100, *LPWKSTA_INFO_100;

Level 101 available with User access:

typedef struct _WKSTA_INFO_101 {
DWORD wki101_platform_id;
LPWSTR wki101_computername;
LPWSTR wki101_langroup;
DWORD wki101_ver_major;
DWORD wki101_ver_minor;
LPWSTR wki101_lanroot;
} WKSTA_INFO_101, *PWKSTA_INFO_101, *LPWKSTA_INFO_101;

Level 102 available with Admin access:

typedef struct _WKSTA_INFO_102 {
DWORD wki102_platform_id;
LPWSTR wki102_computername;
LPWSTR wki102_langroup;
DWORD wki102_ver_major;
DWORD wki102_ver_minor;
LPWSTR wki102_lanroot;
DWORD wki102_logged_on_users;
} WKSTA_INFO_102, *PWKSTA_INFO_102, *LPWKSTA_INFO_102;

Platform Specific Information

Level 300-399 MS-DOS Workstation information.

Level 300 available with guest access:

Level 302 available with Admin access:

typedef struct _WKSTA_INFO_302 {
DWORD wki302_char_wait;
DWORD wki302_collection_time;
DWORD wki302_maximum_collection_count;
DWORD wki302_keep_conn;
DWORD wki302_keep_search;
DWORD wki302_max_cmds;
DWORD wki302_num_work_buf;
DWORD wki302_siz_work_buf;
DWORD wki302_max_wrk_cache;
DWORD wki302_sess_timeout;
DWORD wki302_siz_error;
DWORD wki302_num_alerts;
DWORD wki302_num_services;
DWORD wki302_errlog_sz;
DWORD wki302_print_buf_time;
DWORD wki302_num_char_buf;
DWORD wki302_siz_char_buf;
LPWSTR wki302_wrk_heuristics;
DWORD wki302_mailslots;
DWORD wki302_num_dgram_buf;
} WKSTA_INFO_302, *PWKSTA_INFO_302, *LPWKSTA_INFO_302;

Level 400-499 OS/2 Workstation information.

Level 402 available with Admin access:

typedef struct _WKSTA_INFO_402 {
DWORD wki402_char_wait;
DWORD wki402_collection_time;
DWORD wki402_maximum_collection_count;
DWORD wki402_keep_conn;
DWORD wki402_keep_search;
DWORD wki402_max_cmds;
DWORD wki402_num_work_buf;
DWORD wki402_siz_work_buf;
DWORD wki402_max_wrk_cache;
DWORD wki402_sess_timeout;
DWORD wki402_siz_error;
DWORD wki402_num_alerts;
DWORD wki402_num_services;
DWORD wki402_errlog_sz;
DWORD wki402_print_buf_time;
DWORD wki402_num_char_buf;
DWORD wki402_siz_char_buf;
LPWSTR wki402_wrk_heuristics;
DWORD wki402_mailslots;
DWORD wki402_num_dgram_buf;
DWORD wki402_max_threads;
} WKSTA_INFO_402, *PWKSTA_INFO_402, *LPWKSTA_INFO_402;

Level 500-599 Windows NT Workstation information.

Level 502 available with Admin access:

typedef struct _WKSTA_INFO_502 {
DWORD wki502_char_wait;
DWORD wki502_collection_time;
DWORD wki502_maximum_collection_count;
DWORD wki502_keep_conn;
DWORD wki502_max_cmds;
DWORD wki502_sess_timeout;
DWORD wki502_siz_char_buf;
DWORD wki502_max_threads;
DWORD wki502_lock_quota;
DWORD wki502_lock_increment;
DWORD wki502_lock_maximum;
DWORD wki502_pipe_increment;
DWORD wki502_pipe_maximum;
DWORD wki502_cache_file_timeout;
DWORD wki502_dormant_file_limit;
DWORD wki502_read_ahead_throughput;
DWORD wki502_num_mailslot_buffers;
DWORD wki502_num_srv_announce_buffers;
DWORD wki502_dgreceiver_threads;
BOOL wki502_use_opportunistic_locking;
BOOL wki502_use_unlock_behind;
BOOL wki502_use_close_behind;
BOOL wki502_buf_named_pipes;
BOOL wki502_use_lock_read_unlock;
BOOL wki502_utilize_nt_caching;
BOOL wki502_use_raw_read;
BOOL wki502_use_raw_write;
BOOL wki502_use_write_raw_data;
BOOL wki502_use_encryption;
BOOL wki502_buf_files_deny_write;
BOOL wki502_buf_read_only_files;
BOOL wki502_force_core_create_mode;
BOOL wki502_use_512_byte_max_transfer;
} WKSTA_INFO_502, *PWKSTA_INFO_502, *LPWKSTA_INFO_502;

This format of workstation information allows for a more logical future extension. When new information is to be added to any set of guest,user and admin information a new set of levels is created to include the current infromation plus the new information.

The following information levels are only valid for NetWkstaSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _WKSTA_INFO_1010 {
DWORD wki1010_char_wait;
} WKSTA_INFO_1010, *PWKSTA_INFO_1010, *LPWKSTA_INFO_1010;

typedef struct _WKSTA_INFO_1011 {
DWORD wki1011_collection_time;
} WKSTA_INFO_1011, *PWKSTA_INFO_1011, *LPWKSTA_INFO_1011;

typedef struct _WKSTA_INFO_1012 {
DWORD wki1012_maximum_collection_count;
} WKSTA_INFO_1012, *PWKSTA_INFO_1012, *LPWKSTA_INFO_1012;

typedef struct _WKSTA_INFO_1027 {
DWORD wki1027_errlog_sz;
} WKSTA_INFO_1027, *PWKSTA_INFO_1027, *LPWKSTA_INFO_1027;

typedef struct _WKSTA_INFO_1028 {
DWORD wki1028_print_buf_time;
} WKSTA_INFO_1028, *PWKSTA_INFO_1028, *LPWKSTA_INFO_1028;

typedef struct _WKSTA_INFO_1032 {
DWORD wki1032_wrk_heuristics;
} WKSTA_INFO_1032, *PWKSTA_INFO_1032, *LPWKSTA_INFO_1032;

typedef struct _WKSTA_INFO_1035 {
LPWSTR wki1035_oth_domains;
} WKSTA_INFO_1035, *PWKSTA_INFO_1035, *LPWKSTA_INFO_1035;

The following do nothing on downlevel systems (for example, LAN Manager 2.x) since these fields cannot be set on them:

typedef struct _WKSTA_INFO_1013 {
DWORD wki1013_keep_conn;
} WKSTA_INFO_1013, *PWKSTA_INFO_1013, *LPWKSTA_INFO_1013;

typedef struct _WKSTA_INFO_1018 {
DWORD wki1018_sess_timeout;
} WKSTA_INFO_1018, *PWKSTA_INFO_1018, *LPWKSTA_INFO_1018;

typedef struct _WKSTA_INFO_1023 {
DWORD wki1023_siz_char_buf;
} WKSTA_INFO_1023, *PWKSTA_INFO_1023, *LPWKSTA_INFO_1023;

typedef struct _WKSTA_INFO_1033 {
DWORD wki1033_max_threads;
} WKSTA_INFO_1033, *PWKSTA_INFO_1033, *LPWKSTA_INFO_1033;

The following are not supported on downlevel systems (for example, LAN Manager 2.x):

typedef struct _WKSTA_INFO_1041 {
DWORD wki1041_lock_quota;
} WKSTA_INFO_1041, *PWKSTA_INFO_1041, *LPWKSTA_INFO_1041;

typedef struct _WKSTA_INFO_1042 {
DWORD wki1042_lock_increment;
} WKSTA_INFO_1042, *PWKSTA_INFO_1042, *LPWKSTA_INFO_1042;

typedef struct _WKSTA_INFO_1043 {
DWORD wki1043_lock_maximum;
} WKSTA_INFO_1043, *PWKSTA_INFO_1043, *LPWKSTA_INFO_1043;

typedef struct _WKSTA_INFO_1044 {
DWORD wki1044_pipe_increment;
} WKSTA_INFO_1044, *PWKSTA_INFO_1044, *LPWKSTA_INFO_1044;

typedef struct _WKSTA_INFO_1045 {
DWORD wki1045_pipe_maximum;
} WKSTA_INFO_1045, *PWKSTA_INFO_1045, *LPWKSTA_INFO_1045;

typedef struct _WKSTA_INFO_1046 {
DWORD wki1046_dormant_file_limit;
} WKSTA_INFO_1046, *PWKSTA_INFO_1046, *LPWKSTA_INFO_1046;

typedef struct _WKSTA_INFO_1047 {
DWORD wki1047_cache_file_timeout;
} WKSTA_INFO_1047, *PWKSTA_INFO_1047, *LPWKSTA_INFO_1047;

typedef struct _WKSTA_INFO_1048 {
BOOL wki1048_use_opportunity_locking;
} WKSTA_INFO_1048, *PWKSTA_INFO_1048, *LPWKSTA_INFO_1048;

typedef struct _WKSTA_INFO_1049 {
BOOL wki1049_use_unlock_behind;
} WKSTA_INFO_1049, *PWKSTA_INFO_1049, *LPWKSTA_INFO_1049;

typedef struct _WKSTA_INFO_1050 {
BOOL wki1050_use_close_behind;
} WKSTA_INFO_1050, *PWKSTA_INFO_1050, *LPWKSTA_INFO_1050;

typedef struct _WKSTA_INFO_1051 {
BOOL wki1051_buf_named_pipes;
} WKSTA_INFO_1051, *PWKSTA_INFO_1051, *LPWKSTA_INFO_1051;

typedef struct _WKSTA_INFO_1052 {
BOOL wki1052_use_lock_read_unlock;
} WKSTA_INFO_1052, *PWKSTA_INFO_1052, *LPWKSTA_INFO_1052;

typedef struct _WKSTA_INFO_1053 {
BOOL wki1053_utilize_nt_caching;
} WKSTA_INFO_1053, *PWKSTA_INFO_1053, *LPWKSTA_INFO_1053;

typedef struct _WKSTA_INFO_1054 {
BOOL wki1054_use_raw_read;
} WKSTA_INFO_1054, *PWKSTA_INFO_1054, *LPWKSTA_INFO_1054;

typedef struct _WKSTA_INFO_1055 {
BOOL wki1055_use_raw_write;
} WKSTA_INFO_1055, *PWKSTA_INFO_1055, *LPWKSTA_INFO_1055;

typedef struct _WKSTA_INFO_1056 {
BOOL wki1056_use_write_raw_data;
} WKSTA_INFO_1056, *PWKSTA_INFO_1056, *LPWKSTA_INFO_1056;

typedef struct _WKSTA_INFO_1057 {
BOOL wki1057_use_encryption;
} WKSTA_INFO_1057, *PWKSTA_INFO_1057, *LPWKSTA_INFO_1057;

typedef struct _WKSTA_INFO_1058 {
BOOL wki1058_buf_files_deny_write;
} WKSTA_INFO_1058, *PWKSTA_INFO_1058, *LPWKSTA_INFO_1058;

typedef struct _WKSTA_INFO_1059 {
BOOL wki1059_buf_read_only_files;
} WKSTA_INFO_1059, *PWKSTA_INFO_1059, *LPWKSTA_INFO_1059;

typedef struct _WKSTA_INFO_1060 {
BOOL wki1060_force_core_create_mode;
} WKSTA_INFO_1060, *PWKSTA_INFO_1060, *LPWKSTA_INFO_1060;

typedef struct _WKSTA_INFO_1061 {
BOOL wki1061_use_512_byte_max_transfer;
} WKSTA_INFO_1061, *PWKSTA_INFO_1061, *LPWKSTA_INFO_1061;

typedef struct _WKSTA_INFO_1062 {
DWORD wki1062_read_ahead_throughput;
} WKSTA_INFO_1062, *PWKSTA_INFO_1062, *LPWKSTA_INFO_1062;

The Workstation User function information structures are:

Level 0 and 1 available with User access:

typedef struct _WKSTA_USER_INFO_0 {
LPWSTR wkui0_username;
} WKSTA_USER_INFO_0, *PWKSTA_USER_INFO_0, *LPWKSTA_USER_INFO_0;

typedef struct _WKSTA_USER_INFO_1 {
LPWSTR wkui1_username;
LPWSTR wkui1_logon_domain;
LPWSTR wkui1_logon_server;

LPWSTR wkui1_oth_domains;
} WKSTA_USER_INFO_1, *PWKSTA_USER_INFO_1, *LPWKSTA_USER_INFO_1;

typedef struct _WKSTA_USER_INFO_1101 {
LPTSTR wkui1101_oth_domains;
} WKSTA_USER_INFO_1101, *PWKSTA_USER_INFO_1101,
*LPWKSTA_USER_INFO_1101;

For NetWkstaSetInfo, parmnum values refer to the fields in the workstation info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in wksta_info struct
WKSTA_PLATFORM_ID_PARMNUM | wki_platform_id
WKSTA_COMPUTERNAME_PARMNUM | wki_computername
WKSTA_LANGROUP_PARMNUM | wki_langroup
WKSTA_OTH_DOMAINS_PARMNUM | wki_oth_domains
WKSTA_VER_MAJOR_PARMNUM | wki_ver_major
WKSTA_VER_MINOR_PARMNUM | wki_ver_minor
WKSTA_LOGGED_ON_USERS_PARMNUM | wki_logged_on_users
WKSTA_LANROOT_PARMNUM | wki_lanroot
WKSTA_LOGON_DOMAIN_PARMNUM | wki_logon_domain
WKSTA_LOGON_SERVER_PARMNUM | wki_logon_server
WKSTA_CHARWAIT_PARMNUM | wki_char_wait
WKSTA_CHARTIME_PARMNUM | wki_collection_time
WKSTA_CHARCOUNT_PARMNUM | wki_maximum_collection_count
WKSTA_KEEPCONN_PARMNUM | wki_keep_conn
WKSTA_KEEPSEARCH_PARMNUM | wki_keep_search
WKSTA_MAXCMDS_PARMNUM | wki_max_cmds
WKSTA_NUMWORKBUF_PARMNUM | wki_num_work_buf
WKSTA_MAXWRKCACHE_PARMNUM | wki_max_wrk_cache
WKSTA_SESSTIMEOUT_PARMNUM | wki_sess_timeout
WKSTA_SIZERROR_PARMNUM | wki_siz_error
WKSTA_NUMALERTS_PRAMNUM | wki_num_alerts
WKSTA_NUMSERVICES_PARMNUM | wki_num_services
WKSTA_ERRLOGSZ_PARMNUM | wki_errlog_sz
WKSTA_PRINTBUFTIME_PARMNUM | wki_print_buf_time
WKSTA_NUMCHARBUF_PARMNUM | wki_num_char_buf
WKSTA_SIZCHARBUF_PARMNUM | wki_siz_char_buf
WKSTA_WRKHEURISTICS_PARMNUM | wki_wrk_heuristics
WKSTA_MAILSLOTS_PRAMNUM | wki_mailslots
WKSTA_MAXTHREADS_PARMNUM | wki_max_threads
WKSTA_SIZWORKBUF_PARMNUM | wki_siz_work_buf
WKSTA_DORMANTTIMEOUT_PARMNUM | wki_dormant_timeout
WKSTA_LOCKQUOTA_PARMNUM | wki_lock_quota
WKSTA_LOCKINCREMENT_PARMNUM | wki_lock_increment
WKSTA_LOCKMAXIMUM_PARMNUM | wki_lock_maximum
WKSTA_PIPEINCREMENT_PRAMNUM | wki_pipe_increment
WKSTA_PIPEMAXIMUM_PARMNUM | wki_pipe_maximum
WKSTA_RAWREADTHRESHOLD_PARMNUM | wki_raw_read_threshold
WKSTA_USEOPLOCKING_PARMNUM | wki_use_opportunistic_locking
WKSTA_USEOPBATCH_PARMNUM | wki_use_op_batch
WKSTA_USEUNLOCKBEHIND_PARMNUM | wki_use_unlock_behind
WKSTA_USECLOSEBEHIND_PARMNUM | wki_use_close_behind
WKSTA_BUFNAMEDPIPES_PARMNUM | wksta_buf_named_pipes
WKSTA_USELOCKANDREADANDUNLOCK _PARMNUM | wki_use_lock_and_read_and _unlock
WKSTA_UTILIZENTCACHING_PARMNUM | wki_utilize_nt_caching
WKSTA_USERAWREAD_PARMNUM | wki_use_raw_read
WKSTA_USEWRITERAWWITHDATA_PARMNUM | wki_use_write_raw_with_data
WKSTA_USEENCRYPTION_PARMNUM | wki_use_encryption
WKSTA_BUFFILESWITHDENYWRITE _PARMNUM | wki_buf_files_with_deny_write
WKSTA_BUFREADONLYFILES_PRAMNUM | wki_buf_read_only_files
WKSTA_FORCECORECREATEMODE_PARMNUM | wki_force_core_create_mode
WKSTA_USE512BYTESMAXTRANSFER _PARMNUM | wki_use_512_bytes_max_transfer

The workstation functions are:

NetWkstaGetInfo

NetWkstaSetInfo

The Workstation User functions are:

NetWkstaUserEnum

NetWkstaUserGetInfo

NetWkstaUserSetInfo

+$#A Use Functions

Note The WNetAddConnection2 and WNetCancelConnection2 functions should be used instead of the NetUse functions.

Use functions examine or control connections (uses) between workstations and servers. Connections are distinguished from sessions: a session is established the first time a workstation makes a connection to a shared resource on the server; all further connections between the workstation and the server are part of this same session until the session ends. Two types of connections can be made: device-name connections (which can only be explicit) and universal-naming convention (UNC) connections (which can be explicit or implicit).

Connections are made on a per-user basis. A connection made by a user is deleted when that user logs off. For this reason the NetUse functions are local only, since a connection set up by a remote user would not be accessible to any other users, even the user that was interactively logged onto that computer.

NetUseAdd creates a device-name connection or an explicit UNC connection. Implicit UNC connections are made by the function responsible for the connection.

NetUseAdd establishes an explicit connection between the local computer and a resource shared on a server by redirecting a local device name to the share name of a remote server resource (\\servername\sharename). Once a device-name connection is made, users or applications can use the remote resource by specifying the local device name. To establish an implicit UNC connection, an application passes the share name of a resource to any function that accepts UNC paths. The function accepts the UNC name and makes a connection to the specified share name. All further requests on this connection require the full share name.

NetUseDel ends a connection to a shared resource. NetUseEnum enumerates all current connections between the local computer and resources on remote servers. NetUseGetInfo returns information about a connection to a shared resource.

The use functions are available at three information levels.

typedef struct _USE_INFO_0 {
LPWSTR ui0_local;
LPWSTR ui0_remote;
} USE_INFO_0, *PUSE_INFO_0, *LPUSE_INFO_0;

typedef struct _USE_INFO_1 {
LPWSTR ui1_local;
LPWSTR ui1_remote;
LPWSTR ui1_password;
DWORD ui1_status;
DWORD ui1_asg_type;
DWORD ui1_refcount;
DWORD ui1_usecount;
} USE_INFO_1, *PUSE_INFO_1, *LPUSE_INFO_1;

Information level 2 is not available if the function is remoted to a downlevel system ⎯ ERROR_NOT_SUPPORTED will be returned in that case.

typedef struct _USE_INFO_2 {
LPWSTR ui2_local;
LPWSTR ui2_remote;
LPWSTR ui2_password;
DWORD ui2_status;
DWORD ui2_asg_type;
DWORD ui2_refcount;
DWORD ui2_usecount;
LPWSTR ui2_username;

LPWSTR ui2_domainname;
} USE_INFO_2, *PUSE_INFO_2, *LPUSE_INFO_2;

The use functions are:

NetUseAdd

NetUseDel

NetUseEnum

NetUseGetInfo

+$#A Share Functions

Share functions control shared resources. A shared resource is a local resource on a server (for example, a disk directory, print device, or named pipe) that can be accessed by users and applications on the network.

NetShareAdd allows a user or application to share a resource of a specific type using the specified share name. NetShareAdd requires the share name and local device name to share the resource. A user or application must have an account on the server to access the resource.

You may also specify a Security Descriptor to be associated with a share, which specifies which users will be allowed to access files through this share, and with what type of access.

LAN Manager defines three types of special share names for interprocess communication (IPC) and remote administration of the server:

• IPC$, reserved for interprocess communication.

• ADMIN$, reserved for remote administration.

• A$, B$, C$ (and other local disk names followed by a dollar sign), assigned to local disk devices.

Share functions are available at four information levels.

typedef struct _SHARE_INFO_0 {
LPWSTR shi0_netname;
} SHARE_INFO_0, *PSHARE_INFO_0, *LPSHARE_INFO_0;

typedef struct _SHARE_INFO_1 {
LPWSTR shi1_netname;
DWORD shi1_type;
LPWSTR shi1_remark;
} SHARE_INFO_1, *PSHARE_INFO_1, *LPSHARE_INFO_1;

typedef struct _SHARE_INFO_2 {
LPWSTR shi2_netname;
DWORD shi2_type;
LPWSTR shi2_remark;
DWORD shi2_permissions;
DWORD shi2_max_uses;
DWORD shi2_current_uses;
LPWSTR shi2_path;
LPWSTR shi2_passwd;
} SHARE_INFO_2, *PSHARE_INFO_2, *LPSHARE_INFO_2;

typedef struct _SHARE_INFO_502 {
LPTSTR shi502_netname;
DWORD shi502_type;
LPTSTR shi502_remark;
DWORD shi502_permissions;
DWORD shi502_max_uses;
DWORD shi502_current_uses;
LPTSTR shi502_path;
LPTSTR shi502_passwd;
DWORD shi502_reserved;
PSECURITY_DESCRIPTOR shi502_security_descriptor;
} SHARE_INFO_502, *PSHARE_INFO_502, *LPSHARE_INFO_502;

The following information levels are valid only for NetShareSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _SHARE_INFO_1004 {
LPWSTR shi1004_remark;
} SHARE_INFO_1004, *PSHARE_INFO_1004, *LPSHARE_INFO_1004;

typedef struct _SHARE_INFO_1006 {
DWORD shi1006_max_uses;
} SHARE_INFO_1006, *PSHARE_INFO_1006, *LPSHARE_INFO_1006;

The following is only supported on Windows NT.

typedef struct _SHARE_INFO_1501 {
DWORD shi1501_reserved;
PSECURITY_DESCRIPTOR shi1501_security_descriptor;
} SHARE_INFO_1501, *PSHARE_INFO_1501, *LPSHARE_INFO_1501;

For NetShareSetInfo, parmnum values refer to the fields in the share_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in share_info struct
SHARE_NETNAME_PARMNUM | shi_netname
SHARE_TYPE_PARMNUM | shi_type
SHARE_REMARK_PARMNUM | shi_remark
SHARE_PERMISSIONS_PARMNUM | shi_permissions
SHARE_MAX_USES_PARMNUM | shi_max_uses
SHARE_CURRENT_USES_PARMNUM | shi_current_uses
SHARE_PATH_PARMNUM | shi_path
SHARE_PASSWD_PARMNUM | shi_passwd
SHARE_FILE_SD_PARMNUM | shi_security_descriptor

The share functions are:

NetShareAdd

NetShareCheck

NetShareDel

NetShareEnum

NetShareGetInfo

NetShareSetInfo

+$#A Session Functions

Session functions control network sessions established between workstations and servers. They require that the Server service be started on the specified server. A session is a link between a workstation and a server. It is established the first time a workstation makes a connection with a shared resource on the server. Until the session ends, all further connections between the workstation and the server are part of this same session. To end a session, an application on the server end of a connection calls NetSessionDel. This deletes all current connections between the workstation and the server. NetSessionEnum returns information about all sessions established for a server. NetSessionGetInfo returns information about a particular session.

Per-user information is managed by the NetSession functions through the use of the username parameter. Since there can be multiple users per session, this parameter is necessary in order to access the user-specific information for the session.

Session functions are available at four information levels.

typedef struct _SESSION_INFO_0 {
LPWSTR sesi0_cname;
} SESSION_INFO_0, *PSESSION_INFO_0, *LPSESSION_INFO_0;

typedef struct _SESSION_INFO_1 {
LPWSTR sesi1_cname;
LPWSTR sesi1_username;
DWORD sesi1_num_opens;
DWORD sesi1_time;
DWORD sesi1_idle_time;
DWORD sesi1__flags;
} SESSION_INFO_1, *PSESSION_INFO_1, *LPSESSION_INFO_1;

typedef struct _SESSION_INFO_2 {
LPWSTR sesi2_cname;
LPWSTR sesi2_username;
DWORD sesi2_num_opens;
DWORD sesi2_time;
DWORD sesi2_idle_time;
DWORD sesi2_flags;
LPWSTR sesi2_cltype_name;
} SESSION_INFO_2, *PSESSION_INFO_2, *LPSESSION_INFO_2;

typedef struct _SESSION_INFO_10 {
LPWSTR sesi10_cname;
LPWSTR sesi10_username;
DWORD sesi10_time;
DWORD sesi10_idle_time;
} SESSION_INFO_10, *PSESSION_INFO_10, *LPSESSION_INFO_10;

sesi_username cannot be NULL. sesi1_flags and sesi2_flags can take the following bits:

SESS_GUEST 0x00000001
SESS_NONENCRYPTION 0x00000002

The session functions are:

NetSessionDel

NetSessionEnum

NetSessionGetInfo

+$#A File Functions

File functions provide a way to monitor and close the file, device, and pipe resources open on a server. NetFileClose forces a server resource closed. This function can be used when a system error prevents normal closure. NetFileEnum returns information about resources open on a server. A file can be opened one or more times by one or more applications. Each file opening is uniquely identified. NetFileEnum returns an entry for each file opening. NetFileGetInfo returns information about one particular opening of a resource.

File functions are available at information levels 2 and 3 only. Levels 0 and 1 are not supported.

typedef struct _FILE_INFO_2 {
DWORD fi2_id;
} FILE_INFO_2, *PFILE_INFO_2, *LPFILE_INFO_2;

typedef struct _FILE_INFO_3 {
DWORD fi3_id;
DWORD fi3_permissions;
DWORD fi3_num_locks;
LPWSTR fi3_pathname;
LPWSTR fi3_username;
} FILE_INFO_3, *PFILE_INFO_3, *LPFILE_INFO_3;

The file functions are:

NetFileClose

NetFileEnum

NetFileGetInfo

+$#A Message Functions

Message functions send and receive messages and manipulate message aliases. A message is a buffer of text data sent to a user or application on the network. To receive a message, a user or application must register a message alias in a computer's table of message names. This can be done by using NetMessageNameAdd. A message name table contains a list of registered message aliases (users and applications) permitted to receive messages.

The aliases registered in the message name table are case insensitive. NetMessageNameDel deletes a specific message alias from the message name table. NetMessageNameEnum lists all the aliases stored in the message name table. NetMessageNameGetInfo retrieves information about a particular message alias in the message name table.

To send a message, an application calls NetMessageBufferSend. Applications can also send broadcast messages to all users in a domain or to all computers on a network using NetMessageBufferSend.

Message functions are available at two information levels.

MSG_INFO_1 exists only for compatability. The Windows NT messenger will not forward names or allow names to be forwarded to it.

typedef struct _MSG_INFO_0 {
LPWSTR msgi0_name;
} MSG_INFO_0, *PMSG_INFO_0, *LPMSG_INFO_0;

typedef struct _MSG_INFO_1 {
LPWSTR msgi1_name;
DWORD msgi1_forward_flag;
LPWSTR msgi1_forward;
} MSG_INFO_1, *PMSG_INFO_1, *LPMSG_INFO_1;

The message functions are:

NetMessageBufferSend

NetMessageNameAdd

NetMessageNameDel

NetMessageNameEnum

NetMessageNameGetInfo

+$#A Remote Utility Function

The remote utility function enables applications to access the time-of-day information on a remote server.

NetRemoteTOD returns time-of-day information from a remote server.

+$#A Security Account Functions

User Functions

User functions control a user's account in the security database. Each user or application that accesses resources must have an account in the security database. The Windows NT Security system (SAM) uses this user account to verify that the user or application has permission to use a resource. When a user or an application requests access to a resource, the security system checks for an appropriate user account or group account to permit the access.

NetUserEnum can be used to list all user accounts in a domain. An application can verify the groups to which a user belongs by calling NetUserGetGroups, which returns a list of global group names. The NetUserGetLocalGroups function does the same for local groups. When a user account is no longer needed, use NetUserDel to delete the account from the server. Once the account is removed, the user can no longer access the server, except by using the guest account. Because the user's password is confidential, it is not returned by NetUserEnum or NetUserGetInfo. The password is initially assigned when NetUserAdd is called. NetUserSetInfo sets the password and other elements of a user account.

User account information is available at five levels:

typedef struct _USER_INFO_0 {
LPWSTR usri0_name;
} USER_INFO_0, *PUSER_INFO_0, *LPUSER_INFO_0;

typedef struct _USER_INFO_1 {
LPWSTR usri1_name;
LPWSTR usri1_password;
DWORD usri1_password_age;
DWORD usri1_priv;
LPWSTR usri1_home_dir;
LPWSTR usri1_comment;
DWORD usri1_flags;
LPWSTR usri1_script_path;
} USER_INFO_1, *PUSER_INFO_1, *LPUSER_INFO_1;

typedef struct _USER_INFO_2 {
LPWSTR usri2_name;
LPWSTR usri2_password;
DWORD usri2_password_age;
DWORD usri2_priv;
LPWSTR usri2_home_dir;
LPWSTR usri2_comment;
DWORD usri2_flags;
LPWSTR usri2_script_path;
DWORD usri2_auth_flags;
LPWSTR usri2_full_name;
LPWSTR usri2_usr_comment;
LPWSTR usri2_parms;
LPWSTR usri2_workstations;
DWORD usri2_last_logon;
DWORD usri2_last_logoff;
DWORD usri2_acct_expires;
DWORD usri2_max_storage;
DWORD usri2_units_per_week;
LPBYTE usri2_logon_hours;
DWORD usri2_bad_pw_count;
DWORD usri2_num_logons;
LPWSTR usri2_logon_server;
DWORD usri2_country_code;
DWORD usri2_code_page;
} USER_INFO_2, *PUSER_INFO_2, *LPUSER_INFO_2;

typedef struct _USER_INFO_3 {
LPWSTR usri3_name;
LPWSTR usri3_password;
DWORD usri3_password_age;
DWORD usri3_priv;
LPWSTR usri3_home_dir;
LPWSTR usri3_comment;
DWORD usri3_flags;
LPWSTR usri3_script_path;
DWORD usri3_auth_flags;
LPWSTR usri3_full_name;
LPWSTR usri3_usr_comment;
LPWSTR usri3_parms;
LPWSTR usri3_workstations;
DWORD usri3_last_logon;
DWORD usri3_last_logoff;
DWORD usri3_acct_expires;
DWORD usri3_max_storage;
DWORD usri3_units_per_week;
PBYTE usri3_logon_hours;
DWORD usri3_bad_pw_count;
DWORD usri3_num_logons;
LPWSTR usri3_logon_server;
DWORD usri3_country_code;
DWORD usri3_code_page;
DWORD usri3_user_id;
DWORD usri3_primary_group_id;
LPWSTR usri3_profile;
LPWSTR usri3_home_dir_drive;
DWORD usri3_password_expired;
}USER_INFO_3, *PUSER_INFO_3, *LPUSER_INFO_3;

typedef struct _USER_INFO_10 {
LPWSTR usri10_name;
LPWSTR usri10_comment;
LPWSTR usri10_usr_comment;
LPWSTR usri10_full_name;
} USER_INFO_10, *PUSER_INFO_10, *LPUSER_INFO_10;

typedef struct _USER_INFO_11 {
LPWSTR usri11_name;
LPWSTR usri11_comment;
LPWSTR usri11_usr_comment;
LPWSTR usri11_full_name;
DWORD usri11_priv;
DWORD usri11_auth_flags;
DWORD usri11_password_age;
LPWSTR usri11_home_dir;
LPWSTR usri11_parms;
DWORD usri11_last_logon;
DWORD usri11_last_logoff;
DWORD usri11_bad_pw_count;
DWORD usri11_num_logons;
LPWSTR usri11_logon_server;
DWORD usri11_country_code;
LPWSTR usri11_workstations;
DWORD usri11_max_storage;
DWORD usri11_units_per_week;
LPBYTE usri11_logon_hours;
DWORD usri11_code_page;
} USER_INFO_11, *PUSER_INFO_11, *LPUSER_INFO_11;

typedef struct _USER_INFO_20 {
LPWSTR usri20_name;
LPWSTR usri20_full_name;
LPWSTR usri20_comment;
DWORD usri20_flags;
DWORD usri20_user_id;
}USER_INFO_20, *PUSER_INFO_20, *LPUSER_INFO_20;

The following information levels are valid only for NetUserSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _USER_INFO_1003 {
LPWSTR usri1003_password;
} USER_INFO_1003, *PUSER_INFO_1003, *LPUSER_INFO_1003;

typedef struct _USER_INFO_1005 {
DWORD usri1005_priv;
} USER_INFO_1005, *PUSER_INFO_1005, *LPUSER_INFO_1005;

typedef struct _USER_INFO_1006 {
LPWSTR usri1006_home_dir;
} USER_INFO_1006, *PUSER_INFO_1006, *LPUSER_INFO_1006;

typedef struct _USER_INFO_1007 {
LPWSTR usri1007_comment;
} USER_INFO_1007, *PUSER_INFO_1007, *LPUSER_INFO_1007;

typedef struct _USER_INFO_1008 {
DWORD usri1008_flags;
} USER_INFO_1008, *PUSER_INFO_1008, *LPUSER_INFO_1008;

typedef struct _USER_INFO_1009 {
LPWSTR usri1009_script_path;
} USER_INFO_1009, *PUSER_INFO_1009, *LPUSER_INFO_1009;

typedef struct _USER_INFO_1010 {
DWORD usri1010_auth_flags;
} USER_INFO_1010, *PUSER_INFO_1010, *LPUSER_INFO_1010;

typedef struct _USER_INFO_1011 {
LPWSTR usri1011_full_name;
} USER_INFO_1011, *PUSER_INFO_1011, *LPUSER_INFO_1011;

typedef struct _USER_INFO_1012 {
LPWSTR usri1012_usr_comment;
} USER_INFO_1012, *PUSER_INFO_1012, *LPUSER_INFO_1012;

typedef struct _USER_INFO_1013 {
LPWSTR usri1013_parms;
} USER_INFO_1013, *PUSER_INFO_1013, *LPUSER_INFO_1013;

typedef struct _USER_INFO_1014 {
LPWSTR usri1014_workstations;
} USER_INFO_1014, *PUSER_INFO_1014, *LPUSER_INFO_1014;

typedef struct _USER_INFO_1017 {
DWORD usri1017_acct_expires;
} USER_INFO_1017, *PUSER_INFO_1017, *LPUSER_INFO_1017;

typedef struct _USER_INFO_1018 {
DWORD usri1018_max_storage;
} USER_INFO_1018, *PUSER_INFO_1018, *LPUSER_INFO_1018;

typedef struct _USER_INFO_1020 {

DWORD usri1020_units_per_week;
LPBYTE usri1020_logon_hours;
} USER_INFO_1020, *PUSER_INFO_1020, *LPUSER_INFO_1020;

typedef struct _USER_INFO_1023 {
LPWSTR usri1023_logon_server;
} USER_INFO_1023, *PUSER_INFO_1023, *LPUSER_INFO_1023;

typedef struct _USER_INFO_1024 {
DWORD usri1024_country_code;
} USER_INFO_1024, *PUSER_INFO_1024, *LPUSER_INFO_1024;

typedef struct _USER_INFO_1025 {
DWORD usri1025_code_page;
} USER_INFO_1025, *PUSER_INFO_1025, *LPUSER_INFO_1025;

typedef struct _USER_INFO_1051 {

DWORD usri1051_primary_group_id;

} USER_INFO_1051, *PUSER_INFO_1051, *LPUSER_INFO_1051;

typedef struct _USER_INFO_1052 {

LPWSTR usri1052_profile;

} USER_INFO_1052, *PUSER_INFO_1052, *LPUSER_INFO_1052;

typedef struct _USER_INFO_1053 {

LPWSTR usri1053_home_dir_drive;

} USER_INFO_1053, *PUSER_INFO_1053, *LPUSER_INFO_1053;

For NetUserSetInfo, parmnum values refer to the fields in the user_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in user_info struct
USER_NAME_PARMNUM | usri_name
USER_PASSWORD_PARMNUM | usri_password
USER_PASSWORD_AGE_PARMNUM | usri_password_age
USER_PRIV_PARMNUM | usri_priv
USER_HOME_DIR_PARMNUM | usri_home_dir
USER_COMMENT_PARMNUM | usri_comment
USER_FLAGS_PARMNUM | usri_flags
USER_SCRIPT_PATH_PARMNUM | usri_script_path
USER_AUTH_FLAGS_PARMNUM | usri_auth_flags
USER_FULL_NAME_PARMNUM | usri_full_name
USER_USR_COMMENT_PARMNUM | usri_usr_comment
USER_PARMS_PARMNUM | usri_parms
USER_WORKSTATIONS_PARMNUM | usri_workstations
USER_LAST_LOGON_PARMNUM | usri_last_logon
USER_LAST_LOGOFF_PARMNUM | usri_last_logoff
USER_ACCT_EXPIRES_PARMNUM | usri_acct_expires
USER_MAX_STORAGE_PARMNUM | usri_max_storage
USER_UNITS_PER_WEEK_PARMNUM | usri_units_per_week
USER_LOGON_HOURS_PARMNUM | usri_logon_hours
USER_PAD_PW_COUNT_PARMNUM | usri_bad_pw_count
USER_NUM_LOGONS_PARMNUM | usri_num_logons
USER_LOGON_SERVER_PARMNUM | usri_logon_server
USER_COUNTRY_CODE_PARMNUM | usri_country_code
USER_CODE_PAGE_PARMNUM | usri_code_page
USER_PRIMARY_GROUP_PARMNUM | usri_primary_group_id
USER_PROFILE | usri_profile
USER_HOME_DIR_DRIVE_PARMNUM | usri_home_dir_drive

The user functions are:

NetUserAdd

NetUserDel

NetUserEnum

NetUserGetGroups

NetUserGetInfo

NetUserGetLocalGroups

NetUserSetGroups

NetUserSetInfo

+$#A User Modal Functions

The user modal functions control the system-wide parameters that affect the Windows NT Security system behavior.

User modal information is available at three levels:

typedef struct _USER_MODALS_INFO_0 {
DWORD usrmod0_min_passwd_len;
DWORD usrmod0_max_passwd_age;
DWORD usrmod0_min_passwd_age;
DWORD usrmod0_force_logoff;
DWORD usrmod0_password_hist_len;
} USER_MODALS_INFO_0, *PUSER_MODALS_INFO_0, LPUSER_MODALS_INFO_0;

typedef struct _USER_MODALS_INFO_1 {
DWORD usrmod1_role;
LPWSTR usrmod1_primary;
} USER_MODALS_INFO_1, *PUSER_MODALS_INFO_1, *LPUSER_MODALS_INFO_1;

typedef struct _USER_MODALS_INFO_2 {
LPWSTR usrmod2_domain_name;
PSID usrmod2_domain_id;
}USER_MODALS_INFO_2, *PUSER_MODALS_INFO_2, *LPUSER_MODALS_INFO_2;

The following information levels are valid only for NetUserModalsSet and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well. Note that the mapping from the old parmnum to the information level does not apply to the role and primary fields.

typedef struct _USER_MODALS_INFO_1001 {
DWORD usrmod1001_min_passwd_len;
} USER_MODALS_INFO_1001, *PUSER_MODALS_INFO_1001, *LPUSER_MODALS_INFO_1001;

typedef struct _USER_MODALS_INFO_1002 {
DWORD usrmod1002_max_passwd_age;
} USER_MODALS_INFO_1002, *PUSER_MODALS_INFO_1002, *LPUSER_MODALS_INFO_1002;

typedef struct _USER_MODALS_INFO_1003 {
DWORD usrmod1003_min_passwd_age;
} USER_MODALS_INFO_1003, *PUSER_MODALS_INFO_1003, *LPUSER_MODALS_INFO_1003;

typedef struct _USER_MODALS_INFO_1004 {
DWORD usrmod1004_force_logoff;
} USER_MODALS_INFO_1004, *PUSER_MODALS_INFO_1004, *LPUSER_MODALS_INFO_1004;

typedef struct _USER_MODALS_INFO_1005 {
DWORD usrmod1005_password_hist_len;
} USER_MODALS_INFO_1005, *PUSER_MODALS_INFO_1005, *LPUSER_MODALS_INFO_1005;

typedef struct _USER_MODALS_INFO_1006 {
DWORD usrmod1006_role;
} USER_MODALS_INFO_1006, *PUSER_MODALS_INFO_1006, *LPUSER_MODALS_INFO_1006;

typedef struct _USER_MODALS_INFO_1007 {
LPWSTR usrmod1007_primary;
} USER_MODALS_INFO_1007, *PUSER_MODALS_INFO_1007, *LPUSER_MODALS_INFO_1007;

For NetUserModalsSet, parmnum values refer to the fields in the modals_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in modals_info struct
MODALS_MIN_PASSWD_LEN_PARMNUM | usrmod_min_passwd_len
MODALS_MAX_PASSWD_AGE_PARMNUM | usrmod_max_passwd_age
MODALS_MIN_PASSWD_AGE_PARMNUM | usrmod_min_passwd_age
MODALS_FORCE_LOGOFF_PARMNUM | usrmod_force_logoff
MODALS_PASSWD_HIST_LIST_PARMNUM | usrmod_passwd_hist_list
MODALS_ROLE_PARMNUM | usrmod_role
MODALS_PRIMARY_PARMNUM | usrmod_primary
MODALS_DOMAIN_NAME_PARMNUM | usrmod_domain_name
MODALS_DOMAIN_ID_PARMNUM | usrmod_domain_id

The user modal functions are:

NetUserModalsGet

NetUserModalsSet

+$#A Global Group functions

The global group functions control global groups of users in a way that can be used across domains. A global group is a set of users who share common permissions in the security database. Group functions create or delete global groups, and review or adjust the membership of the global groups. The global group has a group name that specifies the user names of global group members.

To create a global group, an application calls NetGroupAdd, supplying a group name. Initially, the global group has no members. To assign members to the global group, call NetGroupSetUsers. To add a user to an existing global group, call NetGroupAddUser. To set general information about the global group, call NetGroupSetInfo.

NetGroupDelUser deletes a specified user name from a global group, and NetGroupDel disbands a global group. NetGroupDel works whether or not the global group has any members.

Three group functions retrieve information about the global groups on a server: NetGroupEnum produces a list of all global groups; NetGroupGetUsers lists all members of a specified global group; and NetGroupGetInfo returns general information about the global group.Each user account automatically belongs to one of the special global groups Domain Users or None, according to the user's Security Requirements. Membership of these global groups is indirectly controlled by the NetUserAdd, NetUserDel, and NetUserSetInfo functions. Group functions control groups of users.

Group account information is available at two levels:

typedef struct _GROUP_INFO_0 {
LPWSTR grpi0_name;
} GROUP_INFO_0, *PGROUP_INFO_0, *LPGROUP_INFO_0;

typedef struct _GROUP_INFO_1 {
LPWSTR grpi1_name;
LPWSTR grpi1_comment;
} GROUP_INFO_1, *PGROUP_INFO_1, *LPGROUP_INFO_1;

typedef struct _GROUP_INFO_2 {
LPWSTR grpi2_name;
LPWSTR grpi2_comment;
DWORD grpi2_group_id;
DWORD grpi2_attributes;
}GROUP_INFO_2, *PGROUP_INFO_2;

The groups to which a user belongs may be obtained at one information level.

typedef struct _GROUP_USERS_INFO_0 {
LPWSTR grui0_name;
} GROUP_USERS_INFO_0, *PGROUP_USERS_INFO_0, *LPGROUP_USERS_INFO_0;

typedef struct _GROUP_USERS_INFO_1 {
LPWSTR grui1_name;
DWORD grui1_attributes;
} GROUP_USERS_INFO_1, *PGROUP_USERS_INFO_1, *LPGROUP_USERS_INFO_1;

The following information levels are valid only for NetGroupSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _GROUPINFO_1002 {
LPWSTR grpi1002_comment;
} GROUP_USERS_INFO_1002, *PGROUP_USERS_INFO_1002, *LPGROUP_USERS_INFO_1002;

typedef struct _GROUP_INFO_1005 {
DWORD grpi1005_attributes;
} GROUP_INFO_1005, *PGROUP_INFO_1005, *LPGROUP_INFO_1005;

For NetGroupSetInfo, parmnum values refer to the fields in the group_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in group_info struct
GROUP_NAME_PARMNUM | grpi_name
GROUP_COMMENT_PARMNUM | grpi_comment
GROUP_ATTRIBUTES_PARMNUM | grpi_attributes

The group functions are:

NetGroupAdd

NetGroupAddUser

NetGroupDel

NetGroupDelUser

NetGroupEnum

NetGroupGetInfo

NetGroupGetUsers

NetGroupSetInfo

NetGroupSetUsers

+$#A Local Group Functions

A local group is a set of users who share common permissions in the security database. A local group can have members that are either users or global groups (global groups can contain only users). The local group functions control members of local groups in a way that can be used only by the systems within a "cluster". A "cluster" is the individual workstation if the system is a Windows NT system, but it contains all the Windows NT Servers of a domain if the system is a Windows NT Server. Thus, a local group defined on a workstation can be used only on that workstation, but a local group defined on a Windows NT Server can be used by any other Windows NT Server within the same domain. The local Group functions create or delete local groups, and review or adjust the memberships of local groups.

A member can be added to a local group by specifying the security identifier (SID) of the member. The Win32 LookupAccountName function can be used to translate a member account name to a SID.

To create a local group, an application calls NetLocalGroupAdd, supplying a local group name. Initially, the local group has no members. To assign members to the local group, call NetLocalGroupSetMembers. To add a member to an existing local group, call NetLocalGroupAddMember. To set general information about the local group, call NetLocalGroupSetInfo.

NetLocalGroupDelMember deletes a specified member from a local group, and NetLocalGroupDel disbands a local group, deleting all existing members of the local group first.

Three local group functions retrieve information about the local groups on a server: NetLocalGroupEnum produces a list of all local groups; NetLocalGroupGetMembers lists all members of a specified local group; and NetLocalGroupGetInfo returns general information about the local group.

Group account information is available at three levels:

typedef struct _LOCALGROUP_INFO_0 {
LPWSTR lgrpi0_name;
} LOCALGROUP_INFO_0, *PLOCALGROUP_INFO_0, LPLOCALGROUP_INFO_0;

typedef struct _LOCALGROUP_INFO_1 {
LPWSTR lgrpi1_name;
LPWSTR lgrpi1_comment;
} LOCALGROUP_INFO_1, *PLOCALGROUP_INFO_1, LPLOCALGROUP_INFO_1;

typedef struct _LOCALGROUP_INFO_1002 {
LPWSTR lgrpi1002_comment;
} LOCALGROUP_INFO_1002, *PLOCALGROUP_INFO_1002, LPLOCALGROUP_INFO_1002;

The local group to which a member belongs may be obtained at two information level.

typedef struct _LOCALGROUP_MEMBERS_INFO_0 {
PSID lgrmi0_sid;
} LOCALGROUP_MEMBERS_INFO_0, *PLOCALGROUP_MEMBERS_INFO_0, *LPLOCALGROUP_MEMBERS_INFO_0;

typedef struct _LOCALGROUP_MEMBERS_INFO_0 {
PSID lgrmi0_sid;
} LOCALGROUP_MEMBERS_INFO_0, *PLOCALGROUP_MEMBERS_INFO_0, *LPLOCALGROUP_MEMBERS_INFO_0;

typedef struct _LOCALGROUP_MEMBERS_INFO_1 {
PSID lgrmi1_sid;
SID_NAME_USE lgrmi1_sidusage;
LPWSTR lgrmi1_name;
} LOCALGROUP_MEMBERS_INFO_1, *PLOCALGROUP_MEMBERS_INFO_1, *LPLOCALGROUP_MEMBERS_INFO_1;

typedef struct _LOCALGROUP_USERS_INFO_0 {
LPWSTR lgrui0_name;
} LOCALGROUP_USERS_INFO_0, *PLOCALGROUP_USERS_INFO_0, *LPLOCALGROUP_USERS_INFO_0;

For NetLocalGroupSetInfo, parmnum values refer to the fields in the group_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in group_info struct
LOCALGROUP_NAME_PARMNUM | lgrpi_name
LOCALGROUP_COMMENT_PARMNUM | lgrpi_comment

The local group functions are:

NetLocalGroupAdd

NetLocalGroupAddMember

NetLocalGroupDel

NetLocalGroupDelMember

NetLocalGroupEnum

NetLocalGroupGetInfo

NetLocalGroupGetMembers

NetLocalGroupSetInfo

NetLocalGroupSetMembers

+$#A Access Functions

Note A full set of access setting functions exist in Win32. These should be used in place of the NetAccess functions. The NetAccess functions work only when remoted to a downlevel system.

Access permissions functions examine or modify user or group access permissions for specified resources. An access control list (ACL) contains the name of a resource, an audit attribute field, and a list of access control entries. An access control entry (ACE) is a user name or group name and its corresponding access permissions.

The audit attribute field defines what type of events will be audited for that resource. It is possible to audit various types of events, depending on whether the resource is a file or a directory. It is possible to audit events such as opening, writing, and deleting a file; creating or deleting a directory; and changing the ACL of the resource.

NetAccessAdd creates an ACL for a resource and sets user name or group name access permissions. NetAccessDel deletes the ACL for a resource. NetAccessGetInfo returns the ACL for a particular resource. NetAccessEnum returns information about all ACLs.

Only users or applications with admin group membership or special permission for the resource can define or examine access permissions. Users have special permissions for a resource when they are granted ACCESS_PERM permission for that resource; this is also known as P permission.

NetAccessCheck verifies whether a user has permission to perform a specified operation on a particular resource. If access permission is needed, you can use NetAccessSetInfo to change the ACL. NetAccessGetUserPerms returns a specified user's or group's permission for a specified resource.

Access permission information is available at two levels:

typedef struct _ACCESS_INFO_0 {
LPWSTR acc0_resource_name;
} ACCESS_INFO_0, *PACCESS_INFO_0, *LPACCESS_INFO_0;

typedef struct _ACCESS_INFO_1 {
LPWSTR acc1_resource_name;
DWORD acc1_attr;
DWORD acc1_count;
} ACCESS_INFO_1, *PACCESS_INFO_1, *LPACCESS_INFO_1;

Access list information is available at one levels:

typedef struct _ACCESS_LIST {
LPWSTR acl_ugname;
DWORD acl_access;
} ACCESS_LIST, *PACCESS_LIST, *LPACCESS_LIST;

The following information levels are valid only for NetAccessSetInfo and replace the older way of passing in a Parmnum to set a specific field.

The following are supported on downlevel systems (for example, LAN Manager 2.x) as well:

typedef struct _ACCESS_INFO_1002 {
DWORD acc1002_attr;
} ACCESS_INFO_1002, *PACCESS_INFO_1002, *LPACCESS_INFO_1002;

For NetAccessSetInfo, parmnum values refer to the fields in the access_info structure as follows. These values are used when indicating an error in a specific parameter via parm_err.

parmnum value | Field in access_info struct
ACCESS_RESOURCE_NAME_PARMNUM | acc_resource_name
ACCESS_ATTR_PARMNUM | acc_attr
ACCESS_COUNT_PARMNUM | acc_count
ACCESS_ACCESS_LIST_PARMNUM | array following acc1_count

The following are defined for the bits in the acc1_attr field of _ACCESS_INFO_1:

Value | Constant
ACCESS_AUDIT | 0x001
ACCESS_SUCCESS_OPEN | 0x010
ACCESS_SUCCESS_WRITE | 0x020
ACCESS_SUCCESS_DELETE | 0x040
ACCESS_SUCCESS_ACL | 0x080
ACCESS_SUCCESS_MASK | 0x0F0
ACCESS_FAIL_OPEN | 0x100
ACCESS_FAIL_WRITE | 0x200
ACCESS_FAIL_DELETE | 0x400
ACCESS_FAIL_ACL | 0x800
ACCESS_FAIL_MASK | 0xF00
ACCESS_FAIL_SHIFT | 0x004

The access permission functions are:

NetAccessAdd

NetAccessCheck

NetAccessDel

NetAccessEnum

NetAccessGetInfo

NetAccessGetUserPerms

NetAccessSetInfo

+$#A Domain Functions

Domain functions retrieve information about a domain. They require that the workstation service be started.

The domain functions are:

NetGetDCName

NetServerTransportAdd

NetServerTransportDel

NetServerTransportEnum

NetWkstaTransportAdd

NetWkstaTransportDel

NetWkstaTransportEnum

+$#A Transport Functions

Transport functions handle binding and unbinding of transports to and from the server and redirector, and also enumerate the transports used by a component. The server transport functions deal with transports managed by the server, and the workstation transport functions deal with transports managed by the redirector.

NetServerTransportAdd allows the user to bind the transport to the server. NetServerTransportDel allows the user to unbind the server from the transport. NetServerTransportEnum enumerates the transports that are managed by the server.

The NetWkstaTransportAdd/Del/Enum functions perform equivalent operations for the workstation.

Server transport functions are available at one information level.

typedef struct _SERVER_TRANSPORT_INFO_0 {
DWORD svti0_number_of_vcs;
LPWSTR svti0_transport_name;
LPBYTE svti0_transport_address;
DWORD svti0_transportaddresslength;
LPWSTR svti0_networkaddress;
} SERVER_TRANSPORT_INFO_0, *PSERVER_TRANSPORT_INFO_0, *LPSERVER_TRANSPORT_INFO_0;

Workstation transport functions are available at one information level.

typedef struct _WKSTA_TRANSPORT_INFO_0 {
DWORD wkti0_quality_of_service;
DWORD wkti0_number_of_vcs;
LPWSTR wkti0_transport_name;
LPWSTR wkti0_transport_address;
} WKSTA_TRANSPORT_INFO_0, *PWKSTA_TRANSPORT_INFO_0, *LPWKSTA_TRANSPORT_INFO_0;

The transport functions are:

NetServerTransportAdd

NetServerTransportDel

NetServerTransportEnum

+$#A Alert Functions

Alert functions notify network service programs and applications of network events. An event is a particular instance of a process or state of hardware as defined by an application. The alert functions allow applications to indicate when predefined events occur.

NetAlertRaise is used to indicate that an event has occurred.

The fixed-length header contains the standard alert data structure. The STD_ALERT data structure has the following format:

typedef struct _STD_ALERT {
DWORD alrt_timestamp;
WCHAR alrt_eventname[EVLEN+1];
WCHAR alrt_servicename[SNLEN+1];
} STD_ALERT, *PSTD_ALERT, *LPSTD_ALERT;

The ADMIN_OTHER_INFO data structure has the following format:

typedef struct _ADMIN_OTHER_INFO {
DWORD alrtad_errcode;
DWORD alrtad_numstrings;
} ADMIN_OTHER_INFO, *PADMIN_OTHER_INFO, *LPADMIN_OTHER_INFO;

/* Followed by consecutive Unicode strings; the count
is in the alrtad_numstrings element. */
WCHAR mergestrings[];

The ERRLOG_OTHER_INFO data structure has the following format:

typedef struct _ERRLOG_OTHER_INFO {
DWORD alrter_errcode;
DWORD alrter_offset;
} ERRLOG_OTHER_INFO, *PERRLOG_OTHER_INFO, *LPERRLOG_OTHER_INFO;

The PRINT_OTHER_INFO data structure has the following format:

typedef struct _PRINT_OTHER_INFO {
DWORD alrtpr_jobid;
DWORD alrtpr_status;
DWORD alrtpr_submitted;
DWORD alrtpr_size;
} PRINT_OTHER_INFO, *PPRINT_OTHER_INFO, *LPPRINT_OTHER_INFO;
/* Followed by consecutive Unicode strings. */
WCHAR computername[];
WCHAR username[];
WCHAR queuename[];
WCHAR destname[];
WCHAR status_string[];

The USER_OTHER_INFO data structure has the following format:

typedef struct _USER_OTHER_INFO {
DWORD alrtus_errcode;
DWORD alrtus_numstrings;
} USER_OTHER_INFO, *PUSER_OTHER_INFO, *LPUSER_OTHER_INFO;
/*
* Followed by a number of consecutive Unicode strings;
* the count is in the alrtus_numstrings element. */
*/
WCHAR mergestrings[];

/* Further followed by two more consecutive Unicode strings. */
WCHAR username[];
WCHAR computername[];

The alert functions are:

NetAlertRaise

NetAlertRaiseEx

+$#A Error Logging Functions

Note Use the error logging functions only for remoting to downlevel. For Windows NT use the Event Logging functions.

Windows NT uses an integrated Event Logging mechanism for reporting both errors and audits. The NetError and NetAudit functions are provided to access downlevel LAN Manager logs. They will report ERROR_NOT_SUPPORTED if called to an Windows NT system.

The error log is a file that stores error messages (in binary format). It contains information about LAN Manager software internal errors, MS OS/2 and MS-DOS internal errors, and network service errors.

NetErrorLogRead reads entries from the error log; and NetErrorLogClear clears the error log and, optionally, saves the entries in a backup file.

NetErrorLogRead uses the ERROR_LOG data structure to read entries from and write entries to the error log. An entry consists of a fixed-length data structure. The data structure can be followed by Unicode strings (el_text) that describe the error message, and a block of raw data (el_data) related to the cause of the error. Because of the variable lengths and structures of the el_data and el_text portions of the entry, only the fixed-length data structure is defined in the ERROR_LOG data structure.

The error log entry has the following format:

typedef struct _ERROR_LOG {
DWORD el_len;
DWORD el_reserved;
DWORD el_time;
DWORD el_error;
LPWSTR el_name
LPWSTR el_text;

LPBYTE el_data;

DWORD el_data_size;
DWORD el_nstrings;
} ERROR_LOG, *PERROR_LOG, *LPERROR_LOG;

/*
* Variable-length raw data specific to the error. el_len will be preceded by pad bytes

* as required for proper alignment. There may also be pad bytes after the end of the

* fixed length structure, before el_data. Use the pointer in the fixed length structure

* to reference this data.
*/
BYTE el_data[]; /* Raw data */
DWORD el_len2;

The error log handle is defined as follows:

typedef struct _HLOG {
DWORD time;
DWORD last_flags;
DWORD offset;
DWORD rec_offset;
} HLOG, *PHLOG, *LPHLOG;

The error log functions are:

NetErrorLogClear

NetErrorLogRead

+$#A Configuration Functions

Note The configuration functions are only for downlevel support. Use the registry functions to retrieve configuration information on Windows NT.

Configuration functions retrieve network configuration information.

Configuration functions are available at one information level.

typedef struct _CONFIG_INFO_0 {
LPWSTR cfgi0_key;
LPWSTR cfgi0_data;
} CONFIG_INFO_0, *PCONFIG_INFO_0, *LPCONFIG_INFO_0;

The configuration functions are:

NetConfigGet

NetConfigGetAll

NetConfigSet

+$#A Statistics Functions

LAN Manager accumulates a set of operating statistics for workstations and servers from the time that the Workstation or Server service is started. NetStatisticsGet is called to get those statistics. The statistics function, NetStatisticsGet, retrieves the operating statistics for workstations and servers. Since Windows NT and downlevel workstations collect a different set of statistics, the caller must know whether the server is Windows NT or downlevel (which can be discovered using the NetServerGetInfo function) and interpret the returned buffer accordingly.

NetStatisticsGet returns a STAT_WORKSTATION_0 data structure when workstation statistics are requested; it returns a STAT_SERVER_0 data structure when server statistics are requested.

The downlevel STAT_WORKSTATION_0 data structure has the following format:

typedef struct _STAT_WORKSTATION_0 {
DWORD stw0_start;
DWORD stw0_numNCB_r;
DWORD stw0_numNCB_s;
DWORD stw0_numNCB_a;
DWORD stw0_fiNCB_r;
DWORD stw0_fiNCB_s;
DWORD stw0_fiNCB_a;
DWORD stw0_fcNCB_r;
DWORD stw0_fcNCB_s;
DWORD stw0_fcNCB_a;
DWORD stw0_sesstart;
DWORD stw0_sessfailcon;
DWORD stw0_sessbroke;
DWORD stw0_uses;
DWORD stw0_usefail;
DWORD stw0_autorec;
DWORD stw0_bytessent_r_hi;
DWORD stw0_bytessent_r_lo;
DWORD stw0_bytesrcvd_r_hi;
DWORD stw0_bytesrcvd_r_lo;
DWORD stw0_bytessent_s_hi;
DWORD stw0_bytessent_s_lo;
DWORD stw0_bytesrcvd_s_hi;
DWORD stw0_bytesrcvd_s_lo;
DWORD stw0_bytessent_a_hi;
DWORD stw0_bytessent_a_lo;
DWORD stw0_bytesrcvd_a_hi;
DWORD stw0_bytesrcvd_a_lo;
DWORD stw0_reqbufneed;
DWORD stw0_bigbufneed;
} STAT_WORKSTATION_0, *PSTAT_WORKSTATION_0, *LPSTAT_WORKSTATION_0;

The Windows NT STAT_WORKSTATION_0 data structure has the following format:

typedef struct _STAT_WORKSTATION_0 {
LARGE_INTEGER StatisticsStartTime;
LARGE_INTEGER BytesReceived;
LARGE_INTEGER SmbsReceived;
LARGE_INTEGER PagingReadBytesRequested;
LARGE_INTEGER NonPagingReadBytesRequested;
LARGE_INTEGER CacheReadBytesRequested;
LARGE_INTEGER NetworkReadBytesRequested;
LARGE_INTEGER BytesTransmitted;
LARGE_INTEGER SmbsTransmitted;
LARGE_INTEGER PagingWriteBytesRequested;
LARGE_INTEGER NonPagingWriteBytesRequested;
LARGE_INTEGER CacheWriteBytesRequested;
LARGE_INTEGER NetworkWriteBytesRequested;
DWORD InitiallyFailedOperations;
DWORD FailedCompletionOperations;
DWORD ReadOperations;
DWORD RandomReadOperations;
DWORD ReadSmbs;
DWORD LargeReadSmbs;
DWORD SmallReadSmbs;
DWORD WriteOperations;
DWORD RandomWriteOperations;
DWORD WriteSmbs;
DWORD LargeWriteSmbs;
DWORD SmallWriteSmbs;
DWORD RawReadsDenied;
DWORD RawWritesDenied;
DWORD NetworkErrors;
// Connection /Session counts
DWORD Sessions;
DWORD FailedSessions;
DWORD Reconnects;
DWORD CoreConnects;
DWORD Lanman20Connects;
DWORD Lanman21Connects;
DWORD LanmanNtConnects;
DWORD ServerDisconnects;
DWORD HungSessions;
DWORD UseCount;
DWORD FailedUseCount;
//
// Queue Leng ths (updates protected by RdrMpxTableSpinLock NOT
// RdrStatisticsSpinlock)
//
DWORD CurrentCommands;
} STAT_WORKSTATION_0, *PSTAT_WORKSTATION_0, *LPSTAT_WORKSTATION_0;

The STAT_SERVER_0 data structure has the following format:

typedef struct _STAT_SERVER_0 {
DWORD sts0_start;
DWORD sts0_fopens;
DWORD sts0_devopens;
DWORD sts0_jobsqueued;
DWORD sts0_sopens;
DWORD sts 0_stimedout;
DWORD sts0_serrorout;
DWORD sts0_pwerrors;
DWORD sts0_permerrors;
DWORD sts0_syserrors;
DWORD sts0_bytessent_low;
DWORD sts0_bytessent_high;
DWORD sts0_bytesrcvd_low;
DWORD sts0_bytesrcvd_high;
DWORD sts0_avresponse;
DWORD sts0_reqbufneed;
DWORD sts0_bigbufneed;
} STAT_SERVER_0, *PSTAT_SERVER_0, *LPSTAT_SERVER_0;

The statistics function is:

NetStatisticsGet

+$#A Auditing Functions

Note The auditing functions are for downlevel only. Windows NT automatically records audits on failed accesses when AccessCheckAndAudit is used. To read audits, use the ReadEventLog function.

Windows NT uses an integrated event logging mechanism for reporting both errors and audits. The NetError and NetAudit functions are provided to access downlevel LAN Manager logs. They will report ERROR_NOT_SUPPORTED if called to an Windows NT system.

Auditing functions control the audit log. Auditing functions monitor operations on the specified server. If auditing is enabled, each monitored operation generates an audit entry. For example, when a user establishes a connection to the server, a single audit entry is generated.

Audit entries are stored in a binary file called an audit trail or audit log. All Auditing functions perform their operations on this file. LAN Manager defines many types of audit entries.

NetAuditRead reads the audit log. NetAuditClear clears the audit log.

Data Structures

All audit entries include a fixed-length header used in conjunction with variable-length data specific to the entry type. Because of the variable lengths and structures of the ae_data element of the audit entry (it is possible for ae_data to be zero bytes), only the fixed header is defined in the audit_entry data structure.

The variable-length portion of the audit entry can contain an offset to a variable-length Unicode string. The offset values are DWORDs. To determine the value of the pointer to this string, add the offset value to the address of the ae_data data structure.

The following example illustrates this procedure. Assume that pAE points to a buffer that contains a complete audit entry and that the ae_type element of the audit_entry data structure contains the value AE_CONNSTOP, which specifies the predefined AE_CONNSTOP data structure. To point the variable pszComputerName to the Unicode string that contains the name of the client whose connection was stopped, an application would perform the following algorithm:

PAUDIT_ENTRY pAE; /* Fixed part of audit entry */
LPAE_CONNSTOP pAEvar; /* Variable-length structure */
LPWSTR pszComputerName; /* Pointer to var-length string*/

/* Calculate the offset to the variable-length structure. */
pAEvar = (_LPAE_CONNSTOP) (((LPBYTE) pAE) + pAE->ae_data_offset);

/* Calculate the offset to the computername. */
pszComputerName = ((LPBYTE) pAEvar) + pAEvar->ae_cp_compname;

Fixed-Length Header

The audit_entry data structure has the following format:

typedef struct _AUDIT_ENTRY {
DWORD ae_len;
DWORD ae_reserved;
DWORD ae_time;
DWORD ae_type;
DWORD ae_data_offset; /* Offset from beginning
address of audit_entry */

DWORD ae_data_size;
} AUDIT_ENTRY, *PAUDIT_ENTRY, *LPAUDIT_ENTRY;

/* Variable-length data specific to type of audit entry. There may be
pad bytes after ae_data_size for alignment purposes.

*/
BYTE ae_data[];

/* Terminating length indicator. Preceded by pad bytes for proper alignment.*/
DWORD ae_len2;

where

ae_len and ae_len2

Specify the length of the audit entry. Both have the same value. The ae_len element is included at the beginning and at the end of the audit entry to enable both backward and forward scanning of the log.

ae_reserved

Reserved.

ae_time

Specifies when the audit entry was generated. The value is stored as the number of seconds elapsed since 00:00:00, January 1, 1970.

ae_type

Specifies the type of audit entry. Type values from 0x0000 through 0x07FF are reserved. OEMs and other applications programmers can reserve values from 0x0800 through 0xFFFF.

ae_data_offset

Specifies the byte offset from the beginning of the audit entry to the beginning of the variable-length portion (ae_data) of the audit entry.

ae_data

Specifies the variable-length portion of the audit entry; it differs depending on the type of entry specified by ae_type. The information begins at ae_data_offset bytes from the top of the audit entry. For information about the structure of each entry type defined by LAN Manager, see Variable-Length Data.

+$#A Variable-Length Data

The following data structures are specific to the audit entry type The structures follow the audit_entry header, but they are not necessarily contiguous.

The _AE_SRVSTATUS data structure is associated with an audit entry of type AE_SRVSTATUS.

typedef struct _AE_SRVSTATUS {
DWORD ae_sv_status;
}AE_SRVSTATUS , * PAE_SRVSTATUS ;

The _AE_SESSLOGON data structure is associated with an audit entry of type AE_SESSLOGON.

typedef struct _AE_SESSLOGON {
DWORD ae_so_compname;
DWORD ae_so_username;
DWORD ae_so_privilege;
}AE_SESSLOGON , * PAE_SESSLOGON ;

The _AE_SESSLOGOFF data structure is associated with an audit entry of type AE_SESSLOGOFF.

typedef struct AE_SESSLOGOFF {
DWORD ae_sf_compname;
DWORD ae_sf_username;
DWORD ae_sf_reason;
}AE_SESSLOGOFF , * PAE_SESSLOGOFF;

The _AE_SESSPWERR data structure is associated with an audit entry of type AE_SESSPWERR.

typedef struct _AE_SESSPWERR {
DWORD ae_sp_compname;
DWORD ae_sp_username;
}AE_SESSPWERR , * PAE_SESSPWERR ;

The _AE_CONNSTART data structure is associated with an audit entry of type AE_CONNSTART.

typedef struct _AE_CONNSTART {
DWORD ae_ct_compname;
DWORD ae_ct_username;
DWORD ae_ct_netname;
DWORD ae_ct_connid;
}AE_CONNSTART , * PAE_CONNSTART ;

The _AE_CONNSTOP data structure is associated with an audit entry of type AE_CONNSTOP.

typedef struct _AE_CONNSTOP {
DWORD ae_cp_compname;
DWORD ae_cp_username;
DWORD ae_cp_netname;
DWORD ae_cp_connid;
DWORD ae_cp_reason;
}AE_CONNSTOP , * PAE_CONNSTOP ;

The _AE_CONNREJ data structure is associated with an audit entry of type AE_CONNREJ.

typedef struct _AE_CONNREJ {
DWORD ae_cr_compname;
DWORD ae_cr_username;
DWORD ae_cr_netname;
DWORD ae_cr_reason;
}AE_CONNREJ , * PAE_CONNREJ ;

The _AE_RESACCESS data structure is associated with an audit entry of type AE_RESACCESS.

typedef struct _AE_RESACCESS {
DWORD ae_ra_compname;
DWORD ae_ra_username;
DWORD ae_ra_resname;
DWORD ae_ra_operation;
DWORD ae_ra_returncode;
DWORD ae_ra_restype;
DWORD ae_ra_fileid;
}AE_RESACCESS , * PAE_RESACCESS ;

The _AD_RESACCESSREJ data structure is associated with an audit entry of type AE_RESACCESSREJ.

typedef struct _AE_RESACCESSREJ {
DWORD ae_rr_compname;
DWORD ae_rr_username;
DWORD ae_rr_resname;
DWORD ae_rr_operation;
}AE_RESACCESSREJ , * PAE_RESACCESSREJ ;

The _AD_CLOSEFILE data structure is associated with an audit entry of type AE_CLOSEFILE.

typedef struct _AE_CLOSEFILE {
DWORD ae_cf_compname;
DWORD ae_cf_username;
DWORD ae_cf_resname;
DWORD ae_cf_fileid;
DWORD ae_cf_duration
DWORD ae_cf_reason;
}AE_CLOSEFILE , * PAE_CLOSEFILE ;

The _AE_SERVICESTAT data structure is associated with an audit entry of type AE_SERVICESTAT.

typedef struct _AE_SERVICESTAT {
DWORD ae_ss_compname;
DWORD ae_ss_username;
DWORD ae_ss_svcname;
DWORD ae_ss_status;
DWORD ae_ss_code;
DWORD ae_ss_text;
DWORD ae_ss_returnval;
}AE_SERVICESTAT , * PAE_SERVICESTAT ;

The _AE_ACLMOD data structure is associated with audit entries of type AE_ACLMOD and AE_ACLMODFAIL.

struct _AE_ACLMOD {
DWORD ae_am_compname;
DWORD ae_am_username;
DWORD ae_am_resname;
DWORD ae_am_action;
DWORD ae_am_datalen;
}AE_ACLMOD, * PAE_ACLMOD;

The _AE_UASMOD data structure is associated with an audit entry of type AE_UASMOD.

typedef struct _AE_UASMOD {
DWORD ae_um_compname;
DWORD ae_um_username;
DWORD ae_um_resname;
DWORD ae_um_rectype;
DWORD ae_um_action;
DWORD ae_um_datalen;
}AE_UASMOD , * PAE_UASMOD ;

The _AE_NETLOGON data structure is associated with an audit entry of type AE_NETLOGON.

typedef struct _AE_NETLOGON {
DWORD ae_no_compname;
DWORD ae_no_username;
DWORD ae_no_privilege;
DWORD ae_no_authflags;
}AE_NETLOGON , * PAE_NETLOGON ;

The _AE_NETLOGOFF data structure is associated with an audit entry of type AE_NETLOGOFF.

typedef struct _AE_NETLOGOFF {
DWORD ae_nf_compname;
DWORD ae_nf_username;
DWORD ae_nf_reserved1;
DWORD ae_nf_reserved2;
}AE_NETLOGOFF , * PAE_NETLOGOFF ;

The _AE_ACCLIM data structure is associated with an audit entry of type AE_ACCLIMITEXCD.

typedef struct _AE_ACCLIM {
DWORD ae_al_compname;
DWORD ae_al_username;
DWORD ae_al_resname;
DWORD ae_al_limit;
}AE_ACCLIM , * PAE_ACCLIM ;

The _AE_LOCKOUT data structure is associated with an audit entry of type AE_LOCKOUT.

typedef struct AE_LOCKOUT {
DWORD ae_lk_compname;
DWORD ae_lk_username;
DWORD ae_lk_action;
DWORD ae_lk_bad_pw_count;
}AE_LOCKOUT , PAE_LOCKOUT ;

The _AE_GENERIC data structure is associated with an audit entry of type AE_GENERIC.

typedef struct _AE_GENERIC {
DWORD ae_ge_msgfile;
DWORD ae_ge_msgnum;
DWORD ae_ge_params;
DWORD ae_ge_param1;
DWORD ae_ge_param2;
DWORD ae_ge_param3;
DWORD ae_ge_param4;
DWORD ae_ge_param5;
DWORD ae_ge_param6;
DWORD ae_ge_param7;
DWORD ae_ge_param8;
DWORD ae_ge_param9;
}AE_GENERIC , * PAE_GENERIC ;

The audit log handle is defined as follows:

typedef struct _HLOG {
DWORD time;
DWORD last_flags;
DWORD offset;
DWORD rec_offset;
} HLOG, *PHLOG, *LPHLOG;

The auditing functions are:

NetAuditClear

NetAuditRead

+$#A Replicator Functions

The Windows NT replicator functions control how the Windows NT Replicator service updates selective directories from an export server to one or more clients. In addition to providing compatible LAN Manager 2.x functionality in a well-defined manner, this new set of LAN Manager functions allow for specific API (operation) security checking.

The change from the file system based control on LAN Manager 2.x to replicator function control on Windows NT has the following implications:

• Applications can no longer delete a directory in the import path of a client to stop receiving updates from its master.

• Applications can no longer use the REPL.INI file in each replicated directory on a master to control the method of replication.

• Applications can no longer lock or unlock a directory on a master from being replicated by creating or deleting the USERLOCK.* file(s).

• Applications can no longer lock or unlock a directory on a client from receiving updates from its master by creating or deleting the USERLOCK.* file(s).

• Applications that depend on the LAN Manager 2.x behavior of ignoring locks for file integrity trees will need to be modified. (Windows NT policy differs from LAN Manager 2.x policy; under Windows NT the locks are always respected.)

Each of the intended operations listed preceding can be specified to the Windows NT replicator service through an appropriate function.

Any user or application that belongs to the admin or server operator group on a local or remote export server can modify the parameters that control the replication master.

There are three categories of replicator functions:

• Replicator configuration functions

• Replicator export directory functions

• Replicator import directory functions

+$#A Replicator Configuration Functions

The configuration parameters of the replicator service can be examined using NetReplGetInfo. They can be modified using NetReplSetInfo.

The replicator service configuration parameters are:

role

Role of the Replicator service, which is either REPL_ROLE_IMPORT (client), REPL_ROLE_EXPORT (master), or REPL_ROLE_BOTH. On a Windows NT Server, any of these values are allowed. Under Windows NT systems, only REPL_ROLE_IMPORT is allowed.

exportpath

Fully qualified path to the master tree in which directories are created and replicated from. This path must include the drive letter.

exportlist

List of computer and domain names to send update announcements to. If this list is not provided, then update announcements will be sent to the server's domain. The list entries are separated by semicolons; computer names in the list must not have leading backslashes.

importpath

Fully qualified path to the client tree in which directories are created to receive the replicas of the master. This path must include the drive letter.

importlist

List of computers and domain names to receive updates from. If this list is not provided, then update announcements are received from the server's domain. The list entries are separated by semicolons; computer names in the list must not have leading backslashes.

logonusername

User account name belonging to the replicator group with which the client logs on to read files from its master(s). This field is ignored by the Windows NT replicator, but is provided for possible future use.

interval

Time in minutes within which the master checks for changes in all the replicated directories. This field may be ignored by the Windows NT replicator, but is provided for possible future use.

pulse

Time in number of intervals within which the master notifies its clients of the current replica version when no updates are necessary. This field may be ignored by the Windows NT replicator, but is provided for possible future use.

guardtime

Time in minutes that a REPL_INTEGRITY_TREE level integrity directory must be stable before a client is allowed to update from it. (See the explanation of integrity in Replicator Export Directory Functions). The default guard time is 2 minutes.

random

A value in seconds sent by a master to its clients so that the clients can use the 0 random range to generate a random time to wait on before updating from the master.

The configuration parameters (except importpath and exportpath) values can all be dynamically set.

These functions can be called whether the replicator service is running or not. If the replicator service is already running, any modification (except to importpath and exportpath) to the replication configuration takes effect immediately, and is persistent after the replicator service has been stopped. If the replicator service is not started, the parameters are stored as persistent information and will take effect when the replicator service starts up. The service must be stopped to set the importpath and exportpath values.

The replicator configuration information structures are:

typedef struct _REPL_INFO_0 {
DWORD rp0_role;
LPWSTR rp0_exportpath;
LPWSTR rp0_exportlist;
LPWSTR rp0_importpath;
LPWSTR rp0_importlist;
LPWSTR rp0_logonusername;
DWORD rp0_interval;
DWORD rp0_pulse;
DWORD rp0_guardtime;
DWORD rp0_random;
} REPL_INFO_0, *PREPL_INFO_0, *LPREPL_INFO_0;

typedef struct _REPL_INFO_1000 {
DWORD rp1000_interval;
} REPL_INFO_1000, *PREPL_INFO_1000, *LPREPL_INFO_1000;

typedef struct _REPL_INFO_1001 {
DWORD rp1001_pulse;
} REPL_INFO_1001, *PREPL_INFO_1001, *LPREPL_INFO_1001;

typedef struct _REPL_INFO_1002 {
DWORD rp1002_guardtime;
} REPL_INFO_1002, *PREPL_INFO_1002, *LPREPL_INFO_1002;

typedef struct _REPL_INFO_1003 {
DWORD rp1003_random;
} REPL_INFO_1003, *PREPL_INFO_1003, *LPREPL_INFO_1003;

+$#A Replicator Export Directory Functions

The replicator export directory functions control top-level directories under the export path on the master. A user can create a new directory under the export path and the Replicator service will automatically replicate that directory. Or, a directory under the export path can be registered using NetReplExportDirAdd. When adding a directory to be replicated using these functions, the replication controls (integrity and extent) are specified via the NetReplExportDirAdd function. If the directory is created in the file system and no replicator functions are called, then the directory is treated as having file integrity and tree extent.

Integrity determines when a master updates a client. When integrity is REPL_INTEGRITY_FILE, the client gets a replica of a file within the directory when it is not in use (being changed or replicated). When integrity is set to REPL_INTEGRITY_TREE, every file and directory within the replicated directory must be stable for the amount of time specified by the guardtime parameter before the client is updated. Extent determines whether the entire tree within the directory is replicated (REPL_EXTENT_TREE) or only the files in the first-level directory is replicated (REPL_EXTENT_FILE).

The replication controls of each replicated directory can be examined using NetReplExportDirGetInfo, and dynamically modified using NetReplExportDirSetInfo. These control fields used to be specified in the REPL.INI file within each replicated directory on LAN Manager 2.x, and they were not dynamically settable. On Windows NT, the REPL.INI file is not used and will be ignored in the replication process.

NetReplExportDirEnum returns a list of directories that are currently replicated. NetReplExportDirDel deregisters a directory so that it is no longer replicated.

The lock status information is returned in two fields: lockcount and locktime. Lockcount indicates the number of outstanding locks on a directory. Locktime is the time (in seconds since 1970, GMT) when the directory was first locked, or is 0 if the directory is not locked at the present time.

NetReplExportDirLock locks a directory so that it is not replicated, by incrementing a lock reference count for the directory. A lock on a directory can be unlocked using NetReplExportDirUnlock. The replication does not resume unless all outstanding locks on that directory are released, and the lock reference count is returned to 0. (The locktime field is automatically set to 0 when lockcount is 0.)

The replicator export directory functions can be called whether the Replicator service is running or not. If the Replicator service is running as a master, any modification to the directory controls takes effect immediately, and is persistent after the Replicator service has been stopped. If the Replicator service is not started, the controls for the directory is stored as persistent information and will take effect when the Replicator service starts up.

The replicator export directory functions are available at the following information levels:

typedef struct _REPL_EDIR_INFO_0 {
LPWSTR rped0_dirname;
} REPL_EDIR_INFO_0, *PREPL_EDIR_INFO_0, *LPREPL_EDIR_INFO_0;

typedef struct _REPL_EDIR_INFO_1 {
LPWSTR rped1_dirname;
DWORD rped1_integrity;
DWORD rped1_extent;
} REPL_EDIR_INFO_1, *PREPL_EDIR_INFO_1, *LPREPL_EDIR_INFO_1;

typedef struct _REPL_EDIR_INFO_2 {
LPWSTR rped2_dirname;
DWORD rped2_integrity;
DWORD rped2_extent;
DWORD rped2_lockcount;
DWORD rped2_locktime;
} REPL_EDIR_INFO_2, *PREPL_EDIR_INFO_2, *LPREPL_EDIR_INFO_2;

typedef struct _REPL_EDIR_INFO_1000 {
DWORD rped1000_integrity;
} REPL_EDIR_INFO_1000, *PREPL_EDIR_INFO_1000, *LPREPL_EDIR_INFO_1000;

typedef struct _REPL_EDIR_INFO_1001 {
DWORD rped1001_extent;
} REPL_EDIR_INFO_1001, *PREPL_EDIR_INFO_1001, *LPREPL_EDIR_INFO_1001;

The replicator export directory functions are:

NetReplExportDirAdd

NetReplExportDirDel

NetReplExportDirEnum

NetReplExportDirGetInfo

NetReplExportDirSetInfo

NetReplExportDirLock

NetReplExportDirUnlock

+$#A Replicator Import Directory Functions

The replicator import directory functions designate the top-level directories under the import path to receive updates on. They also return status information about a replicated directory on the client. On LAN Manager 2.x, a user must create a directory under the import path and the Replicator service automatically replicates to it. On Windows NT, import directories are automatically added if they are exported by an export server from which importer is importing. Another way to register a directory in advance of it being exported, is to use the NetReplImportDirAdd functions. This function does not create the directory itself. This is useful if you wish to modify some of the properties of the import directory (for example, to lock it) prior to it first beginning to import this directory.

NetReplImportDirDel deregisters a directory. This is used to clean up a directory that is no longer being exported. It will not stop replication if there is an active exporter, since it will be re-registered the next time the exporter tells the importer what directories it is exporting. If you wish to prevent importing of an actively exported directory, use the NetReplImportDirLock function.

NetReplImportDirEnum lists all the directories that are replicated to a client, and NetReplImportDirGetInfo returns the status of a specified directory.

The status information of a directory consists of the replication state, the UNC computer name of the master (mastername), and the time (in seconds since 1970, GMT) when the directory was last updated (last_update_time). If the state is REPL_STATE_OK, the directory currently has a master, and is receiving regular update notices from it. If the state is REPL_STATE_NO_MASTER, the directory is not supported by any master, and it is normally empty. If the state is REPL_STATE_NO_SYNC, the directory has a master, but the master has not sent any update notices within the interval time period. This may be due to a communication failure, the master crashing, the directory being locked, files in the client directory being opened at update time, or an unstable REPL_INTEGRITY_TREE integrity directory on the master. If the client Replicator service is not started the state is REPL_STATE_NEVER_REPLICATED, mastername is a NULL string, and last_update_time is 0.NetReplImportDirLock locks a directory so that it does not receive updates, by incrementing a lock reference count for the directory. A lock on a directory can be unlocked using NetReplImportDirUnlock. The directory is not updated unless all outstanding locks on that directory are released, and the lock reference count is returned to 0.

The lock status information is returned in two fields: lockcount and locktime. Lockcount indicates the number of outstanding locks on a directory. Locktime is the time (in seconds since 1970, GMT) when the directory was first locked. (locktime is set to 0 whenever lockcount goes to 0.)

The replicator import directory functions can be called whether the Replicator service is running or not. If the Replicator service is running as a client, directory adds or deletes take effect immediately, and is persistent after the Replicator service has been stopped. If the Replicator service is not started, any added directory will receive updates when the Replicator service starts up (if there exists a master that exports the directory).

The replicator import directory function are available at the following information levels:

typedef struct _REPL_IDIR_INFO_0 {
LPWSTR rpid0_dirname;
} REPL_IDIR_INFO_0, *PREPL_IDIR_INFO_0, *LPREPL_IDIR_INFO_0;

typedef struct _REPL_IDIR_INFO_1 {
LPWSTR rpid1_dirname;
DWORD rpid1_state;
LPWSTR rpid1_mastername;
DWORD rpid1_last_update_time;
DWORD rpid1_lockcount;
DWORD rpid1_locktime;
} REPL_IDIR_INFO_1, *PREPL_IDIR_INFO_1, *LPREPL_IDIR_INFO_1;

The replicator import directory functions are:

NetReplImportDirAdd

NetReplImportDirDel

NetReplImportDirEnum

NetReplImportDirGetInfo

NetReplImportDirLock

+$#A Schedule Service Functions

Schedule service functions are used to submit and manage jobs to be executed at a given computer at a given time or times in future. Jobs can be commands and programs. Jobs can be managed at remote and local computers provided schedule service is running at a given computer.

Schedule service functions were designed to support old AT command functionality, but they extend this functionality so that new AT command may submit and manage jobs at remote as well as local computers. Schedule service functions are also known as "Job" and "AT command" functions.

Schedule service functions are job submittal (NetScheduleJobAdd), job cancellation (NetScheduleJobDel), job enumeration (NetScheduleJobEnum) and retrieving information about a particular job (NetScheduleJobGetInfo). In order for these functions to succeed, a caller must have administrator's privilege at a computer where Schedule service is running.

Data Structures

AT_INFO data structure is used by NetScheduleJobAdd( ) to specify a job to be added, and by NetScheduleJobGetInfo( ) to return information about an already submitted job. AT_ENUM data structure is used by NetScheduleJobEnum( ) to enumerate and return information about an entire queue of already submitted jobs.

typedef struct _AT_INFO {
DWORD JobTime;
DWORD DaysOfMonth;
UCHAR DaysOfWeek;
UCHAR Flags;
LPWSTR Command;
} AT_INFO, *PAT_INFO, *LPAT_INFO;

typedef struct _AT_ENUM {
DWORD JobId;
DWORD JobTime;
DWORD DaysOfMonth;
UCHAR DaysOfWeek;
UCHAR Flags;
LPWSTR Command;
} AT_ENUM, *PAT_ENUM, *LPAT_ENUM;

JobId

Job identifier of an already submitted job.

JobTime

Time of day when a job is scheduled to run. Time is a local time at a computer where Schedule service is running. Time is measured from midnight and is expressed in milliseconds.

DaysOfMonth

Bitmask for days of month when a job is scheduled to run. The bitmask is NULL if a job was scheduled to run only once at the first occurrence of JobTime. For each bit that is set in the bitmask a job will run at JobTime on a corresponding day of the month. Bit 0 corresponds to first day of the month, etc.

DaysOfWeek

Bitmask for days of week when this job is scheduled to run. The bitmask is NULL if a job was scheduled to run only once at the first occurrence of JobTime. For each bit that is set in the bitmask a job will run at JobTime on a corresponding day of the week. Bit 0 corresponds to Monday (first day of the week), etc.

Flags

Bitmask describing job properties. For job submission (NetScheduleJobAdd) possible values are:

JOB_RUN_PERIODICALLY

If this flag bit is set then job runs on every day where corresponding bits in DaysOfMonth or DaysOfWeek are set. If this flag bit is clear then job runs only once for each bit that was set in DaysOfMonth and/or DaysOfWeek at the time of job submission.

JOB_ADD_CURRENT_DATE

When this flag bit is set then job will also execute at the first occurrence of JobTime at the computer where job is submitted to. In other words, setting this flag bit is equivalent to setting the corresponding day bit in DaysOfMonth bitmask.

For job information retrieval (NetScheduleJobEnum and NetScheduleJobGetInfo) possible values are:

JOB_RUN_PERIODICALLY

This flag bit is equal to the original value of this flag bit when a job was submitted.

JOB_EXEC_ERROR

This flag bit is set whenever Schedule service failed to execute successfully this job the last time this job was supposed to run.

JOB_RUNS_TODAY

This flag bit is set if JobId is larger than the current time of day at the computer where this job is queued at.

Command

Points to a Unicode string that contains the name of command, batch program or binary file to execute.

The schedule service functions are:

NetScheduleJobAdd

NetScheduleJobDel

NetScheduleJobEnum

NetScheduleJobGetInfo

+$#A LAN Manager Functions Not Supported in Windows NT

The following section lists the 16-bit LAN Manager functions that are no longer supported in the Win32 API. In general, the rule we have followed is to not support anything that was marked as obsolete in LAN Manager 2.x. In addition, there are some functions that no longer make sense in Windows NT, and those have been removed as well. Another set of functions has been folded into the Windows NT base or Win32 API.

+$#A Functions with No 32-bit Net Equivalents

• NetAlertStop, NetAlertStart

• NetAuditWrite

• NetBiosClose, NetBiosEnum, NetBiosGetInfo, NetBiosOpen, NetBiosSubmit

• NetCharDev and NetCharDevQ

• NetErrorLogWrite

• NetLogonEnum

• NetHandleGetInfo levels 2 and 3

• NetMessageFileSend, NetMessageLogFileGet, NetMessageLogFileSet, NetMessageNameFwd, NetMessageNameUnFwd

• NetRemoteCopy, NetRemoteExec, NetRemoteMove

• NetServerAdminCommand

• NetWkstaSetUID

+$# Functions That Only Have Support for Remoting to Downlevel

• NetAccess functions

• NetAudit functions

• NetConfig functions

• NetError functions

+$#A Requests from 16-bit LAN Manager Clients

Windows NT provides support for most remote functions called from downlevel clients. However, the following calls are not supported when remoted from a downlevel client to a Windows NT server. In some of the following, there is a more current "2" version of the function that is supported (such as NetServerEnum).

• NetFileEnum

• NetFileGetInfo

• NetFileClose

• NetServerAdminCommand

• NetAuditOpen

• NetAuditClear

• NetErrorLogOpen

• NetErrorLogClear

• NetMessageNameFwd

• NetMessageNameUnFwd

• NetMessageFileSend

• NetMessageLogFileSet

• NetMessageLogFileGet

• NetUserAdd

• NetUserSetInfo

• NetWkstaSetUID

• NetUseEnum

• NetUseAdd

• NetUseDel

• NetUseGetInfo

• NetProfileSave

• NetProfileLoad

• NetStatisticsGet

• NetStatisticsClear

• NetNetBiosEnum

• NetNetBiosGetInfo

• NetServerEnum

• NetConfigGet2

• NetConfigGetAll2

• NetHandleGetInfo

• NetHandleSetInfo

• NetAuditRead

• NetUserValidate2

• NetAccessCheck

• NetAlertRaise

• NetAlertStart

• NetAlertStop

• NetAuditWrite

• NetServiceStatus

• DosPrintDriverEnum

• DosPrintQProcessorEnum

• DosPrintPortEnum

• DosPrintDest

• NetConfigSet

+$#A Calling 16-bit LAN Manager Servers

When an RPC-based function fails to connect to the appropriate interface, the client-side stub may attempt to initiate a down-level function request to the server selected. For most of the Windows networking functions specified in this document, and any API where the functionality and data formats are changed only for 32-bit usage, the conversion is straightforward. For components that offer new functionality the caller of the function should generally be aware of the destination type. When the new function offers a superset of the functionality of the down-level station the same function is used for both destinations, but the new function fields must have either a reserved value of an associated field to inform the conversion layer the field may be ignored if going downlevel. This is required so that a function caller is not misled as to the action performed when the function was called.

Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub gratuit


Large Integer Operations.rtf

+$#AK Large Integer Operations

This overview describes Microsoft Win32 support for operations on large (64-bit) integers.

+$#A About Large Integer Operations

Applications can multiply signed or unsigned 32-bit integers, generating 64-bit results, by using the Int32x32To64 and UInt32x32To64 functions. Applications can shift bits in 64-bit values to the left or right by using the Int64ShllMod32, Int64ShraMod32, and Int64ShrlMod32 functions. These functions provide logical and arithmetic shifting.

Applications can also multiply and divide 32-bit values in a single operation by using the MulDiv function. Although the result of the operation is a 32-bit value, the function stores the intermediate result as a 64-bit value, so that information is not lost when large 32-bit values are multiplied and divided.

+$#A Large Integer Operations Functions

Following are the functions used with large integer operations.

Int32x32To64
Int64ShllMod32
Int64ShraMod32
Int64ShrlMod32
MulDiv
UInt32x32To64

Créé avec HelpNDoc Personal Edition: Générateur facile de livres électroniques et documentation


Legal Information.rtf

+$#A Legal Information

Information in this online help system is subject to change without notice and does not represent a commitment on the part of Microsoft Corporation. The software and/or files described in this online help system are furnished under a license agreement or nondisclosure agreement. The software and/or files may be used or copied only in accordance with the terms of the agreement. The purchaser may make one copy of the software for backup purposes. No part of this online help system may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or information and retrieval systems, for any purpose other than the purchaser's personal use, without the written permission of Microsoft Corporation.

Microsoft may have patents or pending patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property rights except as expressly provided in any written license agreement from Microsoft.

Copyright © 1992-1995 Microsoft Corporation. All rights reserved.

Microsoft, MS, MS-DOS, Win32, Win32s, and Windows are registered trademarks, and Windows NT is a trademark of Microsoft Corporation.

Portions of this documentation are provided under license from Digital Equipment Corporation. Copyright © 1990, 1992 Digital Equipment Corporation. All rights reserved.

DEC, DECnet and PATHWORKS are trademarks of Digital Equipment Corporation.

Créé avec HelpNDoc Personal Edition: Générateur complet de livres électroniques ePub


Lines and Curves.rtf

+$#AK Lines and Curves

Lines and curves are used to draw graphics output on raster devices. As discussed in this overview, a line is a set of highlighted pixels on a raster display (or a set of dots on a printed page) identified by two points: a starting point and an ending point. A regular curve is a set of highlighted pixels on a raster display (or dots on a printed page) that defines the perimeter (or part of the perimeter) of a conic section. An irregular curve is a set of pixels that defines a curve that does not fit the perimeter of a conic section.

+$#A About Lines and Curves

All types of applications written for Microsoft® Windows® use lines and curves to draw graphics output on raster devices. Computer-aided design (CAD) and drawing applications use lines and curves to outline objects, specify the centers of objects, the dimensions of objects, and so on. Spreadsheet applications use lines and curves to draw grids, charts, and graphs. Word processing applications use lines to create rules and borders on a page of text.

+$#A Lines

A line is a set of highlighted pixels on a raster display (or a set of dots on a printed page) identified by two points: a starting point and an ending point. In Windows, the pixel located at the starting point is always included in the line, and the pixel located at the ending point is always excluded. (This kind of line is sometimes called inclusive-exclusive.)

When an application calls one of the Windows line-drawing functions, graphics device interface (GDI), or in some cases a device driver, determines which pixels should be highlighted. GDI is a dynamic-link library (DLL) that processes graphics function calls from a Windows-based application and passes those calls to a device driver. A device driver is a DLL that receives input from GDI, converts the input to device commands, and passes those commands to the appropriate device. GDI uses a digital differential analyzer (DDA) to determine the set of pixels that define a line. A DDA determines the set of pixels by examining each point on the line and identifying those pixels on the display surface (or dots on a printed page) that correspond to the points. The following illustration shows a line, its starting point, its ending point, and the pixels highlighted by using a simple DDA.

{ewc msdncd, EWGraphic, group10660 0 /a "SDK_01.bmp"}

The simplest and most common DDA is the Bresenham, or incremental, DDA. A modified version of this algorithm draws lines in Windows versions 3.x. The incremental DDA is noted for its simplicity, but it is also noted for its inaccuracy. Because it rounds off to the nearest integer value, it sometimes fails to represent the original line requested by the application. The Microsoft® Win32® DDA used by GDI does not round off to the nearest integer. As a result, this new DDA produces output that is sometimes much closer in appearance to the original line requested by the application.

Note If an application requires line output that cannot be achieved with the new DDA, it can draw its own lines by calling the LineDDA function and supplying a private DDA. However, the LineDDA function draws lines much slower than the Windows line-drawing functions. Do not use this function within an application if speed is a primary concern.

An application can use the new DDA to draw single lines and multiple, connected line segments. An application can draw a single line by calling the LineTo function. This function draws a line from the current position up to, but not including, a specified ending point. An application can draw a series of connected line segments by calling the Polyline function, supplying an array of points that specify the ending point of each line segment. An application can draw multiple, disjointed series of connected line segments by calling the PolyPolyline function, supplying the required ending points.

The following illustration shows line output created by calling the LineTo, Polyline, and PolyPolyline functions.

{ewc msdncd, EWGraphic, group10660 1 /a "SDK_02.bmp"}

+$#A Curves

A regular curve is a set of highlighted pixels on a raster display (or dots on a printed page) that define the perimeter (or part of the perimeter) of a conic section. An irregular curve is a set of pixels that define a curve that does not fit the perimeter of a conic section. In Windows, the ending point is excluded from a curve just as it is excluded from a line.

When an application calls one of the Windows curve-drawing functions, GDI breaks the curve into a number of extremely small, discrete line segments. After determining the endpoints (starting point and ending point) for each of these line segments, GDI determines which pixels (or dots) define each line by applying its DDA.

An application can draw an ellipse or part of an ellipse by calling the Arc function. This function draws the curve within the perimeter of an invisible rectangle called a bounding rectangle. The size of the ellipse is specified by two invisible radials extending from the center of the rectangle to the sides of the rectangle. The following illustration shows an arc (part of an ellipse) drawn by using the Arc function.

{ewc msdncd, EWGraphic, group10660 2 /a "SDK_03.bmp"}

When calling the Arc function, an application specifies the coordinates of the bounding rectangle and radials. The preceding illustration shows the rectangle and radials with dashed lines while the actual arc was drawn using a solid line.

When drawing the arc of another object, the application can call the SetArcDirection and GetArcDirection functions to control the direction (clockwise or counterclockwise) in which the object is drawn. The default direction for drawing arcs and other objects is counterclockwise.

In addition to drawing ellipses or parts of ellipses, Windows-based applications can draw irregular curves called Bézier curves. A Bézier curve is an irregular curve whose curvature is defined by four control points (p1, p2, p3, and p4). The control points p1 and p4 define the starting and ending points of the curve, and the control points p2 and p3 define the shape of the curve by marking points where the curve reverses orientation.

{ewc msdncd, EWGraphic, group10660 3 /a "SDK_04.bmp"}

An application can draw irregular curves by calling the PolyBezier function, supplying the appropriate control points.

+$#A Combined Lines and Curves

In addition to drawing lines or curves, Windows-based applications can draw combinations of line and curve output by calling a single function. For example, an application can draw the outline of a pie chart by calling the AngleArc function.

The AngleArc function draws an arc along a circle's perimeter and draws a line connecting the starting point of the arc to the circle's center. In addition to using the AngleArc function, a Windows-based application can also combine line and irregular curve output by using the PolyDraw function.

+$#A Line and Curve Attributes

A device context (DC) contains attributes that affect line and curve output. The line and curve attributes include the current position, brush style, brush color, pen style, pen color, transformation, and so on.

The default current position for any DC is located at the point (0,0) in logical (or world) space. You can set these coordinates to a new position by calling the MoveToEx function and passing a new set of coordinates.

Note Windows provides two sets of line- and curve-drawing functions. The first set retains the current position in a DC, and the second set alters the position. You can identify the functions that alter the current position by examining the function name. If the function name ends with the preposition "To", the function sets the current position to the ending point of the last line drawn (LineTo, ArcTo, PolylineTo, or PolyBezierTo). If the function name does not end with this preposition, it leaves the current position intact (Arc, Polyline, or PolyBezier).

The default brush is a solid white brush. An application can create a new brush by calling the CreateBrushIndirect function. After creating a brush, the application can select it into its DC by calling the SelectObject function. Windows provides a complete set of functions to create, select, and alter the brush in an application's DC. For more information about these functions and about brushes in general, see Brushes.

The default pen is a cosmetic, solid black pen that is one pixel wide. An application can create a pen by using the ExtCreatePen function. After creating a pen, your application can select it into its DC by calling the SelectObject function. Windows provides a complete set of functions to create, select, and alter the pen in an application's DC. For more information about these functions and about pens in general, see Pens.

The default transformation is the unity transformation (specified by the identity matrix). An application can specify a new transformation by calling the SetWorldTransform function. Windows provides a complete set of functions to transform lines and curves by altering their width, location, and general appearance. For more information about these functions, see Coordinate Spaces and Transformations.

+$#A Using Lines and Curves

You can use the line and curve functions to draw virtually any shape or object in the client area of an application window. This section illustrates how these functions can be used to draw markers or a pie chart.

• Drawing markers

• Drawing a pie chart

+$#A Drawing Markers

You can use the line functions to draw markers. A marker is a symbol centered over a point. Drawing applications use markers to designate starting points, ending points, and control points. Spreadsheet applications use markers to designate points of interest on a chart or graph.

In the following code sample, the application-defined Marker function creates a marker by using the MoveToEx and LineTo functions. These functions draw two intersecting lines, 20 pixels in length, centered over the cursor coordinates.

void Marker(LONG x, LONG y, HWND hwnd)

{

HDC hdc;

hdc = GetDC(hwnd);

MoveToEx(hdc, (int) x - 10, (int) y, (LPPOINT) NULL);

LineTo(hdc, (int) x + 10, (int) y);

MoveToEx(hdc, (int) x, (int) y - 10, (LPPOINT) NULL);

LineTo(hdc, (int) x, (int) y + 10);

ReleaseDC(hwnd, hdc);

}

Windows stores the coordinates of the cursor in the lParam parameter of the WM_LBUTTONDOWN message when the user presses the left mouse button. The following code demonstrates how an application gets these coordinates, determines whether they lie within its client area, and passes them to the Marker function to draw the marker.

/* Line- and arc-drawing variables */

static BOOL bCollectPoints;

static POINT ptMouseDown[32];

static int index;

POINTS ptTmp;

RECT rc;

case WM_LBUTTONDOWN:

if (bCollectPoints && index < 32){

/* Create the region from the client area. */

GetClientRect(hwnd, &rc);

hrgn = CreateRectRgn(rc.left, rc.top,

rc.right, rc.bottom);

ptTmp = MAKEPOINTS((POINTS FAR *) lParam);

ptMouseDown[index].x = (LONG) ptTmp.x;

ptMouseDown[index].y = (LONG) ptTmp.y;

/* Test for a hit in the client rectangle. */

if (PtInRegion(hrgn, ptMouseDown[index].x,

ptMouseDown[index].y)) {

/* If a hit occurs, record the mouse coords. */

Marker(ptMouseDown[index].x, ptMouseDown[index].y,

hwnd);

index++;

}

}

break;

+$#A Drawing a Pie Chart

You can use the line and curve functions to draw a pie chart. The primary function used to draw pie charts is the AngleArc function, which requires you to supply the coordinates of the center of the pie, the radius of the pie, a start angle, and a sweep angle. Following is a dialog box that the user can use to enter these values.

{ewc msdncd, EWGraphic, group10660 4 /a "SDK_05.bmp"}

The dialog box template found in the application's resource script (.RC) file specifies characteristics of the preceding dialog box (its height, the controls it contains, and its style), as follows.

AngleArc DIALOG 6, 18, 160, 100

STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION

CAPTION "Pie Chart"

FONT 8, "MS Sans Serif"

BEGIN

EDITTEXT IDD_X, 18, 22, 25, 12, ES_AUTOHSCROLL

LTEXT "X", 102, 4, 24, 9, 8

EDITTEXT IDD_Y, 18, 39, 25, 12, ES_AUTOHSCROLL

LTEXT "Y", 104, 5, 42, 12, 8

LTEXT "Center", 105, 19, 11, 23, 8

EDITTEXT IDD_RADIUS, 103, 9, 32, 12, ES_AUTOHSCROLL

EDITTEXT IDD_STARTANGLE, 103, 31, 32, 12, ES_AUTOHSCROLL

EDITTEXT IDD_SWEEPANGLE, 103, 53, 32, 12, ES_AUTOHSCROLL

LTEXT "Radius", 109, 73, 11, 25, 8

LTEXT "Start Angle", 110, 59, 33, 42, 8

LTEXT "Sweep Angle", 111, 55, 55, 43, 8

PUSHBUTTON "OK", IDD_OK, 9, 82, 40, 14

PUSHBUTTON "Cancel", IDD_CANCEL, 110, 82, 40, 14

END

The dialog box procedure, found in the application's source file, retrieves data (center coordinates, arc radius, and start and sweep angles) by following these steps:

1. The application-defined ClearBits function initializes the array that receives the user-input to zero.

2. The application-defined GetStrLngth function retrieves the length of the string entered by the user.

3. The application-defined RetrieveInput function retrieves the value entered by the user.

The following sample code shows the dialog box procedure.

BOOL CALLBACK ArcDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

CHAR chInput[4]; /* receives control-window input */

int cch; /* array-size and count variable */

switch (uMsg) {

case WM_INITDIALOG:

return FALSE;

case WM_COMMAND:

switch (wParam){

/*

* If the user pressed the OK button, retrieve the

* data that was entered in the various AngleArc

* controls.

*/

case IDD_OK:

/*

* Retrieve the x-coordinate of the arc's

* center.

*/

ClearBits(chInput, sizeof(chInput));

GetDlgItemText(hdlg, IDD_X, chInput,

sizeof(chInput));

cch = GetStrLngth(chInput);

nX = (int)RetrieveInput(chInput, cch);

/*

* Retrieve the y-coordinate of the arc's

* center.

*/

ClearBits(chInput, sizeof(chInput));

GetDlgItemText(hdlg, IDD_Y, chInput,

sizeof(chInput));

cch = GetStrLngth(chInput);

nY = (int)RetrieveInput(chInput, cch);

/* Retrieve the radius of the arc. */

ClearBits(chInput, sizeof(chInput));

GetDlgItemText(hdlg, IDD_RADIUS, chInput,

sizeof(chInput));

cch = GetStrLngth(chInput);

dwRadius = (DWORD) RetrieveInput(chInput, cch);

/* Retrieve the start angle. */

ClearBits(chInput, sizeof(chInput));

GetDlgItemText(hdlg, IDD_STARTANGLE, chInput,

sizeof(chInput));

cch = GetStrLngth(chInput);

xStartAngle = (float) RetrieveInput(chInput,

cch);

/* Retrieve the sweep angle. */

ClearBits(chInput, sizeof(chInput));

GetDlgItemText(hdlg, IDD_SWEEPANGLE, chInput,

sizeof(chInput));

cch = GetStrLngth(chInput);

xSweepAngle = (float) RetrieveInput(chInput,

cch);

EndDialog(hdlg, FALSE);

return TRUE;

/*

* If user presses the CANCEL button, close the

* dialog box.

*/

case IDD_CANCEL:

EndDialog(hdlg, FALSE);

return TRUE;

} /* end switch (wParam) */

break;

default:

return FALSE;

} /* end switch (message) */

UNREFERENCED_PARAMETER(lParam);

}

void ClearBits(LPTSTR cArray, int iLength)

{

int i;

for (i = 0; i < iLength; i++)

cArray[i] = 0;

}

int GetStrLngth(LPTSTR cArray)

{

int i = 0;

while (cArray[i++] != 0);

return i - 1;

}

DWORD RetrieveInput(LPTSTR cArray, int iLength)

{

int i, iTmp;

double dVal, dCount;

dVal = 0.0;

dCount = (double) (iLength - 1);

/* Convert ASCII input to a floating-point value. */

for (i = 0; i < iLength; i++) {

iTmp = cArray[i] - 0x30;

dVal = dVal + (((double)iTmp) * pow(10.0, dCount--));

}

return (DWORD) dVal;

}

To draw each section of the pie chart, pass the values entered by the user to the AngleArc function. To fill the pie chart using the current brush, embed the call to AngleArc in a path bracket. The following code sample shows the defined path bracket and the call to AngleArc.

int nX;

int nY;

DWORD dwRadius;

float xStartAngle;

float xSweepAngle;

case (IDM_ANGLEARC):

DialogBox((HINSTANCE) GetModuleHandle (NULL),

(LPTSTR) "AngleArc",

hwnd, (DLGPROC) ArcDlgProc);

hdc = GetDC(hwnd);

BeginPath(hdc);

SelectObject(hdc,

GetStockObject(GRAY_BRUSH));

MoveToEx(hdc, nX, nY, (LPPOINT) NULL);

AngleArc(hdc, nX, nY, dwRadius,

xStartAngle, xSweepAngle);

LineTo(hdc, nX, nY);

EndPath(hdc);

StrokeAndFillPath(hdc);

ReleaseDC(hwnd, hdc);

break;

+$#A Line and Curve Functions

Following are the functions used with lines and curves.

AngleArc
Arc
ArcTo
GetArcDirection
LineDDA
LineDDAProc
LineTo
MoveToEx
PolyBezier
PolyBezierTo
PolyDraw
Polyline
PolylineTo
PolyPolyline
SetArcDirection

Créé avec HelpNDoc Personal Edition: Création d'aide CHM, PDF, DOC et HTML d'une même source


List Boxes.rtf

+$#AK List Boxes

The Microsoft® Win32® application programming interface (API) provides dialog boxes and controls to support communication between an application and the user. A list box is a control window that contains a list of items from which the user can choose.

+$#A About List Boxes

List box items can be represented by text strings, bitmaps, or both. If the list box is not large enough to display all the list box items at once, the list box can provide a scroll bar. The user maneuvers through the list box items, scrolling the list when necessary, and selects or removes the selection from items. Selecting a list box item changes its visual appearance, usually by changing the text and background colors to the colors specified by the operating system metrics for selected items. When the user selects an item or removes the selection from an item, Windows sends a notification message to the parent window of the list box.

A dialog box procedure is responsible for initializing and monitoring its child windows, including any list boxes. The dialog box procedure communicates with the list box by sending messages to it and by processing the notification messages sent by the list box. For more information about dialog boxes, see Dialog Boxes.

For general information about controls, see Controls.

+$#A List Box Styles

The Win32 API provides two general styles of list box: single-selection (the default style) and multiple-selection. In a single-selection list box, the user can select only one item at a time. In a multiple-selection list box, the user can select more than one item at a time; an application must specify either the LBS_MULTIPLESEL or the LBS_EXTENDEDSEL style for a multiple-selection list box.

The Win32 API provides many other list box and window styles that control the appearance and operation of a list box. These styles indicate whether list box items are sorted, arranged in multiple columns, drawn by the application, and so on. The dimensions and styles of a list box are typically defined in a dialog box template included in an application's resources. For information about how to create a dialog box template and include it in an application, see the documentation provided with your development tools.

To create a list box by using the CreateWindow or CreateWindowEx function, use the LISTBOX class, appropriate window style constants, and the following style constants to define the list box.

List box style | Description
LBS_DISABLENOSCROLL | Shows a disabled vertical scroll bar for the list box when the box does not contain enough items to scroll. If you do not specify this style, the scroll bar is hidden when the list box does not contain enough items.
LBS_EXTENDEDSEL | Allows multiple items to be selected by using the SHIFT key and the mouse or special key combinations.
LBS_HASSTRINGS | Specifies that a list box contains items consisting of strings. The list box maintains the memory and addresses for the strings so that the application can use the LB_GETTEXT message to retrieve the text for a particular item. By default, all list boxes except owner-drawn list boxes have this style. You can create an owner-drawn list box either with or without this style.
LBS_MULTICOLUMN | Specifies a multicolumn list box that is scrolled horizontally. The LB_SETCOLUMNWIDTH message sets the width of the columns.
LBS_MULTIPLESEL | Turns string selection on or off each time the user clicks or double-clicks a string in the list box. The user can select any number of strings.
LBS_NODATA | Obsolete.
LBS_NOINTEGRALHEIGHT | Specifies that the size of the list box is exactly the size specified by the application when it created the list box. Normally, Windows sizes a list box so that the list box does not display partial items.
LBS_NOREDRAW | Specifies that the list box's appearance is not updated when changes are made. You can change this style by sending a WM_SETREDRAW message at any time.
LBS_NOSEL | Specifies that the list box contains items that can be viewed but not selected.
LBS_NOTIFY | Notifies the parent window with an input message whenever the user clicks or double-clicks a string in the list box.
LBS_OWNERDRAWFIXED | Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are the same height. The owner window receives a WM_MEASUREITEM message when the list box is created and a WM_DRAWITEM message when a visual aspect of the list box has changed.
LBS_OWNERDRAWVARIABLE | Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are variable in height. The owner window receives a WM_MEASUREITEM message for each item in the combo box when the combo box is created and a WM_DRAWITEM message when a visual aspect of the combo box has changed.
LBS_SORT | Sorts strings in the list box alphabetically.
LBS_STANDARD | Sorts strings in the list box alphabetically. The parent window receives an input message whenever the user clicks or double-clicks a string. The list box has borders on all sides.
LBS_USETABSTOPS | Enables a list box to recognize and expand tab characters when drawing its strings. The default tab positions are 32 dialog box units. A dialog box unit is a horizontal or vertical distance. One horizontal dialog box unit is equal to 0.25 of the current dialog box base-width unit. Windows calculates these units based on the height and width of the current system font. The GetDialogBaseUnits function returns the current dialog box base units in pixels.
LBS_WANTKEYBOARDINPUT | Specifies that the owner of the list box receives WM_VKEYTOITEM messages whenever the user presses a key and the list box has the input focus. This enables an application to perform special processing on the keyboard input.

+$#A List Box Functions Overview

The Win32 API provides two functions specific to list boxes. The DlgDirList function fills a list box with the files and directories in a specified path. The DlgDirSelectEx function retrieves the current selection in a list box initialized by DlgDirList. These functions make it possible for the user to select a file from a list box without typing the location and name of the file.

+$#A Notification Messages from List Boxes

When an event occurs in a list box, the list box sends a notification message to the dialog box procedure of the owner window. List box notification messages are sent when a user selects, double-clicks, or cancels a list box item; when the list box receives or loses the keyboard focus; and when the system cannot allocate enough memory for a list box request. A notification message is sent as a WM_COMMAND message in which the low-order word of the wParam parameter contains the list box identifier, the high-order word of wParam contains the notification message, and the lParam parameter contains the control window handle.

Portability Issue The placement of notification message has moved from the lParam parameter to the wParam parameter. Windows 3.x−based applications that process notification messages must be modified in this respect when they are ported to Win32-based applications.

A dialog box procedure is not required to process these messages; the default window procedure processes them.

An application should monitor and process the following list box notification messages.

Notification message | Description
LBN_DBLCLK | The user double-clicks an item in the list box.
LBN_ERRSPACE | The list box cannot allocate enough memory to fulfill a request.
LBN_KILLFOCUS | The list box loses the keyboard focus.
LBN_SELCANCEL | The user cancels the selection of an item in the list box.
LBN_SELCHANGE | The selection in a list box is about to change.
LBN_SETFOCUS | The list box receives the keyboard focus.

+$#A Messages to List Boxes

A dialog box procedure can send messages to a list box to add, delete, examine, and change list box items. For example, a dialog box procedure could send an LB_ADDSTRING message to a list box to add an item, and an LB_GETSEL message to determine whether the item is selected. Other messages set and retrieve information about the size, appearance, and behavior of the list box. For example, the LB_SETHORIZONTALEXTENT message sets the scrollable width of a list box. A dialog box procedure can send any message to a list box by using the SendMessage or SendDlgItemMessage function.

A list box item is often referenced by its index, an integer that represents the item's position in the list box. The index of the first item in a list box is 0, the index of the second item is 1, and so on.

The following table describes how the predefined list box procedure responds to list box messages.

Message | Response
LB_ADDFILE | Inserts a file into a directory list box filled by the DlgDirList function and retrieves the list box index of the inserted item.
LB_ADDSTRING | Adds a string to a list box and returns its index.
LB_DELETESTRING | Removes a string from a list box and returns the number of strings remaining in the list.
LB_DIR | Adds a list of filenames to a list box and returns the index of the last filename added.
LB_FINDSTRING | Returns the index of the first string in the list box that matches a given prefix.
LB_FINDSTRINGEXACT | Returns the index of the string that is equivalent to or prefixed by a given prefix.
LB_GETANCHORINDEX | Returns the index of the item that the mouse last selected.
LB_GETCARETINDEX | Returns the index of the item that has the focus rectangle.
LB_GETCOUNT | Returns the number of items in the list box.
LB_GETCURSEL | Returns the index of the currently selected item.
LB_GETHORIZONTALEXTENT | Returns the scrollable width, in pixels, of a list box.
LB_GETITEMDATA | Returns the 32-bit value associated with the given item.
LB_GETITEMHEIGHT | Returns the height, in pixels, of an item in a list box.
LB_GETITEMRECT | Retrieves the client coordinates of the given list box item.
LB_GETLOCALE | Retrieves the locale of the list box. The high-order word contains the country code and the low-order word contains the language identifier.
LB_GETSEL | Returns the selection state of a list box item.
LB_GETSELCOUNT | Returns the number of selected items in a multiple-selection list box.
LB_GETSELITEMS | Creates an array of the indexes of all selected items in a multiple-selection list box and returns the total number of selected items.
LB_GETTEXT | Retrieves the string associated with a given item and the length of the string.
LB_GETTEXTLEN | Returns the length, in characters, of the string associated with a given item.
LB_GETTOPINDEX | Returns the index of the first visible item in a list box.
LB_INITSTORAGE | Allocates memory for the specified number of items and their associated strings.
LB_INSERTSTRING | Inserts a string at a given index in a list box.
LB_ITEMFROMPOINT | Retrieves the zero-based index of the item nearest the specified point in a list box.
LB_RESETCONTENT | Removes all items from a list box.
LB_SELECTSTRING | Selects the first string it finds that matches a given prefix.
LB_SELITEMRANGE | Selects a given range of items in a list box.
LB_SELITEMRANGEEX | Selects a given range of items if the index of the first item in the range is less than the index of the last item in the range. Cancels the selection in the range if the index of the first item is greater than the last.
LB_SETANCHORINDEX | Sets the item that the mouse last selected to a given item.
LB_SETCARETINDEX | Sets the focus rectangle to a given list box item.
LB_SETCOLUMNWIDTH | Sets the width, in pixels, of all columns in a list box.
LB_SETCOUNT | Sets the number of items in a list box.
LB_SETCURSEL | Selects a given list box item.
LB_SETHORIZONTALEXTENT | Sets the scrollable width, in pixels, of a list box.
LB_SETITEMDATA | Associates a 32-bit value with a list box item.
LB_SETITEMHEIGHT | Sets the height, in pixels, of an item or items in a list box.
LB_SETLOCALE | Sets the locale of a list box and returns the previous locale identifier.
LB_SETSEL | Selects an item in a multiple-selection list box.
LB_SETTABSTOPS | Sets the tab stops to those specified in a given array.
LB_SETTOPINDEX | Scrolls the list box so the specified item is at the top of the visible range.

+$#A Default Window-Message Processing

The window procedure for the predefined list box window class carries out default processing for all messages that the list box does not process. When the list box procedure returns FALSE for a message, the predefined window procedure checks the message and performs default actions, as shown in the following table.

Message

Default action

WM_CHAR

Moves the selection to the first item that begins with the character the user typed. If the list box has the LBS_OWNERDRAW style, no action occurs.

Multiple characters typed within a short interval are treated as a group, and the first item that begins with that series of characters is selected.

WM_CREATE

Creates an empty list box.

WM_DESTROY

Destroys the list box and frees any resources it uses.

WM_DROPFILES

Passes the message to the dialog box procedure or parent window process.

WM_ENABLE

If the control is visible, invalidates the rectangle so the strings can be painted gray.

WM_ERASEBKGND

Erases the background of a list box. If the list box has the LBS_OWNERDRAW style, the background is not erased.

WM_GETDLGCODE

Returns DLGC_WANTARROWS | DLGC_WANTCHARS, indicating the default list box procedure processes the arrow keys and WM_CHAR messages.

WM_GETFONT

Returns a handle of the current font for the list box.

WM_HSCROLL

Scrolls the list box horizontally.

WM_KEYDOWN

Processes virtual keys for scrolling. The virtual key is the index of the item to move the caret to. The selection is not changed.

WM_KILLFOCUS

Turns the caret off and destroys it. Sends an LBN_KILLFOCUS notification message to the owner of the list box.

WM_LBUTTONDBLCLK

Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.

WM_LBUTTONDOWN

Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.

WM_LBUTTONUP

Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.

WM_MOUSEMOVE

Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area.

WM_PAINT

Performs a subclassed paint operation by using the list box handle of the device context (DC).

WM_SETFOCUS

Turns the caret on and sends an LBN_SETFOCUS notification message to the owner of the list box.

WM_SETFONT

Sets a new font for the list box.

WM_SETREDRAW

Sets or clears the redraw flag based on the value of wParam.

WM_SIZE

Resizes the list box to an integral number of items.

WM_VSCROLL

Scrolls the list box vertically.

The predefined list box procedure passes all other messages to DefWindowProc for default processing.

+$#A Using List Boxes

• Creating a simple list box

• Creating a directory listing in a single-selection list box

• Creating a multiple-selection list box

• Creating an owner-drawn list box

+$#A Creating a Simple List Box

The following example demonstrates how a dialog box procedure creates a simple list box and fills it with the names of people on a softball team. When a name in the list is selected, additional information about the player is displayed in the dialog box. The following illustration shows the dialog box.

{ewc msdncd, EWGraphic, group10616 0 /a "SDK_01.bmp"}

The list box has the LBS_STANDARD style, a combination of LBS_SORT, LBS_NOTIFY, WS_VSCROLL, and WS_BORDER. The code initializes the dialog box while processing the WM_INITDIALOG message. For each name that appears in the list box, the code sends an LB_ADDSTRING message to the list box. By processing the LBN_SELCHANGE notification message, the code also keeps track of when the selection changes.

#define BUFFER MAX_PATH

#define NAMELENGTH 15

#define POSITIONLENGTH 20

#define TEAMSIZE 15

typedef struct {

TCHAR tchName[NAMELENGTH];

TCHAR tchPosition[POSITIONLENGTH];

int nGamesPlayed;

int nInningsPlayed;

double xBattingAverage;

TCHAR tchFoodName[NAMELENGTH];

} Player;

Player Roster[] = {

{"Pete", "shortstop", 26, 90, .608, "Rutabaga"},

{"Suzanna", "catcher", 16, 53, .286, "Toast"},

{"Jack", "pitcher", 27, 110, .542, "Animal Crackers"},

{"Karen", "second base", 26, 140, .238, "Pez"},

{"Dave", "first base", 28, 138, .508, "Suds"},

{"Wendy", "third base", 25, 154, .493, "Ham"},

{"Matt", "shortstop", 24, 112, .579, "Oats"},

{"Jenny", "right field", 22, 101, .509, "Mashed Potatoes"},

{"Seth", "left-center field", 20, 76, .407, "Otter Pop"},

{"Kathie", "left field", 26, 127, .353, "Baba Ganouj"},

{"Colin", "pitcher", 26, 96, .456, "Lefse"},

{"Penny", "right field", 24, 112, .393, "Zotz"},

{"Art", "left-center field", 17, 56, .375, "Cannelloni"},

{"Cindy", "second base", 13, 58, .207, "Tequila"},

{"David", "center field", 18, 101, .612, "Bok Choy"}

};

/*

* FUNCTION: DlgTeamProc(HWND, unsigned, UINT, LONG)

*

* PURPOSE: Dialog box for "BFG Softball Statistics"

*/

BOOL APIENTRY DlgTeamProc(

HWND hDlg, /* window handle of dialog box */

UINT message, /* type of message */

UINT wParam, /* message-specific information */

LONG lParam) /* message-specific information */

{

TCHAR tchBuffer[BUFFER];

int nItem;

int i;

HWND hwndList;

switch (message) {

case WM_INITDIALOG:

{

hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);

/* Initialize the list box (fill it with player names). */

for (i = 0; i < TEAMSIZE; i++) {

SendMessage(hwndList, LB_ADDSTRING, 0,

(LPARAM) Roster[i].tchName);

SendMessage(hwndList, LB_SETITEMDATA, i, (LPARAM) i);

}

SetFocus(hwndList);

return FALSE;

}

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDL_SOFTBALL:

switch (HIWORD(wParam)) {

case LBN_SELCHANGE:

/* Show the selected player's statistics. */

hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);

nItem = SendMessage(hwndList, LB_GETCURSEL,

0, 0);

i = SendMessage(hwndList, LB_GETITEMDATA,

nItem, 0);

SetDlgItemText(hDlg, IDS_POS,

Roster[i].tchPosition);

SetDlgItemText(hDlg, IDS_GAME,

_itoa(Roster[i].nGamesPlayed,

tchBuffer, 10));

SetDlgItemText(hDlg, IDS_INN,

_itoa(Roster[i].nInningsPlayed,

tchBuffer, 10));

SetDlgItemText(hDlg, IDS_BA,

_gcvt(Roster[i].xBattingAverage,

3, tchBuffer));

SetDlgItemText(hDlg, IDS_FOOD,

Roster[i].tchFoodName);

return TRUE;

}

break;

case IDOK:

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);

return TRUE;

default:

return FALSE;

}

default:

return FALSE;

}

}

+$#A Creating a Directory Listing in a Single-Selection List Box

The following example demonstrates how to display the content of the current directory in a list box and enable the user to delete one file at a time.

In addition to the standard list box styles, this list box has the LBS_MULTICOLUMN and LBS_HSCROLL styles. The code initializes the list box by using the DlgDirList function to fill the list box with the names of all the files in the current directory. When the user chooses the Delete button, the DlgDirSelectEx function retrieves the name of the selected file. The code deletes the file by using the DeleteFile function and updates the directory list box by sending the LB_DELETESTRING message.

#define BUFFER MAX_PATH

/*

* FUNCTION: DlgDelFileProc(HWND, unsigned, UINT, LONG)

*

* PURPOSE: "Delete File" dialog box

*/

BOOL APIENTRY DlgDelFileProc(

HWND hDlg, /* window handle of dialog box */

UINT message, /* type of message */

UINT wParam, /* message-specific information */

LONG lParam)

{

DWORD cchCurDir;

LPTSTR lpszCurDir;

LPTSTR lpszFileToDelete;

int nItem;

int nTotal;

TCHAR tchBuffer[BUFFER];

BOOL fResult;

switch (message) {

case WM_INITDIALOG:

/*

* Initialize the list box by filling it with files from

* the current directory.

*/

lpszCurDir = tchBuffer;

GetCurrentDirectory(cchCurDir, lpszCurDir);

DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);

SetFocus(GetDlgItem(hDlg, IDL_FILES));

return FALSE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

/*

* When the user presses the DEL (IDOK) button,

* delete the selected file.

*/

lpszFileToDelete = tchBuffer;

DlgDirSelectEx(hDlg, lpszFileToDelete, MAX_PATH,

IDL_FILES);

fResult = DeleteFile(lpszFileToDelete);

if (!fResult) {

MessageBox(hDlg, "Could not delete file.",

NULL, MB_OK);

}

else {

nItem = SendMessage(GetDlgItem(hDlg, IDL_FILES),

LB_GETCURSEL, 0, 0);

nTotal = SendMessage(GetDlgItem(hDlg,

IDL_FILES), LB_DELETESTRING, nItem, 0);

/* Update the list box. */

if (nTotal > nItem) {

SendMessage(GetDlgItem(hDlg, IDL_FILES),

LB_SETCURSEL, nItem, 0);

}

else {

SendMessage(GetDlgItem(hDlg, IDL_FILES),

LB_SETCURSEL, nTotal, 0);

}

}

return TRUE;

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);

return TRUE;

default:

return FALSE;

}

default:

return FALSE;

}

}

+$#A Creating a Multiple-Selection List Box

The following example displays and initializes the dialog box used in the preceding example. However, this code uses the LBS_MULTIPLESEL style to enable the user to select more than one file at a time. When the user chooses the Delete button, the example sends the LB_GETSELCOUNT message (to retrieve the number of files selected) and the LB_GETSELITEMS message (to retrieve an array of selected list box items). After deleting a file, the code removes the corresponding item from the list box by sending the LB_DELETESTRING message.

#define BUFFER MAX_PATH

#define BIGBUFF 8192

/*

* FUNCTION: DlgDelFilesProc(HWND, unsigned, UINT, LONG)

*

* PURPOSE: "Delete files" dialog box

*/

BOOL APIENTRY DlgDelFilesProc(

HWND hDlg, /* window handle of dialog box */

UINT message, /* type of message */

UINT wParam, /* message-specific information */

LONG lParam)

{

DWORD cchCurDir;

LPTSTR lpszCurDir;

LPTSTR lpszFileToDelete;

int nSelItems;

int nSelItemsInBuffer;

TCHAR tchBuffer[BUFFER];

TCHAR tchMsgBuff[BUFFER];

int nBuffer[BIGBUFF];

int i;

BOOL fResult;

HWND hListBox;

switch (message) {

case WM_INITDIALOG:

/*

* Initialize the list box by filling it with files from

* the current directory.

*/

lpszCurDir = tchBuffer;

GetCurrentDirectory(cchCurDir, lpszCurDir);

DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);

SetFocus(GetDlgItem(hDlg, IDL_FILES));

return FALSE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

/*

* When the user presses the Delete (IDOK)

* button, delete all the selected files.

*/

lpszFileToDelete = tchBuffer;

hListBox = GetDlgItem(hDlg, IDL_FILES);

nSelItems = SendMessage(hListBox,

LB_GETSELCOUNT, 0, 0);

nSelItemsInBuffer = SendMessage(hListBox,

LB_GETSELITEMS, 512, (LPARAM) nBuffer);

if (nSelItems > nSelItemsInBuffer) {

MessageBox(hDlg, "Too many items selected.",

NULL, MB_OK);

}

else {

/*

* Go through the list backwards because after

* deleting an item the indices change for every

* subsequent item. By going backward, the

* indice are never invalidated.

*/

for (i = nSelItemsInBuffer - 1; i >= 0; i--) {

SendMessage(hListBox, LB_GETTEXT,

nBuffer[i],

(LPARAM) lpszFileToDelete);

fResult = DeleteFile(lpszFileToDelete);

if (!fResult) {

sprintf(tchMsgBuff,

"Could not delete file: %s "

"GetLastError = %u",

(LPARAM) lpszFileToDelete);

/* Call app-defined error handler. */

ErrorHandler(tchMsgBuff);

}

else {

SendMessage(hListBox, LB_DELETESTRING,

nBuffer[i], 0);

}

}

SendMessage(hListBox, LB_SETCARETINDEX, 0, 0);

}

return TRUE;

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);

return TRUE;

default:

return FALSE;

}

default:

return FALSE;

}

}

+$#A Creating an Owner-Drawn List Box

The following example shows how to draw a list box that contains five owner-drawn items: four drawing implements and a fork. Each list item appears as a bitmap followed by the name of the object. A button prompts the user to select one item that is not like the others. Choosing the button with the fork selected displays a "You're right!" message and closes the dialog box. Choosing the button with any other list item selected displays a "Try again!" message.

The list box has the LBS_OWNERDRAW and LBS_HASSTRINGS styles, in addition to the standard list box styles. The code initializes the list box by sending the LB_ADDSTRING message to set the text, and then sends the LB_SETITEMDATA message to associate a bitmap with each list box item. The code also sets the height of each list box item by processing the WM_MEASUREITEM message and draws the text and bitmap for each item by processing the WM_DRAWITEM message.

#define XBITMAP 80

#define YBITMAP 20

#define BUFFER MAX_PATH

HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork;

HBITMAP hbmpPicture, hbmpOld;

void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp)

{

int nItem;

nItem = SendMessage(hwndList, LB_ADDSTRING, 0, lpstr);

SendMessage(hwndList, LB_SETITEMDATA, nItem, hbmp);

}

DWORD APIENTRY DlgDrawProc(

HWND hDlg, /* window handle of dialog box */

UINT message, /* type of message */

UINT wParam, /* message-specific information */

LONG lParam)

{

int nItem;

TCHAR tchBuffer[BUFFER];

HBITMAP hbmp;

HWND hListBox;

TEXTMETRIC tm;

int y;

HDC hdcMem;

LPMEASUREITEMSTRUCT lpmis;

LPDRAWITEMSTRUCT lpdis;

RECT rcBitmap;

switch (message) {

case WM_INITDIALOG:

/* Load bitmaps. */

hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700));

hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701));

hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702));

hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703));

hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704));

/* Retrieve list box handle. */

hListBox = GetDlgItem(hDlg, IDL_STUFF);

/*

* Initialize the list box text and associate a bitmap

* with each list box item.

*/

AddItem(hListBox, "pencil", hbmpPencil);

AddItem(hListBox, "crayon", hbmpCrayon);

AddItem(hListBox, "marker", hbmpMarker);

AddItem(hListBox, "pen", hbmpPen);

AddItem(hListBox, "fork", hbmpFork);

SetFocus(hListBox);

SendMessage(hListBox, LB_SETCURSEL, 0, 0);

return TRUE;

case WM_MEASUREITEM:

lpmis = (LPMEASUREITEMSTRUCT) lParam;

/* Set the height of the list box items. */

lpmis->itemHeight = 20;

return TRUE;

case WM_DRAWITEM:

lpdis = (LPDRAWITEMSTRUCT) lParam;

/* If there are no list box items, skip this message. */

if (lpdis->itemID == -1) {

break;

}

/*

* Draw the bitmap and text for the list box item. Draw a

* rectangle around the bitmap if it is selected.

*/

switch (lpdis->itemAction) {

case ODA_SELECT:

case ODA_DRAWENTIRE:

/* Display the bitmap associated with the item. */

hbmpPicture = (HBITMAP) SendMessage(lpdis->hwndItem,

LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0);

hdcMem = CreateCompatibleDC(lpdis->hDC);

hbmpOld = SelectObject(hdcMem, hbmpPicture);

BitBlt(lpdis->hDC,

lpdis->rcItem.left, lpdis->rcItem.top,

lpdis->rcItem.right - lpdis->rcItem.left,

lpdis->rcItem.bottom - lpdis->rcItem.top,

hdcMem, 0, 0, SRCCOPY);

/* Display the text associated with the item. */

SendMessage(lpdis->hwndItem, LB_GETTEXT,

lpdis->itemID, (LPARAM) tchBuffer);

GetTextMetrics(lpdis->hDC, &tm);

y = (lpdis->rcItem.bottom + lpdis->rcItem.top -

tm.tmHeight) / 2;

TextOut(lpdis->hDC,

XBITMAP + 6,

y,

tchBuffer,

strlen(tchBuffer));

SelectObject(hdcMem, hbmpOld);

DeleteDC(hdcMem);

/* Is the item selected? */

if (lpdis->itemState & ODS_SELECTED) {

/*

* Set RECT coordinates to surround only the

* bitmap.

*/

rcBitmap.left = lpdis->rcItem.left;

rcBitmap.top = lpdis->rcItem.top;

rcBitmap.right = lpdis->rcItem.left + XBITMAP;

rcBitmap.bottom = lpdis->rcItem.top + YBITMAP;

/*

* Draw a rectangle around bitmap to indicate

* the selection.

*/

DrawFocusRect(lpdis->hDC, &rcBitmap);

}

break;

case ODA_FOCUS:

/*

* Do not process focus changes. The focus caret

* (outline rectangle) indicates the selection.

* The Which one? (IDOK) button indicates the final

* selection.

*/

break;

}

return TRUE;

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDOK:

/* Get the selected item's text. */

nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF),

LB_GETCURSEL, 0, (LPARAM) 0);

hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF),

LB_GETITEMDATA, nItem, 0);

/*

* If the item is not the correct answer, tell the

* user to try again.

*

* If the item is the correct answer, congratulate

* the user and destroy the dialog box.

*/

if (hbmp != hbmpFork) {

MessageBox(hDlg, "Try again!", "Oops.", MB_OK);

return FALSE;

}

else {

MessageBox(hDlg, "You're right!",

"Congratulations.", MB_OK);

/* Fall through. */

}

case IDCANCEL:

/* Destroy the dialog box. */

EndDialog(hDlg, TRUE);

return TRUE;

default:

return FALSE;

}

case WM_DESTROY:

/* Free any resources used by the bitmaps. */

DeleteObject(hbmpPencil);

DeleteObject(hbmpCrayon);

DeleteObject(hbmpMarker);

DeleteObject(hbmpPen);

DeleteObject(hbmpFork);

return TRUE;

default:

return FALSE;

}

return FALSE;

}

+$#AK List Box Reference

The following functions and messages are associated with list boxes.

+$#A List Box Functions

Following are the functions used with list boxes.

DlgDirList
DlgDirSelectEx

+$#A List Box Messages

Following are the messages used with list boxes.

LB_ADDFILE
LB_ADDSTRING
LB_DELETESTRING
LB_DIR
LB_FINDSTRING
LB_FINDSTRINGEXACT
LB_GETANCHORINDEX
LB_GETCARETINDEX
LB_GETCOUNT
LB_GETCURSEL
LB_GETHORIZONTALEXTENT
LB_GETITEMDATA
LB_GETITEMHEIGHT
LB_GETITEMRECT
LB_GETLOCALE
LB_GETSEL
LB_GETSELCOUNT
LB_GETSELITEMS
LB_GETTEXT
LB_GETTEXTLEN
LB_GETTOPINDEX
LB_INSERTSTRING
LB_RESETCONTENT
LB_SELECTSTRING
LB_SELITEMRANGE
LB_SELITEMRANGEEX
LB_SETANCHORINDEX
LB_SETCARETINDEX
LB_SETCOLUMNWIDTH
LB_SETCOUNT
LB_SETCURSEL
LB_SETHORIZONTALEXTENT
LB_SETITEMDATA
LB_SETITEMHEIGHT
LB_SETLOCALE
LB_SETSEL
LB_SETTABSTOPS
LB_SETTOPINDEX
LBN_DBLCLK
LBN_ERRSPACE
LBN_KILLFOCUS
LBN_SELCANCEL
LBN_SELCHANGE
LBN_SETFOCUS
WM_CHARTOITEM
WM_DELETEITEM
WM_VKEYTOITEM

Créé avec HelpNDoc Personal Edition: Générateur de documentation complet


List View Controls.rtf

+$#AK List View Controls

A list view control is a window that displays a collection of items, each item consisting of an icon and a label. List view controls provide several ways of arranging items and displaying individual items. For example, additional information about each item can be displayed in columns to the right of the icon and label.

+$#A About List View Controls

You can create a list view control by calling the CreateWindowEx function, specifying the WC_LISTVIEW window class. This window class is registered when the common controls dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls function.

An application sends messages to a list view control to add, remove, arrange, and otherwise manipulate items. Each message has a macro that you can use instead of sending the message explicitly.

Like most common controls, a list view control sends notification messages to its parent window in the form of WM_NOTIFY messages. For more information about common controls, see Common Controls.

+$#A Views and Styles

List view controls can display their contents in four different views. The current view is specified by the control's window style. Additional window styles specify the alignment of items and control-specific functionality of the list view control. Information about the four views follows.

View | Description
Icon view | Specified by the LVS_ICON window style. Each item appears as a full-sized icon with a label below it. The user can drag the items to any location in the list view window.
Small icon view | Specified by the LVS_SMALLICON window style. Each item appears as a small icon with the label to the right of it. The user can drag the items to any location.
List view | Specified by the LVS_LIST window style. Each item appears as a small icon with a label to the right of it. Items are arranged in columns and cannot be dragged to any arbitrary location by the user.
Report view | Specified by the LVS_REPORT window style. Each item appears on its own line with information arranged in columns. The leftmost column contains the small icon and label, and subsequent columns contain subitems as specified by the application. Unless the LVS_NOCOLUMNHEADER window style is also specified, each column has a header.

You can change the view type after a list view control is created. To retrieve and change the window style, use the GetWindowLong and SetWindowLong functions. To determine the window styles that correspond to the current view, use the LVS_TYPEMASK value.

You can control the way items are arranged in icon or small icon view by specifying either the LVS_ALIGNTOP (default) or LVS_ALIGNLEFT window style. You can change the alignment after a list view control is created. To isolate the window styles that specify the alignment of items, use the LVS_ALIGNMASK value.

Additional window styles control other options ⎯ for example, whether a user can edit labels in place, whether more than one item can be selected at a time, and so on.

+$#A List View Image Lists

The icons for list view items are contained in image lists, which you create and assign to the list view control. One image list contains the full-sized icons used in icon view, and a separate image list contains smaller versions of the same icons for use in other views. You can also specify a third image list that contains state images, which are displayed next to an item's icon to indicate an application-defined state.

You assign an image list to a list view control by using the LVM_SETIMAGELIST message, specifying whether the image list contains full-sized icons, small icons, or state images. You can use the GetSystemMetrics function to determine appropriate dimensions for the full-sized and small icons and the ImageList_Create function to create the image lists. For more information about image lists, see Image Lists.

You can retrieve the handle of an image list currently assigned to a list view control by using the LVM_GETIMAGELIST message.

The full-sized and small icon image lists typically contain icons for each type of list view item. You do not need to create both of these image lists if only one is used ⎯ for example, if a list view control is never in icon view. If you create both image lists, they must contain the same images in the same order because a single value is used to identify a list view item's icon in both image lists.

The full-sized and small icon image lists can also contain overlay images, which are designed to be superimposed on item icons. A nonzero value in bits 8 through 11 of a list view item's state specifies the one-based index of an overlay image (zero indicates no overlay image). Because a 4-bit, one-based index is used, overlay images must be among the first 15 images in the image lists. For more information about list view item states, see Item States.

If a state image list is specified, a list view control reserves space to the left of each item's icon for a state image. An application can use state images, such as checked and cleared check boxes, to indicate application-defined item states. A nonzero value in bits 12 through 15 specifies the one-based index of a state image (zero indicates no state image). State images are typically not used in icon view.

By default, a list view control destroys the image lists assigned to it when it is destroyed. However, if a list view control has the LVS_SHAREIMAGELISTS window style, the application is responsible for destroying the image lists when they are no longer in use. You should specify this style if you assign the same image lists to multiple list view controls; otherwise, more than one control might try to destroy the same image list.

+$#A Items and Subitems

Each item in a list view control consists of an icon, a label, a current state, and an application-defined value. One or more subitems can also be associated with each item. A subitem is a string that, in report view, can be displayed in a column to the right of an item's icon and label. All items in a list view control have the same number of subitems. By using list view messages, you can add, modify, retrieve information about, and delete items. You can also find items with specific attributes.

The LV_ITEM structure defines a list view item or subitem. The iItem member is the zero-based index of the item. The iSubItem member is the one-based index of a subitem or zero if the structure contains information about an item. Additional members specify the item's text, icon, state, and item data. Item data is an application-defined value associated with a list view item.

To add an item to a list view control, use the LVM_INSERTITEM message, specifying the address of an LV_ITEM structure. Before adding multiple items, you can send the control an LVM_SETITEMCOUNT message, specifying the number of items the control will ultimately contain. This message enables the list view control to reallocate its internal data structures only once rather than every time you add an item. You can determine the number of items in a list view control by using the LVM_GETITEMCOUNT message.

To change the attributes of a list view item, use the LVM_SETITEM message, specifying the address of an LV_ITEM structure. The mask member of this structure specifies the item attributes you want to change. To change only the text of an item or subitem, use the LVM_SETITEMTEXT message.

To retrieve information about a list view item, use the LVM_GETITEM message, specifying the address of the LV_ITEM structure to fill in. The mask member of this structure specifies the item attributes to be retrieved. To retrieve only an item or subitem's text, use the LVM_GETITEMTEXT message.

To delete a list view item, use the LVM_DELETEITEM message. You can delete all items in a list view control by using the LVM_DELETEALLITEMS message.

+$#A Item States

An item's state is a value that specifies the item's availablility, indicates user actions, or otherwise reflects the item's status. A list view control changes some state bits, such as when the user selects an item. An application might change other state bits to disable or hide the item or to specify an overlay image or state image. For more information about overlay images and state images, see List View Image Lists.

An item's state is specified by the state member of the LV_ITEM structure. When you specify or change an item's state, the stateMask member specifies which state bits you want to change. You can change an item's state by using the LVM_SETITEMSTATE message. You can specify an item's state when you create it or when you change its attributes by using the LMV_SETITEM message. To determine an item's current state, use the LVM_GETITEMSTATE or LVM_GETITEM message.

To set an item's overlay image, the stateMask member of the LV_ITEM structure must include the LVIS_OVERLAYMASK value, and the state member must include the one-based index of the overlay image shifted left 8 bits by using the INDEXTOOVERLAYMASK macro. The index can be zero to specify no overlay image.

To set an item's state image, the stateMask member of the LV_ITEM structure must include the LVIS_STATEIMAGEMASK value, and the state member must include the one-based index of the state image shifted left 12 bits by using the INDEXTOSTATEIMAGEMASK macro. The index can be zero to specify no state image.

+$#A Callback Items and the Callback Mask

A callback item is a list view item for which the application ⎯ rather than the control ⎯ stores the text, icon, or both. Although a list view control can store these attributes for you, you may want to use callback items if your application already maintains some of this information. The callback mask specifies which item state bits are maintained by the application, and it applies to the whole control rather than to a specific item. The callback mask is zero by default, meaning that the list view control tracks all item states. If an application uses callback items or specifies a nonzero callback mask, it must be able to supply list view item attributes on demand.

A list view control requests any information that it needs to display an item by sending its owner window an LVN_GETDISPINFO notification message. The specified LV_DISPINFO structure identifies the item or subitem and indicates which attributes the application must supply. If item attributes or state bits that are maintained by the application change, the list view control sends its owner window an LVN_SETDISPINFO notification message. This notification enables the application to update its information.

If you change a callback item's attributes, you can use the LVM_UPDATE message to force the control to repaint the item. This message also arranges the list view control if it has the LVS_AUTOARRANGE style. You can use the LVM_REDRAWITEMS message to redraw a range of items by invaliding the corresponding portions of the list view control's client area.

You can define a callback item by specifying appropriate values for the pszText and iImage members of the LV_ITEM structure. If the application maintains the item's or subitem's text, you should specify the LPSTR_TEXTCALLBACK value for the pszText member. If the application keeps track of the icon for the item, you should specify the I_IMAGECALLBACK value for the iImage member.

To set the callback mask for a list view control, use the LVM_SETCALLBACKMASK message. A one bit in the callback mask means the application maintains the corresponding state bit for each list view item. To get the current callback mask, use the LVM_GETCALLBACKMASK message.

By effectively using callback items and the callback mask, you can ensure that each item attribute is maintained in only one place. Doing this can simplify your application, but the only space saved is the memory that would otherwise be required to store item labels and subitem text.

+$#A Columns

Columns control the way items and their subitems are displayed in report view. Each column has a title and width and is associated with a specific subitem (subitem zero is the item's icon and label). The attributes of a column are defined by an LV_COLUMN structure.

To add a column to a list view control, use the LVM_INSERTCOLUMN message. To delete a column, use the LVM_DELETECOLUMN message. You can retrieve and change the properties of an existing column by using the LVM_GETCOLUMN and LVM_SETCOLUMN messages.

To retrieve or change a column's width, use the LVM_GETCOLUMNWIDTH and LVM_SETCOLUMNWIDTH messages. You can determine the minimum column width necessary to display all of a string by using the LVM_GETSTRINGWIDTH message.

Unless the LVS_NOCOLUMNHEADER window style is specified, column headers appear in report view. The user can click a column header, causing an LVN_COLUMNCLICK notification message to be sent to the parent window. Typically, the parent window sorts the list view control by the specified column when this clicking occurs. The user can also drag the column guides between the headers to size the columns.

+$#A Arranging, Sorting, and Finding

You can use list view messages to arrange and sort items and to find items based on their attributes or positions. Arranging repositions items to align on a grid, but the indexes of the items do not change. Sorting changes the sequence of items (and their corresponding indexes) and then repositions them accordingly. You can arrange items only in icon and small icon views, but you can sort items in any view.

To arrange items, use the LVM_ARRANGE message. You can ensure that items are arranged at all times by specifying the LVS_AUTOARRANGE window style.

To sort items, use the LVM_SORTITEMS message. When you sort using this message, you specify an application-defined callback function that is called to compare the relative order of any two items. By supplying an appropriate comparison function, you can sort items by their label, by any subitem, or by any other property. Note that sorting items does not reorder the corresponding subitems. Thus, if any subitems are not callback items, you must regenerate the subitems after sorting.

You can ensure that a list view control is always sorted by specifying the LVS_SORTASCENDING or LVS_SORTDESCENDING window style. You cannot supply a comparison function when using these window styles. The list view sorts the items in ascending or descending order by label.

You can find a list view item with specific properties by using the LVM_FINDITEM message. You can find a list view item that is in a specified state and bears a specified geometrical relationship to a given item by using the LVM_GETNEXTITEM message. For example, you can retrieve the next selected item to the right of a specified item.

+$#A List View Item Position

Every list view item has a position and size, which you can retrieve and set using messages. You can also determine which item, if any, is at a specified position. The position of list view items is specified in view coordinates, which are client coordinates offset by the scroll position.

To retrieve and set an item's position, use the LVM_GETITEMPOSITION and LVM_SETITEMPOSITION messages. LVM_GETITEMPOSITION works for all views, but LVM_SETITEMPOSITION works only for icon and small icon views.

You can determine which item, if any, is at a particular location by using the LVM_HITTEST message.

To get the bounding rectangle for a list item or for only its icon or label, use the LVM_GETITEMRECT message.

+$#A Scroll Position

Unless the LVS_NOSCROLL window style is specified, a list view control can be scrolled to show more items than can fit in the client area of the control. You can get a list view control's scroll position and related information, scroll a list view control by a specified amount, or scroll a list view control such that a specified list item is visible.

In icon view or small icon view, the current scroll position is defined by the view origin. The view origin is the set of coordinates, relative to the visible area of the list view control, that correspond to the view coordinates (0, 0). To get the current view origin, use the LVM_GETORIGIN message. This message should be used only in icon or small icon view; it returns an error in list or report view.

In list or report view, the current scroll position is defined by the top index. The top index is the index of the first visible item in the list view control. To get the current top index, use the LVM_GETTOPINDEX message. This message returns a valid result only in list or report view; it returns zero in icon or small icon view.

You can use the LVM_GETVIEWRECT message to get the bounding rectangle of all items in a list view control, relative to the visible area of the control.

The LVM_GETCOUNTPERPAGE message returns the number of items that fit in one page of the list view control. This message returns a valid result only in list and report views; in icon and small icon views, it returns the total number of items.

To scroll a list view control by a specific amount, use the LVM_SCROLL message. Using the LVM_ENSUREVISIBLE message, you can scroll the list view control, if necessary, to ensure that a specified item is visible.

+$#A Label Editing

A list view control that has the LVS_EDITLABELS window style enables a user to edit item labels in place. The user begins editing by clicking the label of an item that has the focus. An application can begin editing automatically by using the LVM_EDITLABEL message. The list view control notifies the parent window when editing begins and when it is canceled or completed. When editing is completed, the parent window is responsible for updating the item's label, if appropriate.

When label editing begins, a list view control sends its parent window an LVN_BEGINLABELEDIT notification message. You can process this message to allow selective editing of specific labels; returning a nonzero value prevents label editing.

When label editing is canceled or completed, a list view control sends its parent window an LVN_ENDLABELEDIT notification message. The lParam parameter is the address of an LV_DISPINFO structure identifying the item and specifying the edited text. The parent window is responsible for updating the item's label, if appropriate, perhaps after validating the edited string. If editing is canceled, the iItem member is − 1.

During label editing, you can get the handle of the edit control used for label editing by using the LVM_GETEDITCONTROL message. To limit the amount of text a user can enter, you can send the edit control an EM_LIMITTEXT message. You can even subclass the edit control to intercept and discard invalid characters. The edit control is created after the LVN_BEGINLABELEDIT notification message is sent.

+$#A List View Colors

An application can retrieve and set three colors for a list view control. To retrieve and set the text color, use the LVM_GETTEXTCOLOR and LVM_SETTEXTCOLOR messages. To retrieve and set the text background color, use the LVM_GETTEXTBKCOLOR and LVM_SETTEXTBKCOLOR messages. To retrieve and set the window background color, use the LVM_GETBKCOLOR and LVM_SETBKCOLOR messages.

+$#A List View Notification Messages

A list view control sends notification messages to its parent window in the form of WM_NOTIFY messages. The following notification messages are sent by a list view control.

Notification message | Description
LVN_BEGINDRAG | Signals the start of a drag and drop operation.
LVN_BEGINLABELEDIT | Signals the start of in-place label editing.
LVN_BEGINRDRAG | Signals the start of a drag and drop operation, using the right mouse button.
LVN_COLUMNCLICK | Indicates that the user clicked a column header in report view.
LVN_DELETEALLITEMS | Signals the deletion of all list view items.
LVN_DELETEITEM | Signals the deletion of a specific item.
LVN_ENDLABELEDIT | Signals the end of label editing.
LVN_GETDISPINFO | Requests information that the list view control requires to display an item.
LVN_INSERTITEM | Signals the insertion of a new list view item.
LVN_ITEMCHANGED | Indicates that an item has changed.
LVN_ITEMCHANGING | Indicates that an item is in the process of changing and enables the parent window to accept or deny the change.
LVN_KEYDOWN | Signals a keyboard event.
LVN_SETDISPINFO | Notifies a parent window that it must update the information it maintains for an item.

+$#A Default List View Message Processing

This section describes the window message processing performed by a list view control. Messages specific to list view controls are discussed elsewhere and are, therefore, not included here.

Message

Processing performed

WM_CHAR

Searches for a list view item that begins with the specified character and, if the item is found, selects and sets the focus to the item.

Multiple characters received within a time-out interval are concatenated, and the list view control searches for an item that begins with the resulting string.

WM_COMMAND

Processes the EN_UPDATE and EN_KILLFOCUS notification messages and forwards all other edit control notifications to the parent window.

WM_CREATE

Performs additional initialization. If the LVS_SHAREIMAGELISTS window style is not specified, the list view control creates the icon and small icon image lists at this point.

WM_DESTROY

Frees resources.

WM_ERASEBKGND

Erases the window background using the current background color for the list view control. If the background color is the CLR_NONE value, the list view control forwards the message to the parent window.

WM_GETDLGCODE

Returns a combination of the DLGC_WANTTAB and DLGC_WANTARROWS values.

WM_GETFONT

Returns the handle of the current label font.

WM_HSCROLL

Scrolls the list view control horizontally.

WM_KEYDOWN

Processes the SPACEBAR, ENTER, and arrow keys and sends a LVN_KEYDOWN notification message to the parent window.

WM_KILLFOCUS

Repaints the focused list item, if any, and sends a NM_KILLFOCUS notification message to the parent window.

WM_LBUTTONDBLCLK

Sends the parent window an NM_DBLCLK notification message.

WM_LBUTTONDOWN

Processed in different ways depending on whether a click or drag operation is being initiated. To determine which operation is involved, the list view control enters a modal message loop until either the button is released or the mouse is moved.

In the case of a click, the list view control may change which item has the focus and which items are selected, taking into account the cursor position, the state of the SHIFT and CTRL keys, and so on. Then the list view control sends its parent window an NM_CLICK notification message.

If dragging begins over an item, the list view control selects and sets the focus to the item. Then it sends an LVN_BEGINDRAG notification message to the parent window. The parent window is responsible for actually carrying out the drag operation.

If dragging begins over the window background, the list view control enters another modal message loop, enabling the user to form a rectangle by dragging the mouse. Items within the rectangle are selected.

WM_NCCREATE

Allocates and initializes an internal data structure and then calls the DefWindowProc function.

WM_NCDESTROY

Frees resources allocated by the list view control. Unless the LVS_SHAREIMAGELISTS style is used, this includes deleting the full-sized and small image lists.

WM_NOTIFY

Processes header control notification messages.

WM_PAINT

Displays any items in the update region. For callback items, the control first sends an LVN_GETDISPINFO notification message to the owner window to request display information.

WM_RBUTTONDOWN

Processed the same way as the WM_LBUTTONDOWN message, except that the control sends an NM_RCLICK notification message (instead of NM_CLICK) and an LVN_BEGINRDRAG notification message (instead of LVN_BEGINDRAG).

WM_SETFOCUS

Repaints the focused list item, if any, and sends an NM_SETFOCUS notification message to the parent window.

WM_SETFONT

Saves the specified font handle, forwards the message to the header window, if any, and repaints using the new font.

WM_SETREDRAW

Turns redrawing on or off.

WM_TIMER

Begins editing of an item label. If the user clicks the label of the focused item, the list view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the list view control to not enter edit mode if the user double-clicks the label.

WM_VSCROLL

Scrolls the list view control vertically.

WM_WINDOWPOSCHANGED

Updates the window scroll bars. If the current view is icon or small icon view and the LVS_AUTOARRANGE style is specified, the list view control also arranges the list items.

WM_WININICHANGE

Processes changes to system metrics.

+$#A Using List View Controls

• Creating a list view control

• Initializing the image lists for a list view control

• Adding columns to a list view control

• Adding items to a list view control

• Processing the WM_NOTIFY message

• Changing list view styles

+$#A Creating a List View Control

To create a list view control, use the CreateWindowEx function, specifying the WC_LISTVIEW window class. The list view window class is registered in the application's address space when the common controls dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls function.

The following example creates a list view control and then calls application-defined functions that add image lists, columns, and list view items. The window style specifies the list view control's initial view and other options. This example specifies report view, which enables the user to edit the labels of list view items.

// CreateListView - creates a list view control.

// Returns the handle of the new control if successful or NULL

// otherwise.

// hwndParent - handle of the control's parent window

// pfData - file containing list view items

HWND WINAPI CreateListView(HWND hwndParent, FILE *pfData)

{

HWND hwndLV;

// Force the common controls DLL to be loaded.

InitCommonControls();

// Create the list view window.

hwndLV = CreateWindow(WC_LISTVIEW, "",

WS_CHILD | LVS_REPORT | LVS_EDITLABELS,

0, 0, CW_USEDEFAULT, CW_USEDEFAULT,

hwndParent, NULL, g_hinst, NULL);

if (hwndLV == NULL)

return NULL;

// Call application-defined functions to initialize the

// image lists, add columns, and add some items.

if (!InitListViewImageLists(hwndLV) ||

!InitListViewColumns(hwndLV) ||

!InitListViewItems(hwndLV, pfData)) {

DestroyWindow(hwndLV);

return FALSE;

}

return hwndLV; // return the control's handle

}

+$#A Initializing the Image Lists for a List View Control

A list view control can have up to three image lists associated with it: one for item icons in icon view, one for item icons in other views, and one for application-defined item states. The following example creates two image lists, adds an icon to each, and assigns them to a list view control by using the LVM_SETIMAGELIST message.

// InitListViewImageList - creates image lists for a list view.

// Returns TRUE if successful or FALSE otherwise.

// hwndLV - handle of the list view control

BOOL WINAPI InitListViewImageLists(HWND hwndLV)

{

HICON hiconItem; // icon for list view items

HIMAGELIST himlLarge; // image list for icon view

HIMAGELIST himlSmall; // image list for other views

// Create the full-sized and small icon image lists.

himlLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),

GetSystemMetrics(SM_CYICON), TRUE, 1, 1);

himlSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),

GetSystemMetrics(SM_CYSMICON), TRUE, 1, 1);

// Add an icon to each image list.

hiconItem = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_ITEM));

ImageList_AddIcon(himlLarge, hiconItem);

ImageList_AddIcon(himlSmall, hiconItem);

DeleteObject(hiconItem);

// Assign the image lists to the list view control.

ListView_SetImageList(hwndLV, himlLarge, LVSIL_NORMAL);

ListView_SetImageList(hwndLV, himlSmall, LVSIL_SMALL);

return TRUE;

}

+$#A Adding Columns to a List View Control

Columns appear only in report view, and they enable multiple pieces of information to be displayed for each list item. You can add columns to a list view control by using the LVM_INSERTCOLUMN message. Each item in a list view control can have, in addition to its item text, any number of strings called subitems. When you add a column to a list view control, you specify which subitem to display in the column.

Unless a list view control has the LVS_NOCOLUMNHEADER window style, each column has a header showing the column name. The user can click the header and can size the columns using the header.

The following example adds several columns to a list view control. The column headings are defined as string resources, which are consecutively numbered starting with IDS_FIRSTCOLUMN (defined in the application's header file). The number of columns is defined in the application's header file as C_COLUMNS.

// InitListViewColumns - adds columns to a list view control.

// Returns TRUE if successful or FALSE otherwise.

// hwndLV - handle of the list view control

BOOL WINAPI InitListViewColumns(HWND hwndLV)

{

extern char g_achTemp[256]; // temporary buffer

LV_COLUMN lvc;

int iCol;

// Initialize the LV_COLUMN structure.

lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

lvc.fmt = LVCFMT_LEFT;

lvc.cx = 100;

lvc.pszText = g_achTemp;

// Add the columns.

for (iCol = 0; iCol < C_COLUMNS; iCol++) {

lvc.iSubItem = iCol;

LoadString(g_hinst, IDS_FIRSTCOLUMN + iCol,

g_achTemp, sizeof(g_achTemp));

if (ListView_InsertColumn(hwndLV, iCol, &lvc) == -1)

return FALSE;

}

return TRUE;

}

+$#A Adding Items to a List View Control

An application can add items to a list view control by using the LVM_INSERTITEM message. The attributes of a list view item that are specified by an LV_ITEM structure include a state, a label, an icon, and item data. Associated with each item may be one or more subitems, which are strings that appear to the right of an item in report view.

The example in this section adds a list view item for each line in a text file. Semicolons are assumed to separate the item label and the subitem strings that follow it. The example saves each item's label and subitem strings in a structure, which is defined in the application's header file, as follows.

#define C_COLUMNS 6

typedef struct myitem_tag {

LPSTR aCols[C_COLUMNS];

} MYITEM;

The application fills in an LV_ITEM structure and adds a list view item by using the LVM_INSERTITEM message. Because the application saves the item label in its own application-defined MYITEM structure, it specifies the LPSTR_TEXTCALLBACK value for the pszText member of the LV_ITEM structure. Specifying this value causes the control to send an LVN_GETDISPINFO notification message to its owner window whenever it needs to display the item. The address of the application-defined structure is saved as item data.

// InitListViewItems - adds items and subitems to a list view.

// Returns TRUE if successful or FALSE otherwise.

// hwndLV - handle of the list view control

// pfData - text file containing list view items with columns

// separated by semicolons

BOOL WINAPI InitListViewItems(HWND hwndLV, FILE *pfData)

{

extern char g_achTemp[256]; // temporary buffer

PSTR pszStart;

PSTR pszEnd;

int iItem;

int iSubItem;

LV_ITEM lvi;

// Initialize LV_ITEM members that are common to all items.

lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;

lvi.state = 0;

lvi.stateMask = 0;

lvi.pszText = LPSTR_TEXTCALLBACK; // app. maintains text

lvi.iImage = 0; // image list index

// Read each line in the specified file.

for (iItem = 0;

fgets(g_achTemp, sizeof(g_achTemp), pfData);

iItem++) {

// Allocate an application-defined structure to store the

// item label and the text of each subitem.

MYITEM *pItem = LocalAlloc(LPTR, sizeof(MYITEM));

// Copy the first string (the label).

pszEnd = strchr(g_achTemp, ';');

*pszEnd = '\0';

pItem->aCols[0] = DupString(g_achTemp);

// Copy subsequent strings (subitems).

for (iSubItem = 1;

iSubItem < C_COLUMNS && pszEnd != NULL;

iSubItem++) {

pszStart = pszEnd + 1;

if ((pszEnd = strchr(pszStart, ';')) != NULL)

*pszEnd = '\0';

pItem->aCols[iSubItem] = DupString(pszStart);

}

// Initialize item-specific LV_ITEM members.

lvi.iItem = iItem;

lvi.iSubItem = 0;

lvi.lParam = (LPARAM) pItem; // item data

// Add the item.

ListView_InsertItem(hwndLV, &lvi);

// There is no need to set the text of the subitems. They

// default to LPSTR_TEXTCALLBACK.

}

return TRUE;

}

// DupString - allocates a copy of a string.

// lpsz - address of the null-terminated string to copy

LPSTR DupString(LPSTR lpsz)

{

int cb = lstrlen(lpsz) + 1;

LPSTR lpszNew = LocalAlloc(LMEM_FIXED, cb);

if (lpszNew != NULL)

CopyMemory(lpszNew, lpsz, cb);

return lpszNew;

}

+$#A Processing the WM_NOTIFY Message

A list view control notifies its parent window of events by sending a WM_NOTIFY message. The wParam parameter is the identifier of the list view control, and the lParam parameter is the address of an NMHDR structure (or to a larger structure that has an NMHDR structure as its first member). The example in this section processes the LVN_GETDISPINFO, LVN_ENDLABELEDIT, and LVN_COLUMNCLICK notification messages.

A list view control sends the LVN_GETDISPINFO notification message to retrieve information about an item or subitem from the parent window. This notification is sent, for example, when an item with the LPSTR_TEXTCALLBACK value needs to be displayed.

A list view control sends the LVN_ENDLABELEDIT notification message when the user completes or cancels editing of an item's label. This notification is only sent if the list view control has the LVS_EDITLABELS window style. If editing is being canceled, the parent window typically does nothing. If editing is being completed, the parent window should set the item label to the new text, unless the item label is LPSTR_TEXTCALLBACK. In that case, the parent window should simply update the application-defined data it maintains for the list item.

If the user clicks a column header in report view, a list view control sends the LVN_COLUMNCLICK notification message. Typically, an application sorts a list view by the specified column when this clicking occurs. To sort, use the LVM_SORTITEMS message, specifying an application-defined comparison function.

The following example shows the portion of the application's window procedure that processes the WM_NOTIFY message.

case WM_NOTIFY:

// Branch depending on the specific notification message.

switch (((LPNMHDR) lParam)->code) {

// Process LVN_GETDISPINFO to supply information about

// callback items.

case LVN_GETDISPINFO:

Main_OnGetDispInfo((LV_DISPINFO *) lParam);

break;

// Process LVN_ENDLABELEDIT to change item labels after

// in-place editing.

case LVN_ENDLABELEDIT:

return Main_OnEndLabelEdit(

(LV_DISPINFO *) lParam

);

// Process LVN_COLUMNCLICK to sort items by column.

case LVN_COLUMNCLICK:

#define pnm ((NM_LISTVIEW *) lParam)

ListView_SortItems(

pnm->hdr.hwndFrom,

ListViewCompareFunc,

(LPARAM) (pnm->iSubItem)

);

#undef pnm

break;

}

break;

The following example shows the application-defined functions that the window procedure uses to process list view notification messages.

// Main_OnGetDispInfo - processes the LVN_GETDISPINFO

// notification message.

// pnmv - value of lParam (points to an LV_DISPINFO structure)

VOID WINAPI Main_OnGetDispInfo(LV_DISPINFO *pnmv)

{

// Provide the item or subitem's text, if requested.

if (pnmv->item.mask & LVIF_TEXT) {

MYITEM *pItem = (MYITEM *) (pnmv->item.lParam);

lstrcpy(pnmv->item.pszText,

pItem->aCols[pnmv->item.iSubItem]);

}

}

// Main_OnEndLabelEdit - processes the LVN_ENDLABELEDIT

// notification message.

// Returns TRUE if the label is changed or FALSE otherwise.

// pnmv - value of lParam (points to an LV_DISPINFO structure)

BOOL Main_OnEndLabelEdit(LV_DISPINFO *pnmv)

{

MYITEM *pItem;

// The item is -1 if editing is being canceled.

if (pnmv->item.iItem == -1)

return FALSE;

// Copy the new text to the application-defined structure,

// a pointer to which is saved as item data.

pItem = (MYITEM *) (pnmv->item.lParam);

pItem->aCols[0] = (PSTR) LocalReAlloc(

(HLOCAL) (pItem->aCols[0]),

lstrlen(pnmv->item.pszText) + 1,

LMEM_MOVEABLE

);

lstrcpy(pItem->aCols[0], pnmv->item.pszText);

// No need to set the item text, because it is a callback item.

return TRUE;

}

// ListViewCompareFunc - sorts the list view control. It is a

// comparison function.

// Returns a negative value if the first item should precede the

// second item, a positive value if the first item should

// follow the second item, and zero if the items are equivalent.

// lParam1 and lParam2 - item data for the two items (in this

// case, pointers to application-defined MYITEM structures)

// lParamSort - value specified by the LVM_SORTITEMS message

// (in this case, the index of the column to sort)

int CALLBACK ListViewCompareFunc(

LPARAM lParam1,

LPARAM lParam2,

LPARAM lParamSort)

{

MYITEM *pItem1 = (MYITEM *) lParam1;

MYITEM *pItem2 = (MYITEM *) lParam2;

// Compare the specified column.

int iCmp = lstrcmpi(pItem1->aCols[lParamSort],

pItem2->aCols[lParamSort]);

// Return the result if nonzero, or compare the

// first column otherwise.

return (iCmp != 0) ? iCmp :

lstrcmpi(pItem1->aCols[0], pItem2->aCols[0]);

}

+$#A Changing List View Styles

An application can change the window style of a list view control after it is created by using the GetWindowLong and SetWindowLong functions. The following example changes the style bits that govern the view mode.

// SetView - sets a list view's window style to change the view.

// hwndLV - handle of the list view control

// dwView - value specifying a view style

VOID WINAPI SetView(HWND hwndLV, DWORD dwView)

{

// Get the current window style.

DWORD dwStyle = GetWindowLong(hwndLV, GWL_STYLE);

// Only set the window style if the view bits have changed.

if ((dwStyle & LVS_TYPEMASK) != dwView)

SetWindowLong(hwndLV, GWL_STYLE,

(dwStyle & ~LVS_TYPEMASK) | dwView);

}

+$#A List View Control Reference

The following messages, notification messages, and structures are associated with list view controls. These elements can be grouped, as follows.

Items and Subitems

LV_ITEM
LVM_DELETEALLITEMS
LVM_DELETEITEM
LVM_GETITEM
LVM_GETITEMCOUNT
LVM_GETITEMSPACING
LVM_GETITEMSTATE
LVM_GETITEMTEXT
LVM_GETSELECTEDCOUNT
LVM_INSERTITEM
LVM_SETITEM
LVM_SETITEMCOUNT
LVM_SETITEMSTATE
LVM_SETITEMTEXT

Callback Items

LV_DISPINFO
LVM_GETCALLBACKMASK
LVM_REDRAWITEMS
LVM_SETCALLBACKMASK
LVM_UPDATE
LVN_GETDISPINFO
LVN_SETDISPINFO

Columns

LV_COLUMN
LVM_DELETECOLUMN
LVM_GETCOLUMN
LVM_GETCOLUMNWIDTH
LVM_GETSTRINGWIDTH
LVM_INSERTCOLUMN
LVM_SETCOLUMN
LVM_SETCOLUMNWIDTH
LVN_COLUMNCLICK

Arranging, Sorting, and Finding

LV_FINDINFO
LVM_ARRANGE
LVM_FINDITEM
LVM_GETNEXTITEM
LVM_SORTITEMS

Item Positions and Scrolling

LV_HITTESTINFO
LVM_ENSUREVISIBLE
LVM_GETCOUNTPERPAGE
LVM_GETITEMPOSITION
LVM_GETITEMRECT
LVM_GETORIGIN
LVM_GETTOPINDEX
LVM_GETVIEWRECT
LVM_HITTEST
LVM_SCROLL
LVM_SETITEMPOSITION
LVM_SETITEMPOSITION32

Colors

LVM_GETBKCOLOR
LVM_GETTEXTBKCOLOR
LVM_GETTEXTCOLOR
LVM_SETBKCOLOR
LVM_SETTEXTBKCOLOR
LVM_SETTEXTCOLOR

Miscellaneous

LV_KEYDOWN
LVM_CREATEDRAGIMAGE
LVM_EDITLABEL
LVM_GETEDITCONTROL
LVM_GETIMAGELIST
LVM_SETIMAGELIST
LVN_BEGINDRAG
LVN_BEGINLABELEDIT
LVN_BEGINRDRAG
LVN_DELETEALLITEMS
LVN_DELETEITEM
LVN_ENDLABELEDIT
LVN_INSERTITEM
LVN_ITEMCHANGED
LVN_ITEMCHANGING
LVN_KEYDOWN
LVN_PEN
NM_LISTVIEW

+$#A List View Control Messages

An application sends messages to a list view control to alter its appearance, add or change items and columns, and so on. Each message has a corresponding macro, which you can use instead of sending the message explicitly.

LVM_ARRANGE
LVM_CREATEDRAGIMAGE
LVM_DELETEALLITEMS
LVM_DELETECOLUMN
LVM_DELETEITEM
LVM_EDITLABEL
LVM_ENSUREVISIBLE
LVM_FINDITEM
LVM_GETBKCOLOR
LVM_GETCALLBACKMASK
LVM_GETCOLUMN
LVM_GETCOLUMNWIDTH
LVM_GETCOUNTPERPAGE
LVM_GETEDITCONTROL
LVM_GETIMAGELIST
LVM_GETISEARCHSTRING
LVM_GETITEM
LVM_GETITEMCOUNT
LVM_GETITEMPOSITION
LVM_GETITEMRECT
LVM_GETITEMSPACING
LVM_GETITEMSTATE
LVM_GETITEMTEXT
LVM_GETNEXTITEM
LVM_GETORIGIN
LVM_GETSELECTEDCOUNT
LVM_GETSTRINGWIDTH
LVM_GETTEXTBKCOLOR
LVM_GETTEXTCOLOR
LVM_GETTOPINDEX
LVM_GETVIEWRECT
LVM_HITTEST
LVM_INSERTCOLUMN
LVM_INSERTITEM
LVM_REDRAWITEMS
LVM_SCROLL
LVM_SETBKCOLOR
LVM_SETCALLBACKMASK
LVM_SETCOLUMN
LVM_SETCOLUMNWIDTH
LVM_SETIMAGELIST
LVM_SETITEM
LVM_SETITEMCOUNT
LVM_SETITEMPOSITION
LVM_SETITEMPOSITION32
LVM_SETITEMSTATE
LVM_SETITEMTEXT
LVM_SETTEXTBKCOLOR
LVM_SETTEXTCOLOR
LVM_SORTITEMS
LVM_UPDATE

+$#A List View Control Notification Messages

A list view control sends notification messages to its owner window when events occur in the control.

LVN_BEGINDRAG
LVN_BEGINLABELEDIT
LVN_BEGINRDRAG
LVN_COLUMNCLICK
LVN_DELETEALLITEMS
LVN_DELETEITEM
LVN_ENDLABELEDIT
LVN_GETDISPINFO
LVN_INSERTITEM
LVN_ITEMCHANGED
LVN_ITEMCHANGING
LVN_KEYDOWN
LVN_PEN
LVN_SETDISPINFO

+$#A List View Control Structures

The following structures are used with list view controls.

LV_COLUMN
LV_DISPINFO
LV_FINDINFO
LV_HITTESTINFO
LV_ITEM
LV_KEYDOWN
NM_LISTVIEW

+$#A List View Control Constants

The following sections list window style flags and item state flags are used with list view controls.

+$#A List View Window Styles

The following window styles are specific to list view controls.

LVS_ALIGNLEFT | Specifies that items are left-aligned in icon and small icon view.
LVS_ALIGNTOP | Specifies that items are aligned with the top of the list view control in icon and small icon view.
LVS_AUTOARRANGE | Specifies that icons are automatically kept arranged in icon and small icon view.
LVS_BUTTON | Specifies that item icons look like buttons in icon view.
LVS_EDITLABELS | Allows item text to be edited in place. The parent window must process the LVN_ENDLABELEDIT notification message.
LVS_ICON | Specifies icon view.
LVS_LIST | Specifies list view.
LVS_NOCOLUMNHEADER | Specifies that a column header is not displayed in report view. By default, columns have headers in report view.
LVS_NOLABELWRAP | Displays item text on a single line in icon view. By default, item text may wrap in icon view.
LVS_NOSCROLL | Disables scrolling. All items must be within the client area.
LVS_NOSORTHEADER | Specifies that column headers do not work like buttons. This style is useful if clicking a column header in report view does not carry out an action, such as sorting.
LVS_OWNERDRAWFIXED | Enables the owner window to paint items in report view. The list view control sends a WM_DRAWITEM message to paint each item; it does not send separate messages for each subitem. The itemData member of the DRAWITEMSTRUCT structure contains the item data for the specified list view item.
LVS_REPORT | Specifies report view. When using the LVS_REPORT style with a List View control, the first column is always left-aligned. You can not use LVCFMT_RIGHT to change this alignment.
LVS_SHAREIMAGELISTS | Specifies that the control does not take ownership of the image lists assigned to it; that is, it does not destroy the image lists when it is destroyed. This style enables the same image lists to be used with multiple list view controls.
LVS_SHOWSELALWAYS | Always show the selection,if any, even if the control does not have the focus.
LVS_SINGLESEL | Allows only one item at a time to be selected. By default, multiple items may be selected.
LVS_SMALLICON | Specifies small icon view.
LVS_SORTASCENDING | Sorts items based on item text in ascending order.
LVS_SORTDESCENDING | Sorts items based on item text in descending order.

You can use the LVS_TYPESTYLEMASK value to isolate the window styles that correspond to the current view: LVS_ICON, LVS_SMALLICON, LVS_LIST, and LVS_REPORT.

You can use the LVS_ALIGNMASK value to isolate the window styles that specify the alignment of items: LVS_ALIGNLEFT and LVS_ALIGNTOP.

+$#A List View Item States

An item's state determines its appearance and functionality. The state can be zero, or one or more of the following values.

LVIS_CUT | The item is marked for a cut and paste operation.
LVIS_DROPHILITED | The item is highlighted as a drag-and-drop target.
LVIS_FOCUSED | The item has the focus, so it is surrounded by a standard focus rectangle. Although more than one item may be selected, only one item can have the focus.
LVIS_SELECTED | The item is selected. The appearance of a selected item depends on whether it has the focus and on the system colors used for selection.

You can use the LVIS_OVERLAYMASK value to isolate the state bits that contain the one-based index of the overlay image. You can use the LVIS_STATEIMAGEMASK value to isolate the state bits that contain the one-based index of the state image.

Créé avec HelpNDoc Personal Edition: Création d'aide CHM, PDF, DOC et HTML d'une même source


Mailslots.rtf

+$#AK Mailslots

A mailslot is a mechanism for one-way interprocess communications (IPC). An application written for Microsoft® Windows® can store messages in a mailslot. The owner of the mailslot can retrieve messages that are stored there. These messages are typically sent over a network to either a specified computer or to all computers in a specified domain. A domain is a group of workstations and servers that share a group name.

+$#A About Mailslots

A mailslot is a pseudofile; it resides in memory, and standard Windows file functions write to it. Unlike disk files, however, mailslots are temporary. When every handle of a mailslot is closed, the mailslot and all the data it contains are deleted. The data in a mailslot message can be in any form.

A mailslot server is a process that creates and owns a mailslot. When the server creates a mailslot, it receives a mailslot handle. This handle must be used when a process reads messages from the mailslot. Only the process that creates a mailslot (or has obtained the handle by some other mechanism, such as inheritance) can read from the mailslot. A mailslot exists until all server handles to it have been closed or all server processes have exited. All mailslots are local to the process that creates them; a process cannot create a remote mailslot.

A mailslot client is a process that writes a message to a mailslot. Any process that has the name of a mailslot can put a message there. New messages follow preexisting messages in the mailslot.

Mailslots can broadcast messages within a domain. If several processes in a domain each create a mailslot using the same name, every message that is addressed to that mailslot and sent to the domain is received by the participating processes. Because one process can control both a server mailslot handle and the client handle retrieved when the mailslot is opened for a write operation, applications can easily implement a simple message-passing facility within a domain.

Some developers choose to use named pipes instead of mailslots for interprocess communications. Named pipes are a simple way for two processes to exchange messages. Mailslots, on the other hand, are a simple way for a process to broadcast messages to many other processes. For more information about named pipes, see Pipes.

Another issue in the choice between mailslots and named pipes is that mailslots use datagrams, and named pipes do not. A datagram is a small packet of information that the network sends along the wire. Like a radio or television broadcast, a datagram offers no confirmation of receipt; there is no way to guarantee that a datagram has been received. Just as mountains, large buildings, or interfering signals might cause a radio or television signal to get lost, there are things the sender has no knowledge of that can prevent a datagram from reaching a particular destination. Named pipes are more like simple one-to-one telephone calls: you can talk only to one party, but you know that the conversation is occurring. So, if an application requires guaranteed reception, it should use a named pipe; but named pipes cannot broadcast. Mailslots do not guarantee reception, but they can broadcast.

+$#A Mailslot Names

When a process creates a mailslot, the mailslot name must have the following form.

\\.\mailslot\[path]name

A mailslot name requires the following elements: two backslashes to begin the name, a period, a backslash following the period, the word mailslot, and a trailing backslash. Case, however, is not important. A mailslot name can be preceded by a path consisting of the names of one or more pseudodirectories, separated by backslashes. For example, if a user expects messages on the subject of taxes from Bob, Pete, and Sue, the user's mailslot application might allow the user to create individual mailslots for each sender.

\\.\mailslot\taxes\bobs_comments

\\.\mailslot\taxes\petes_comments

\\.\mailslot\taxes\sues_comments

To put a message into a mailslot, a process opens a mailslot whose name is in one of several forms. To write to a mailslot on the local computer, a process can use a mailslot name using the same form required for creating a mailslot. This situation, however, is relatively uncommon. More frequently, a process uses the following form to write to a mailslot on a specific remote computer.

\\computername\mailslot\[path]name

The following form allows a process to put a message into every mailslot in the specified domain that has the given name.

\\domainname\mailslot\[path]name

To put a message into every mailslot with a given name in the system's primary domain, a process uses the following form.

\\*\mailslot\[path]name

+$#A Mailslots and Microsoft LAN Manager 2.x

Windows uses mailslots similar to those developed for Microsoft LAN Manager version 2.0. An application that uses LAN Manager 2.x mailslots can interact easily with mailslots created by Windows. There are significant differences, however, between Windows-based mailslots and LAN Manager mailslots:

• Windows-based mailslots all have the same class ⎯ there are no "first class" and "second class" mailslots.

• All Windows-based mailslot messages have equal priority and are always appended to the mailslot buffer. When a Windows-based mailslot message is written to a mailslot on a computer that requires message priority, the system sets the priority to the lowest value (0).

• Write operations to Windows-based mailslots on local computers return an error value if an error occurs. Write operations to remote Windows-based mailslots do not return an error value when they fail. (Because there is no built-in limit to the size of the mailslot buffer failure is unlikely.)

• Write operations to Windows-based mailslots fail immediately if the message cannot be appended to the mailslot buffer (if the buffer is too small, for example). Immediate failure makes write time-outs irrelevant; contrary to LAN Manager mailslots, there are no write time-outs for Windows-based mailslots.

• A process that creates a Windows-based mailslot can specify that the mailslot handle be inherited by its child processes. (Mailslot handles in LAN Manager cannot be inherited.)

A process writes messages to a Windows-based mailslot by using standard file input and output functions. The following table shows LAN Manager mailslot functions and their corresponding Windows functions.

LAN Manager functions | Windows functions
DosDeleteMailslot | CloseHandle
DosMailslotInfo | GetMailslotInfo
DosMakeMailslot | CreateMailslot
DosReadMailslot | ReadFile
DosWriteMailslot | WriteFile

+$#A Server and Client Functions

Clients and servers when working with mailslots should use only the functions discussed in this section. Other Windows functions should not be used, even if they accept file handles or filenames as arguments.

+$#A Mailslot Server Functions

Mailslot servers have exclusive use of three functions, as shown in the following table.

Function | Description
CreateMailslot | Creates a mailslot and returns a mailslot handle.
GetMailslotInfo | Retrieves the maximum message size, the mailslot size, the size of the next message in the mailslot, the number of messages in the mailslot, and the amount of time a read operation can wait for a message.
SetMailslotInfo | Changes the read time-out for a mailslot.

The following functions are also used by mailslot servers.

Function | Description
DuplicateHandle | Duplicates the mailslot handle.
ReadFile | Retrieves messages from a mailslot.
GetFileTime | Retrieves the date and time a mailslot was created.
SetFileTime | Sets the date and time a mailslot was created.

+$#A Client Functions

A client process uses the following functions when interacting with a mailslot.

Function | Description
CloseHandle | Closes a mailslot handle for a client process.
CreateFile | Creates a mailslot handle for a client process.
DuplicateHandle | Duplicates a mailslot handle.
WriteFile | Writes data to a mailslot.

+$#A Using Mailslots

• Creating a mailslot

• Writing to a mailslot

• Reading from a mailslot

+$#A Creating a Mailslot

Mailslots are supported by three specialized functions: CreateMailslot, GetMailslotInfo, and SetMailslotInfo. These functions are used by the mailslot server.

The following code sample uses the CreateMailslot function to retrieve the handle of a mailslot named sample_mailslot.

BOOL FAR PASCAL Makeslot(HWND hwnd, HDC hdc)

{

LPSTR lpszSlotName = "\\\\.\\mailslot\\sample_mailslot";

/* The mailslot handle "hSlot1" is declared globally. */

hSlot1 = CreateMailslot(lpszSlotName,

0, /* no maximum message size */

MAILSLOT_WAIT_FOREVER, /* no time-out for read operations */

(LPSECURITY_ATTRIBUTES) NULL); /* no security attributes */

if (hSlot1 == INVALID_HANDLE_VALUE) {

ErrorHandler(hwnd, "CreateMailslot"); /* local error handler */

return FALSE;

}

TextOut(hdc, 10, 10, "CreateMailslot successful.", 26);

return TRUE;

}

To create a mailslot that can be inherited by child processes, an application should change the SECURITY_ATTRIBUTES structure passed as the last parameter of CreateMailslot. To do this, the application sets the bInheritHandle member of this structure to TRUE (the default setting is FALSE).

+$#A Writing to a Mailslot

Writing to a mailslot is similar to writing to a standard disk file. The following code uses the CreateFile, WriteFile, and CloseHandle functions to put a short message in a mailslot. The message is broadcast to every computer in the primary domain of the system.

LPSTR lpszMessage = "Message for sample_mailslot in primary domain.";

BOOL fResult;

HANDLE hFile;

DWORD cbWritten;

hFile = CreateFile("\\\\*\\mailslot\\sample_mailslot",

GENERIC_WRITE,

FILE_SHARE_READ, /* required to write to a mailslot */

(LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler(hwnd, "Primary domain"); /* local error handler */

return FALSE;

}

fResult = WriteFile(hFile,

lpszMessage,

(DWORD) lstrlen(lpszMessage) + 1, /* include terminat. null char. */

&cbWritten,

(LPOVERLAPPED) NULL);

if (!fResult) {

ErrorHandler(hwnd, "WriteFile");

return FALSE;

}

TextOut(hdc, 10, 10, "WriteFile successful.", 21);

fResult = CloseHandle(hFile);

if (!fResult) {

ErrorHandler(hwnd, "CloseHandle");

return FALSE;

}

TextOut(hdc, 10, 30, "CloseHandle successful.", 23);

return TRUE;

Messages broadcast to a domain must be no longer than 400 bytes. Messages that are not broadcast should be smaller than 64 kilobytes. There is no limit to the number of messages that can be sent to a mailslot.

+$#A Reading from a Mailslot

The process that creates a mailslot can read messages from it by using the mailslot handle in a call to the ReadFile function. The code in the following example calls the GetMailslotInfo function to determine whether there are messages in the mailslot. If messages are waiting, each is displayed in a message box along with the number of messages remaining to be read.

BOOL FAR PASCAL Readslot(HWND hwnd, HDC hdc)

{

DWORD cbMessage, cMessage, cbRead;

BOOL fResult;

LPSTR lpszBuffer;

CHAR achID[80];

DWORD cAllMessages;

cbMessage = cMessage = cbRead = 0;

/* Mailslot handle "hSlot1" is declared globally. */

fResult = GetMailslotInfo(hSlot1, /* mailslot handle */

(LPDWORD) NULL, /* no maximum message size */

&cbMessage, /* size of next message */

&cMessage, /* number of messages */

(LPDWORD) NULL); /* no read time-out */

if (!fResult) {

ErrorHandler(hwnd, "GetMailslotInfo");

return FALSE;

}

if (cbMessage == MAILSLOT_NO_MESSAGE) {

TextOut(hdc, 10, 10, "No waiting messages.", 20);

return TRUE;

}

cAllMessages = cMessage;

while (cMessage != 0) { /* retrieves each message */

/* Create a message-number string. */

wsprintf((LPSTR) achID,

"\nMessage #%d of %d\n", cAllMessages - cMessage + 1,

cAllMessages);

/* Allocate memory for the message. */

lpszBuffer = (LPSTR) GlobalAlloc(GPTR,

lstrlen((LPSTR) achID) + cbMessage);

lpszBuffer[0] = '\0';

fResult = ReadFile(hSlot1,

lpszBuffer,

cbMessage,

&cbRead,

(LPOVERLAPPED) NULL);

if (!fResult) {

ErrorHandler(hwnd, "ReadFile");

GlobalFree((HGLOBAL) lpszBuffer);

return FALSE;

}

/* Concatenate the message and the message-number string. */

lstrcat(lpszBuffer, (LPSTR) achID);

/* Display the message. */

MessageBox(hwnd,

lpszBuffer,

"Contents of Mailslot",

MB_OK);

GlobalFree((HGLOBAL) lpszBuffer);

fResult = GetMailslotInfo(hSlot1, /* mailslot handle */

(LPDWORD) NULL, /* no maximum message size */

&cbMessage, /* size of next message */

&cMessage, /* number of messages */

(LPDWORD) NULL); /* no read time-out */

if (!fResult) {

ErrorHandler(hwnd, "GetMailslotInfo");

return FALSE;

}

}

return TRUE;

}

A mailslot exists until the CloseHandle function is called for all open server handles or until all server processes that own a mailslot handle exit. In both cases, any unread messages are deleted from the mailslot, all client handles to the mailslot are closed, and the mailslot itself is deleted from memory.

+$#A Mailslot Functions

The following functions can be used with mailslots.

CreateMailslot
GetMailslotInfo
SetMailslotInfo

Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub gratuit


Memory Management.rtf

+$#AK Memory Management

This overview describes Win32 memory management. It also explains how to allocate and use memory.

+$#A About Memory Management

In the Microsoft® Win32® application programming interface (API), each process has its own 32-bit virtual address space that enables addressing up to 4 gigabytes (GB) of memory. The 2 GB in low memory (0x00 to 0x7FFFFFFF) are available to the user, and the 2 GB in high memory (0x80000000 to 0xFFFFFFFF) are reserved for the kernel. The virtual addresses used by a process do not represent the actual physical location of an object in memory. Instead, for each process the kernel maintains a page map, an internal data structure used to translate virtual addresses into corresponding physical addresses.

+$#A Virtual Address Space and Physical Storage

The virtual address space of each process is much larger than the total physical memory, random-access memory (RAM), available to all processes. To increase the size of physical storage, the kernel uses the disk for additional storage. The total amount of storage available to all executing processes is the sum of the physical memory, RAM, and the free space on disk available to the paging file, a disk file used to increase the amount of physical storage. Physical storage and the virtual (logical) address space of each process are organized into pages, units of memory, whose size depends on the host computer. For example, on x86 computers the host page size is 4 kilobytes (K).

To maximize its flexibility in managing memory, the kernel can move pages of physical memory to and from a paging file on disk. When a page is moved in physical memory, the kernel updates the page maps of the affected processes. When the kernel needs space in physical memory, it moves the least recently used pages of physical memory to the paging file. Manipulation of physical memory by the kernel is completely transparent to applications, which operate only in their virtual address spaces.

The pages of a process's virtual address space can be in one of the following states.

State | Description
Free | A free page is not currently accessible, but it is available to be committed or reserved.
Reserved | A reserved page is a block of the process's virtual address space that has been set aside for future use. The process cannot access a reserved page, and there is no physical storage associated with it. A reserved page reserves a range of virtual addresses that cannot be used subsequently by other allocation operations (that is, by functions such as malloc, LocalAlloc, and so on). A process can use the VirtualAlloc function to reserve pages of its address space and later to commit the reserved pages. It can use the VirtualFree function to release them.
Committed | A committed page is one for which physical storage (in memory or on disk) has been allocated. It can be protected to allow either no access or read-only access, or it can have read and write access. A process can use the VirtualAlloc function to allocate committed pages. The GlobalAlloc and LocalAlloc functions allocate committed pages with read-write access. A committed page allocated by VirtualAlloc can be decommitted by the VirtualFree function, which releases the page's storage and changes the state of the page to reserved.

+$#A Global and Local Functions

A process can use the GlobalAlloc and LocalAlloc functions to allocate memory. In the linear 32-bit environment of the Win32 API, the local heap and the global heap are not distinguished. As a result, there is no difference between the memory objects allocated by these functions.

Memory objects allocated by GlobalAlloc and LocalAlloc are in private, committed pages with read-write access. Private memory cannot be accessed by other processes. Memory allocated by using GlobalAlloc with the GMEM_DDESHARE flag is not actually shared globally as it is in Windows version 3. x. However, this flag is available for compatibility purposes and can be used by some applications to enhance the performance of dynamic data exchange (DDE) operations. Applications requiring shared memory for other purposes must use file-mapping objects. Multiple processes can map a view of the same file-mapping object to provide named shared memory. For more information about file mapping, see File Mapping and Shared Memory.

By using GlobalAlloc and LocalAlloc, you can allocate a block of memory of any size that can be represented by 32 bits. You are limited only by the available physical memory, including storage in the paging file on disk. These functions, along with the other global and local functions that manipulate global and local memory objects, are included in the Win32 API for compatibility with 16-bit versions of Windows. However, the change from a 16-bit segmented memory model to a 32-bit virtual memory model has made some of the functions and their options unnecessary or meaningless. For example, there are no longer near and far pointers, because both local and global allocations return 32-bit virtual addresses.

Both GlobalAlloc and LocalAlloc can allocate fixed or movable memory objects. Movable objects can also be marked as discardable. In earlier versions of Windows, movable memory objects were important for memory management. They enabled the system to compact the heap when necessary to make space available for other memory allocations. By using virtual memory, the system is able to manage memory by moving pages of physical memory without affecting the virtual addresses of the processes using the pages. When the system moves a page of physical memory, it simply maps the process's virtual page to the new location of the physical page. Movable memory is still useful for allocating discardable memory. When the system needs additional physical storage, it can use a "least recently used" algorithm to free discardable memory that is not locked. Discardable memory should be used for data that is needed infrequently and can be regenerated easily.

When allocating a fixed memory object, GlobalAlloc and LocalAlloc return a 32-bit pointer that the calling process can immediately use to access the memory. For movable memory, the return value is a handle. To get a pointer to a movable memory object, the calling process uses the GlobalLock and LocalLock functions. These functions lock the memory so that it cannot be moved or discarded. The internal data structures for each memory object include a lock count that is initially zero. For movable memory objects, GlobalLock and LocalLock increment the count by one, and the GlobalUnlock and LocalUnlock functions decrement the count by one. Locked memory is not moved or discarded, unless the memory object is reallocated by using the GlobalReAlloc or LocalReAlloc function. The memory block of a locked memory object remains locked in memory until its lock count is decreased to zero, at which time it can be moved or discarded.

The actual size of the memory allocated by GlobalAlloc or LocalAlloc can be larger than the requested size. To determine the actual number of bytes allocated, use the GlobalSize or LocalSize function. If the amount allocated is greater than the amount requested, the process can use the entire amount.

The GlobalReAlloc and LocalReAlloc functions change the size, in bytes, or the attributes of a memory object allocated by GlobalAlloc and LocalAlloc. The size can increase or decrease.

The GlobalFree and LocalFree functions release memory allocated by GlobalAlloc, LocalAlloc, GlobalReAlloc, or LocalReAlloc.

Other global and local functions include the GlobalDiscard, LocalDiscard, GlobalFlags, LocalFlags, GlobalHandle, and LocalHandle functions. To discard a specified discardable memory object without invalidating the handle, use GlobalDiscard or LocalDiscard. The handle can be used later by GlobalReAlloc or LocalReAlloc to allocate a new block of memory associated with the same handle. To return information about a specified memory object, use GlobalFlags or LocalFlags. The information includes the object's lock count and indicates whether the object is discardable or has already been discarded. To return the handle of the memory object associated with a specified pointer, use GlobalHandle or LocalHandle.

+$#A Standard C Library Functions

Win32 processes can safely use the standard C library functions (malloc, free, and so on) to manipulate memory. When used with previous versions of Windows, these functions had potential problems that no longer apply to applications using the Win32 API. For example, malloc allocates a fixed pointer that does not take advantage of movable memory. Memory management is no longer a problem because the system is free to manage memory by moving pages of physical memory without affecting the virtual addresses. Similarly, the distinction between near and far pointers is no longer relevant. So, unless you want to allocate discardable memory, it is now reasonable to use the standard C library functions for memory management.

+$#A Virtual Memory Functions

The Win32 API provides a set of virtual memory functions that enable a process to manipulate or determine the status of pages in its virtual address space. Many applications are able to satisfy their memory needs by using the standard allocation functions (GlobalAlloc, LocalAlloc, malloc, and so on). However, virtual memory functions provide some capabilities not available to the standard allocation functions. They can perform the following operations:

• Reserve a range of a process's virtual address space. Reserving address space does not allocate any physical storage, but it prevents other allocation operations from using the specified range. It does not affect the virtual address spaces of other processes. Reserving pages prevents needless consumption of physical storage, while enabling a process to reserve a range of its address space into which a dynamic data structure can grow. The process can allocate physical storage for this space, as needed.

• Commit a range of reserved pages in a process's virtual address space so that physical storage (either in RAM or on disk) is accessible only to the allocating process.

• Specify read-write, read-only, or no access for a range of committed pages. This differs from the standard allocation functions that always allocate pages with read-write access.

• Free a range of reserved pages, making the range of virtual addresses available for subsequent allocation operations by the calling process.

• Decommit a range of committed pages, releasing their physical storage and making it available for subsequent allocation by any process.

• Lock one or more pages of committed memory into physical memory (RAM) so that the system cannot swap the pages out to the paging file.

• Obtain information about a range of pages in the virtual address space of the calling process or a specified process.

• Change the access protection for a specified range of committed pages in the virtual address space of the calling process or a specified process.

The virtual memory functions manipulate pages of memory. The functions use the size of a page on the current computer to round off specified sizes and addresses.

To determine the size of a page on the current computer, use the GetSystemInfo function.

The VirtualAlloc function performs one of the following operations:

• Reserves one or more free pages.

• Commits one or more reserved pages.

• Reserves and commits one or more free pages.

You can specify the starting address of the pages to be reserved or committed, or you can allow the system to determine the address. The function rounds the specified address to the appropriate page boundary. Reserved pages are not accessible, but committed pages can be allocated with the PAGE_READWRITE, PAGE_READONLY, or PAGE_NOACCESS flag. When pages are committed, storage is allocated in the paging file, but each page is initialized and loaded into physical memory only at the first attempt to read from or write to that page. You can use normal pointer references to access memory committed by the VirtualAlloc function.

The VirtualFree function performs one of the following operations:

• Decommits one or more committed pages, changing the state of the pages to reserved. Decommitting pages releases the physical storage associated with the pages, making it available to be allocated by any process. Any block of committed pages can be decommitted.

• Releases a block of one or more reserved pages, changing the state of the pages to free. Releasing a block of pages makes the range of reserved addresses available to be allocated by the process. Reserved pages can be released only by freeing the entire block that was initially reserved by VirtualAlloc.

• Decommits and releases a block of one or more committed pages simultaneously, changing the state of the pages to free. The specified block must include the entire block initially reserved by VirtualAlloc, and all of the pages must be currently committed.

The VirtualLock function enables a process to lock one or more pages of committed memory into physical memory (RAM), preventing the system from swapping the pages out to the paging file. It can be used to ensure that critical data is accessible without disk access. Locking pages into memory is dangerous because it restricts the system's ability to manage memory. Excessive use of VirtualLock can degrade system performance by causing executable code to be swapped out to the paging file. The VirtualUnlock function unlocks memory locked by VirtualLock.

The VirtualQuery and VirtualQueryEx functions return information about a region of consecutive pages beginning at a specified address in the address space of a process. VirtualQuery returns information about memory in the calling process. VirtualQueryEx returns information about memory in a specified process and is used to support debuggers that need information about a process being debugged. The region of pages is bounded by the specified address rounded down to the nearest page boundary. It extends through all subsequent pages with the following attributes in common:

• The state of all pages is the same: either committed, reserved, or free.

• If the initial page is not free, all pages in the region are part of the same initial allocation of pages that were reserved by a call to VirtualAlloc.

• The access protection of all pages is the same (that is, the PAGE_READONLY, PAGE_READWRITE, or PAGE_NOACCESS flag).

The VirtualProtect function enables a process to modify the access protection of any committed page in the address space of a process. For example, a process can allocate read-write pages to store sensitive data, and then it can change the access to read only or no access to protect against accidental overwriting. VirtualProtect is typically used with pages allocated by VirtualAlloc, but it also works with pages committed by any of the other allocation functions. However, VirtualProtect changes the protection of entire pages, and pointers returned by the other functions are not necessarily aligned on page boundaries. The VirtualProtectEx function is similar to VirtualProtect, except it changes the protection of memory in a specified process. Changing the protection is useful to debuggers in accessing the memory of a process being debugged.

+$#A Heap Functions

The heap functions enable a process to create a private heap, a block of one or more pages in the address space of the calling process. The process can then use a separate set of functions to manage the memory in that heap. There is no difference between memory allocated from a private heap and that allocated by using the standard allocation functions (GlobalAlloc, LocalAlloc, malloc, and so on).

The HeapCreate function creates a private heap object from which the calling process can allocate memory blocks by using the HeapAlloc function. HeapCreate specifies both an initial size and a maximum size for the heap. The initial size determines the number of committed, read-write pages initially allocated for the heap. The maximum size determines the total number of reserved pages. These pages create a contiguous block in the virtual address space of a process into which the heap can grow. Additional pages are automatically committed from this reserved space if requests by HeapAlloc exceed the current size of committed pages, assuming that the physical storage for it is available. Once the pages are committed, they are not decommitted until the process is terminated or until the heap is destroyed by calling the HeapDestroy function.

The memory of a private heap object is accessible only to the process that created it. If a dynamic-link library (DLL) creates a private heap, it does so in the address space of the process that called the DLL. It is accessible only to that process.

The HeapAlloc function allocates a specified number of bytes from a private heap and returns a pointer to the allocated block. The pointer identifies the block for the HeapFree function to release or for the HeapSize function to determine the size.

Memory allocated by HeapAlloc is not movable. Because the system cannot compact a private heap, the heap can become fragmented.

A possible use for the heap functions is to create a private heap when a process starts up, specifying an initial size sufficient to satisfy the memory requirements of the process. If the call to the HeapCreate function fails, the process can terminate or notify the user of the memory shortage; if it succeeds, however, the process is assured of having the memory it needs.

+$#A Shared Memory

In the Win32 API, shared memory is implemented by file mapping. All memory allocated by the other allocation methods (the GlobalAlloc, LocalAlloc, HeapAlloc, or VirtualAlloc function) is accessible only to the calling process. Memory allocated by a DLL, however, is in the address space of the process that called the DLL and is not accessible to other processes using the same DLL.

Named file mapping provides an easy way to create a block of shared memory. A process can specify a name when it uses the CreateFileMapping function to create a file-mapping object. Other processes can specify the same name to either the CreateFileMapping or OpenFileMapping function to obtain a handle of the mapping object. The names of event objects, semaphore objects, mutex objects, and file-mapping objects share the same name space. If a specified name matches the name of an existing object of a different type, an error occurs. When creating named objects, try to use unique names and check the function return values for duplicate name errors.

Each process specifies its handle of the file-mapping object in the MapViewOfFile function to map a view of the file into its own address space. The views of all processes for a single file-mapping object are mapped into the same sharable pages of physical storage. However, the virtual addresses of the mapped views can vary from one process to another, unless the MapViewOfFileEx function is used to map the view at a specified address. Although sharable, the pages of physical storage used for a mapped file view are not global; they are not accessible to processes that have not mapped a view of the file.

A file-mapping object is associated with a disk file that the system uses when the mapped view is swapped out of physical memory and onto the disk. This disk swap file can be the system's paging file, or it can be some other file that was specified when the file-mapping object was created. In that case, the memory is initialized along with the contents of the file. Mapping a specified file in the file system is useful for processes that need to share the data in an existing file or that want to use the file to save data generated by the sharing processes. If you map a specified file, you should open it for exclusive access and keep the handle open until you are finished with the shared memory. Keeping it open will prevent other processes from opening another handle on the file to use ReadFile or WriteFile or from creating additional mapping objects for the same file, any of which actions can lead to unpredictable results.

Any pages committed by mapping a view of a file are released when the last process with a view of the mapping object either terminates or unmaps its view by calling the UnmapViewOfFile function. At this time, the specified file (if any) associated with the mapping object is updated. A specified file can also be forced to update by calling the FlushViewOfFile function.

For more information about file mapping, see File Mapping. For an example of shared memory in a DLL, see Dynamic-Link Libraries.

If multiple processes have write access to shared memory, access to the memory should be synchronized. For more information about interprocess synchronization, see Synchronization.

+$#A Access Validation

The Win32 API provides a set of functions that a process can use to verify whether it has a specified type of access to a given memory address or range of addresses. The following access validation functions are available.

Function | Description
IsBadCodePtr | Determines whether the calling process has read access to the memory at the specified address.
IsBadReadPtr | Determines whether the calling process has read access to the memory at a specified range of addresses.
IsBadStringPtr | Determines whether the calling process has read access to the memory pointed to by a null-terminated string pointer. The function validates access for a specified number of characters or until it encounters the string's terminating null character.
IsBadWritePtr | Determines whether the calling process has write access to the memory at a specified range of addresses.

The IsBadHugeReadPtr and IsBadHugeWritePtr functions are also available for compatibility with earlier versions of Windows that distinguished between normal memory allocations and huge allocations occupying multiple segments. In the Win32 API, these functions are equivalent to IsBadReadPtr and IsBadWritePtr.

In a preemptive multitasking environment, it is possible for some other thread to change the process's access to the memory being tested. Even when an access validation function indicates that the process has the desired access to the specified memory, you should use structured exception handling when attempting to access the memory. Use of structured exception handling enables the system to notify the process if an access violation exception occurs, giving the process an opportunity to handle the exception. For more information about structured exception handling, see Structured Exception Handling.

+$#A Guard Pages

An application establishes a guard page by setting a memory page's PAGE_GUARD page protection modifier flag. This flag can be specified, along with other page protection flags, in the functions VirtualAlloc, VirtualProtect, and VirtualProtectEx. The PAGE_GUARD flag can be used with any other page protection flag, except for the NO_ACCESS flag.

If a program attempts to access an address within a guard page, the operating system raises a STATUS_GUARD_PAGE (0x80000001) exception. The operating system also clears the PAGE_GUARD flag, removing the memory page's guard page status. The system will not stop the next attempt to access the memory page with a STATUS_GUARD_PAGE exception.

If a guard page exception occurs during a system service, the service fails and typically returns some failure status indicator. Since the system also removes the relevant memory page's guard page status, the next invocation of the same system service won't fail due to a STATUS_GUARD_PAGE exception (unless, of course, someone reestablishes the guard page).

A guard page thus provides a one-shot alarm for memory page access. This can be useful for an application that needs to monitor the growth of large dynamic data structures. For example, there are operating systems that use guard pages to implement automatic stack checking.

The following short program illustrates the one-shot behavior of guard page protection, and how it can cause a system service to fail:

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

int main()

{

// local variables

LPVOID lpvAddr;

DWORD cbSize;

BOOL vLock;

LPVOID commit;

// amount of memory we'll allocate

cbSize = 512;

// try to allocate some memory

lpvAddr = VirtualAlloc(NULL,cbSize,MEM_RESERVE,PAGE_NOACCESS);

// if we failed ...

if(lpvAddr == NULL)

fprintf(stdout,"VirtualAlloc failed on RESERVE with %ld\n",

GetLastError());

// try to commit the allocated memory

commit = VirtualAlloc(NULL,cbSize,MEM_COMMIT,PAGE_READONLY|PAGE_GUARD);

// if we failed ...

if(commit == NULL)

fprintf(stderr,"VirtualAlloc failed on COMMIT with %ld\n",

GetLastError());

else // we succeeded

fprintf(stderr,"Committed %lu bytes at address %lp\n",

cbSize,commit);

// try to lock the committed memory

vLock = VirtualLock(commit,cbSize);

// if we failed ...

if(!vLock)

fprintf(stderr,"Cannot lock at %lp, error = %lu\n",

commit,GetLastError());

else // we succeeded

fprintf(stderr,"Lock Achieved at %lp\n",commit);

// try to lock the committed memory again

vLock = VirtualLock(commit,cbSize);

// if we failed ...

if(!vLock)

fprintf(stderr,"Cannot get 2nd lock at %lp, error = %lu\n",

commit,GetLastError());

else // we succeeded

fprintf(stderr,"2nd Lock Achieved at %lp\n",commit);

} // endof function

The output of this program looks like this:

Committed 512 bytes at address 003F0000

Cannot lock at 003F0000, error = 0x80000001

2nd Lock Achieved at 003F0000

Note that the first attempt to lock the memory block fails, raising a STATUS_GUARD_PAGE exception. The second attempt succeeds, because the memory block's guard page protection has been toggled off by the first attempt.

+$#A Using the Virtual Memory Functions

This section explains how to use the virtual memory functions for dynamic allocation.

The following example illustrates the use of the VirtualAlloc and VirtualFree functions in reserving and committing memory as needed for a dynamic array. First, VirtualAlloc is called to reserve a block of pages with NULL specified as the base address parameter, forcing the kernel to determine the location of the block. Later, VirtualAlloc is called whenever it is necessary to commit a page from this reserved region, and the base address of the next page to be committed is specified.

The example uses try-except structured exception-handling syntax to commit pages from the reserved region. Whenever a page fault exception occurs during the execution of the try block, the filter function in the expression preceding the except block is executed. If the filter function can allocate another page, execution continues in the try block at the point where the exception occurred. Otherwise, the exception handler in the except block is executed. For more information about structured exception handling, see Structured Exception Handling.

As an alternative to dynamic allocation, the process can simply commit the entire region instead of only reserving it. However, committing the region consumes physical storage that might not be needed, making it unavailable for use by other processes.

The example uses VirtualFree to free the reserved and committed pages when it is finished with them. The function is called twice: first to decommit the committed pages and again to release the entire region of reserved pages.

#define PAGELIMIT 80

#define PAGESIZE 0x1000

INT PageFaultExceptionFilter(DWORD);

VOID MyErrorExit(LPTSTR);

LPTSTR lpNxtPage;

DWORD dwPages = 0;

VOID UseDynamicVirtualAlloc(VOID) {

LPVOID lpvBase;

LPTSTR lpPtr;

BOOL bSuccess;

DWORD i;

/* Reserve pages in the process's virtual address space. */

lpvBase = VirtualAlloc(

NULL, /* system selects address */

PAGELIMIT*PAGESIZE, /* size of allocation */

MEM_RESERVE, /* allocates reserved pages */

PAGE_NOACCESS); /* protection = no access */

if (lpvBase == NULL )

MyErrorExit("VirtualAlloc reserve");

lpPtr = lpNxtPage = (LPTSTR) lpvBase;

/*

* Use try-except structured exception handling when

* accessing the pages. If a page fault occurs, the

* exception filter is executed to commit another page

* from the reserved block of pages.

*/

for (i=0; i < PAGELIMIT*PAGESIZE; i++) {

try {

/* Write to memory. */

lpPtr[i] = 'a';

}

/*

* If there is a page fault, commit another page

* and try again.

*/

except ( PageFaultExceptionFilter(

GetExceptionCode() ) ) {

/*

* This is executed only if the filter function is

* unsuccessful in committing the next page.

*/

ExitProcess( GetLastError() );

}

}

/* Release the block of pages when you are finished using them. */

/* First, decommit the committed pages. */

bSuccess = VirtualFree(

lpvBase, /* base address of block */

dwPages*PAGESIZE, /* bytes of committed pages */

MEM_DECOMMIT); /* decommit the pages */

/* Release the entire block. */

if (bSuccess)

bSuccess = VirtualFree(

lpvBase, /* base address of block */

0, /* releases the entire block */

MEM_RELEASE); /* releases the pages */

}

INT PageFaultExceptionFilter(DWORD dwCode) {

LPVOID lpvResult;

/* If the exception is not a page fault, exit. */

if (dwCode != EXCEPTION_ACCESS_VIOLATION) {

printf("exception code = %d\n", dwCode);

return EXCEPTION_EXECUTE_HANDLER;

}

printf("page fault\n");

/* If the reserved pages are used up, exit. */

if (dwPages >= PAGELIMIT) {

printf("out of pages\n");

return EXCEPTION_EXECUTE_HANDLER;

}

/* Otherwise, commit another page. */

lpvResult = VirtualAlloc(

(LPVOID) lpNxtPage, /* next page to commit */

PAGESIZE, /* page size, in bytes */

MEM_COMMIT, /* alloc committed page */

PAGE_READWRITE); /* read-write access */

if (lpvResult == NULL ) {

printf("VirtualAlloc failed\n");

return EXCEPTION_EXECUTE_HANDLER;

}

/*

* Increment the page count, and advance lpNxtPage

* to the next page.

*/

dwPages++;

lpNxtPage += PAGESIZE;

/* Continue execution where the page fault occurred. */

return EXCEPTION_CONTINUE_EXECUTION;

}

+$#A Memory Management Functions

Following are the Win32 functions used in memory management.

CopyMemory
FillMemory
GetProcessHeap
GetProcessHeaps
GlobalAlloc
GlobalDiscard
GlobalFlags
GlobalFree
GlobalHandle
GlobalLock
GlobalMemoryStatus
GlobalReAlloc
GlobalSize
GlobalUnlock
HeapAlloc
HeapCompact
HeapCreate
HeapDestroy
HeapFree
HeapLock
HeapReAlloc
HeapSize
HeapUnlock
HeapValidate
IsBadCodePtr
IsBadHugeReadPtr
IsBadHugeWritePtr
IsBadReadPtr
IsBadStringPtr
IsBadWritePtr
LocalAlloc
LocalDiscard
LocalFlags
LocalFree
LocalHandle
LocalLock
LocalReAlloc
LocalSize
LocalUnlock
MoveMemory
VirtualAlloc
VirtualFree
VirtualLock
VirtualProtect
VirtualProtectEx
VirtualQuery
VirtualQueryEx
VirtualUnlock
ZeroMemory

Obsolete or Deleted Functions

ChangeSelector
DefineHandleTable
FreeSelector
GetCodeInfo
GetFreeSpace
GlobalCompact
GlobalDosAlloc
GlobalDosFree
GlobalFix
GlobalLRUNewest
GlobalLRUOldest
GlobalNotify
GlobalPageLock
GlobalPageUnlock
GlobalUnfix
GlobalUnWire
GlobalWire
LimitEmsPages
LocalCompact
LocalInit
LocalShrink
LockSegment
SetSwapAreaSize
SwitchStackBack
SwitchStackTo
UnlockSegment

Créé avec HelpNDoc Personal Edition: Créer des sites web d'aide facilement


Menus (Windows NT).rtf

+$#AK Menus (Windows NT)

A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with the menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.

This Windows NT overview describes menus and explains how to use them in a Win32-based application.

+$#A About Menus

{ewl msdncd, EWGraphic, group10806 0 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview About Extended Menus.

A menu is one or more lists of application commands. Each command, which is represented by a text string or bitmap, is called a menu item. By using the mouse or the keyboard to choose a menu item, the user directs the application to perform the command associated with that menu item. When the user chooses a menu item, Microsoft® Windows® sends the application a command message that indicates which menu item the user chose.

Win32 also provides extended menu functions that give applications an improved way to create and control menus. The extended functions supersede the menu functions described in this topic and should be used in Win32-based applications whenever possible. For information about extended menu functions, see Extended Menus.

+$#A Menu Bars and Pop-up Menus

{ewl msdncd, EWGraphic, group10806 1 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Bars and Pop-up Menus.

The menus in an application written for Microsoft Windows are arranged hierarchically. A menu bar (sometimes called a top-level menu) resides at the uppermost level of the hierarchy; the lower levels consist of pop-up menus (also known as submenus).

The items in a menu bar represent the main categories of commands provided by an application. Selecting an item from the menu bar typically activates a pop-up menu whose items correspond to the commands in a given category. For example, a menu bar might contain a File item that, when the user selects it, activates a pop-up menu with menu items such as New, Open, Save, and so on.

Only an overlapped or pop-up window can contain a menu bar; a child window cannot contain one. A menu bar is always visible. If the window has a title bar, Windows positions the menu bar just below it. By contrast, a pop-up menu is not visible until the user selects a menu item that activates it. When this menu is displayed, it is attached to the title bar.

Every menu must have an owner window. Windows sends messages to a menu's owner window when the user selects the menu or chooses a command from the menu. These messages are described in Menu Messages Overview.

+$#A Floating Pop-up Menus

{ewl msdncd, EWGraphic, group10806 2 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Floating Pop-up Menus.

Windows provides a special type of pop-up menu called a floating pop-up menu. A floating pop-up menu is not attached to the menu bar; it can appear anywhere on the screen. An application typically associates a floating pop-up menu with a portion of a window such as the client area or with a specific object such as an icon.

A floating pop-up menu remains hidden until the user selects it (for example, by clicking the appropriate screen location with the mouse.) A floating pop-up menu is typically displayed at the position of the caret or mouse cursor.

+$#A The System Menu

{ewl msdncd, EWGraphic, group10806 3 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview The System Menu.

A System menu (also known as the Control menu) is a pop-up menu defined and managed almost exclusively by the system. It is represented by a small box containing a horizontal line that is displayed at the left end of the title bar. When the user clicks the box, Windows displays the System menu. Windows also displays the System menu when the user clicks the icon of a minimized application.

A System menu provides a standard set of menu items the user can choose to change a window's size or position, close the application, or activate the Windows Task List. Items in a System menu can be added, deleted, and modified, but most applications just use the standard set of menu items. An overlapped, pop-up, or child window can have a System menu. It is uncommon for an overlapped or pop-up window not to include a System menu.

When the user chooses a command from a System menu, Windows sends a WM_SYSCOMMAND message to the menu's owner window. In most applications, the window procedure does not process messages from a System menu. Instead, it simply passes the messages to the DefWindowProc function for system-default processing of the message. If a command is added to a System menu, the window procedure must process the command.

+$#A Menu Handles

{ewl msdncd, EWGraphic, group10806 4 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Handles.

The system generates a unique handle for each menu. A menu handle is a value of the HMENU type. An application must specify a menu handle in many of the Windows menu functions. You receive the handle of a menu bar when you create the menu or loads a menu resource. For information about creating menus and loading menus, see Menu Creation.

The GetMenu or GetSubMenu function is used to retrieve the handle of a menu that has been created or loaded. GetMenu returns the handle of the menu bar assigned to the specified window. GetSubMenu returns the handle of a pop-up menu within the specified menu bar or pop-up menu. The GetSystemMenu function is used to retrieve the handle of a window's System menu.

+$#A Class Menus

{ewl msdncd, EWGraphic, group10806 5 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Class Menus.

When registering a window class, the developer can specify a default menu for that class. This default menu is known as the class menu. To specify a class menu, the developer must assign the name of the menu, as given in your application's resource-definition file, to the lpszMenuName member of the WNDCLASS structure used to register the window class. After the window class has been registered, each window of that class will have the specified class menu. The developer can override the class menu by specifying the handle of a different menu when creating a window of that class. For more information about window classes, see Window Classes.

+$#A Menu Items

{ewl msdncd, EWGraphic, group10806 6 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Items.

There are two kinds of menu items in Win32 applications: command items and pop-up items. When the user chooses a command item, Windows sends a WM_COMMAND or WM_SYSCOMMAND message to the window procedure of the menu's owner window. A command item on a System menu generates a WM_SYSCOMMAND message; command items in other menus generate WM_COMMAND messages. When the user selects a pop-up item, Windows displays a pop-up menu but doesn't send a command message to the window procedure.

A menu bar typically contains pop-up items, but may also contain command items. A pop-up menu typically contains command items, but may also contain pop-up items. By adding pop-up items to a pop-up menu, you can nest pop-up menus to an arbitrary depth. If you include a pop-up item in a pop-up menu, Windows automatically displays a small arrow to the right of the menu item text as a visual cue to the user.

+$#A Menu-Item Identifier

{ewl msdncd, EWGraphic, group10806 7 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Menu Item Identifier.

Associated with each menu item is a unique, application-defined integer, called an identifier. When the user chooses a command item, Windows sends the item's identifier to the owner window as part of a WM_COMMAND message. The window procedure examines the identifier to determine the source of the message, and processes the message accordingly. In addition, you can specify a menu item using its identifier when you call menu functions ⎯ for example, to enable or disable a menu item.

A menu identifier must be a value from 0 to 65,535, even though it is a 32-bit integer. This is because the WM_COMMAND message passes a menu identifier as the low-order word of its wParam parameter.

Beginning with the Win32 API, pop-up menu items have identifiers just as other menu items do. However, Windows does not send a WM_COMMAND message when a pop-up menu item is selected. Instead, Windows activates the pop-up menu associated with the pop-up menu item.

+$#A Checked and Unchecked Menu Items

{ewl msdncd, EWGraphic, group10806 8 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Checked and Unchecked Menu Items.

A menu item can be either checked or unchecked. When a menu item is checked, Windows uses a bitmap to display a check mark to the left of the menu item. When the menu item is unchecked, Windows does not display the check mark. Only menu items in a pop-up menu can be checked; menu items in a menu bar can not be checked.

An application typically uses check marks to indicate menu options that are in effect. For example, when the user selects paragraph formatting options from a menu in a word processing application, the application places check marks next to the selected options. The following illustration shows check marks next to the Left and Single Space options in a word processing application's Paragraph menu.

A check mark attribute controls whether a menu item is checked or not. A menu item's check mark attribute can be set by using the CheckMenuItem function. The GetMenuState function is used to determine whether a menu item is currently checked or unchecked.

Windows provides a default bitmap for displaying check marks. The developer, however, can substitute other bitmaps and direct Windows to use them instead of the default. Windows requires, however, that these check mark bitmaps have the same dimensions as the default check mark bitmap. The GetMenuCheckMarkDimensions function can be used to retrieve the default dimensions.

Two bitmaps can be associated with each menu item: one for when the menu item is checked and another for when it is unchecked. The SetMenuItemBitmaps function can be used to associate bitmaps with a menu item.

The developer can either provide check mark bitmaps as bitmap resources or create the bitmaps at run time by using graphic device interface (GDI) bitmap functions. A bitmap resource, however, isn't suitable for all video resolutions and aspect ratios, so the bitmap must be sized appropriately. The check mark bitmaps can be either monochrome or color. However, on a color screen the bitmap colors are inverted when the menu item is highlighted, and this result may be undesirable. For more information about bitmaps, see Bitmaps.

+$#A Enabled, Disabled, and Graye Menu Items

{ewl msdncd, EWGraphic, group10806 9 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Enabled, Disabled, and Grayed Menu Items.

A menu item can be enabled, disabled, or grayed. By default, a menu item is enabled. When the user chooses an enabled menu item, Windows sends a command message to the corresponding window procedure or displays the corresponding pop-up menu, depending on whether the menu item is a command item or a pop-up item.

When menu items are not available to the user, they should be disabled or grayed. Disabled or grayed menu items do not generate a command message or display a pop-up menu when a user chooses them. Typically, applications gray an unavailable menu item to provide a visual clue to the user that a command is not available. A disabled menu item does not provide this visual clue. A disabled item can serve as a heading for group related menu items. (For example, a Save heading can be listed and then a series of commands such as Save File, Save All, and Save As). A grayed item can be used when an action is not appropriate. (For example, the Print command in the File menu can be grayed when the system does not have a printer installed.)

The EnableMenuItem function is used to enable, disable, or gray a menu item, and the GetMenuState function is used to determine whether a menu item is enabled, disabled, or grayed.

+$#A Menu-Item Separators and Line Breaks

{ewl msdncd, EWGraphic, group10806 10 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Menu Item Separators and Line Breaks.

Windows provides a special type of menu item, called a separator, that consists of a horizontal line. A separator menu item can be used in a pop-up menu to divide a menu into groups of related items. A separator item cannot be used in a menu bar. The user cannot select a separator menu item, and a separator does not send command messages to the window procedure.

When a menu bar contains more items than will fit on one line, Windows automatically breaks it into two or more lines. You can force a line break at a specific item on a menu bar by assigning the MF_MENUBREAK flag to the item. Windows places that item and all remaining items on a new line.

The MF_MENUBREAK and MF_MENUBARBREAK flags are used to divide items in a pop-up menu into multiple columns. When MF_MENUBREAK flag is assigned to an item, Windows places it and all remaining items in a new column. The MF_MENUBARBREAK flag creates a similar effect, except Windows includes a vertical line in the menu to separate the new column from the old one.

+$#A Menu-Item Position

{ewl msdncd, EWGraphic, group10806 11 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Menu-Item Position.

Windows assigns an integer position value to each item on a menu and uses the value to keep track of each item's position in the menu. Position values are zero-based; that is, the first item on a menu occupies position zero, the next position one, and so on. In a menu bar, the leftmost menu item is considered the first item, so it occupies position zero; in a pop-up menu, the uppermost menu item occupies position zero. Windows assigns position values to menu-item separators. The following illustration shows the position values of both items and separators in a menu bar and a pop-up menu.

{ewc msdncd, EWGraphic, group10806 12 /a "SDK_05.bmp"}

A number of functions are used to add an item to a menu, delete an item from a menu, or modify an existing item on a menu. When one of these functions is called, the item to be modified is identified either by its position or by its identifier. Either the MF_BYPOSITION or MF_BYCOMMAND flag is also specified. For more information about modifying the contents of a menu, see Menu Modifications.

+$#A Highlighted Menu Items

{ewl msdncd, EWGraphic, group10806 13 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Highlighted Menu Items.

Windows automatically highlights menu items as the user selects them. Highlighting can, however, be explicitly added or removed from a menu item on the menu bar by calling the HiliteMenuItem function. This function has no effect on menu items in pop-up menus. When you use HiliteMenuItem to highlight a menu item, however, the menu item only appears to be selected; if the user presses the ENTER key, the highlighted item is not chosen.

+$#A Owner-drawn Menu Items

An application can completely control the appearance of a menu item by using an owner-drawn item. Owner-drawn items require an application to take total responsibility for drawing selected (highlighted), checked, and unchecked states. For example, if an application provided a font menu it could draw each menu item by using the corresponding font; the item for roman would be drawn with a roman font, the item for italic would be drawn in italic, and so on.

For information on how to create owner-drawn menu items, see Creating Owner-drawn Menu Items.

+$#A Help Item

The developer should provide a standard interface for the user, including a Help item on an application's menu bars. The item should read "Help," and it should be the rightmost item on the menu bar.

+$#A Keyboard Access to Menus

{ewl msdncd, EWGraphic, group10806 14 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Keyboard Access to Menus.

Windows provides a standard keyboard interface for menus. You can enhance this interface by including mnemonics and accelerator keys for your menu items. The following three sections describe the standard keyboard interface, mnemonics, and accelerator keys.

+$#A Standard Keyboard Interface

{ewl msdncd, EWGraphic, group10806 15 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Standard Keyboard Interface.

Windows is designed to work with or without a mouse or other pointing device. Because Windows provides a standard keyboard interface the user can use the keyboard to select menu items. Special code is not needed to support this keyboard interface. An application receives a command message whether a menu item is selected through the keyboard or by using a mouse. The standard keyboard interface processes the following keystrokes.

Keystroke | Action
ALT | Toggles in and out of menu bar mode.
ALT+SPACEBAR | Displays the System menu.
ESC | Exits menu mode.
RIGHT ARROW | Cycles to the next top-level menu item. This keystroke cycles between the application and System menus. If the selected item is in a pop-up menu, the next column in the pop-up menu is selected or the next top-level menu item is selected.
LEFT ARROW | Works like the RIGHT ARROW key, except in the opposite direction. In pop-up menus, this keystroke backs up one column; when the currently selected item is in the far-left column, the previous pop-up menu is selected.
UP or DOWN ARROWS | Activate a pop-up menu when pressed in a top-level menu. When pressed in a pop-up menu, either keystroke selects the next or previous item.
ENTER | Activates a pop-up menu and highlights the first item if an item has a pop-up menu associated with it; otherwise, this keystroke chooses the item as if the user released the mouse button while the item was selected.
Alphabetic character | Selects the first menu item with the specified character as its mnemonic. If the selected item onvokes a pop-up menu, the menu is displayed and the first item is highlighted; otherwise, the item is chosen.

+$#A Menu Mnemonics

{ewl msdncd, EWGraphic, group10806 16 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Mnemonics.

The standard keyboard interface for menus can be enhanced by adding mnemonics to menu items. A mnemonic is a underlined letter in the text of a menu item. When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter. The menu bar is active when the user presses and releases the ALT key and the first item on the menu bar is highlighted. A pop-up menu is active when it is displayed.

A mnemonic keystroke for a menu item is created by preceding a character in the item's text string with an ampersand. For example, "&Move" causes Windows to underline the "M" character.

+$#A Menu Keyboard Accelerators

{ewl msdncd, EWGraphic, group10806 17 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Keyboard Accelerators.

In addition to having a mnemonic, a menu item can have a keyboard accelerator associated with it. A keyboard accelerator is different from a mnemonic in that the menu need not be active for the accelerator key to work. Also, a mnemonic is always associated with a menu item, while a keyboard accelerator is usually associated with a menu item, but it does not have to be.

Text to identify the keyboard accelerator is added to the menu-item text string. The accelerator text appear to the right of the menu-item name after a backslash and tab character. For example, "&Close\tAlt+F4" represents a Close item with the ALT+F4 key combination as its keyboard accelerator and with C as its mnemonic. For more information on accelerators, see Keyboard Accelerators.

+$#A Menu Creation

{ewl msdncd, EWGraphic, group10806 18 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Creation.

You can use the following methods to create a menu for use in an application:

• Load a menu from a menu-template resource.

• Build a menu from a menu template created at run time.

• Use the menu creation functions to build a menu at run time.

The following sections describe how to use these three methods to create menus.

+$#A Menu-Template Resources

{ewl msdncd, EWGraphic, group10806 19 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Menu Template Resources.

Most developers implement menus by creating a menu-template resource that is loaded at run time. A menu template defines a menu, including the items in the menu bar and all pop-up menus. For information about creating menu template resources, see your tools documentation.

Once you have created a menu-template resource and added it to your application's executable file, you can use the LoadMenu function to load the menu-template resource into memory. This function returns the handle of the menu, which you can then assign to a window by using the SetMenu function.

Implementing menus as resources makes an application easier to localize for use in multiple countries. Only the resource definition needs to be translated for each language, not the application's source code.

+$#A Menu Template in Memory

{ewl msdncd, EWGraphic, group10806 20 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Template in Memory.

Instead of loading a menu-template resource, an application can create create a menu template in memory at run time. You create a menu from a template in memory by using the LoadMenuIndirect function. For descriptions of menu-template formats, see Using a Menu-Template Resource.

+$#A Menu-Creation Functions

{ewl msdncd, EWGraphic, group10806 21 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Menu Creation Functions.

Another way to create a menu at run time is to use the CreateMenu and CreatePopupMenu functions to create empty menus. CreateMenu creates an empty menu bar; CreatePopupMenu creates an empty pop-up menu. Then menu items are added to the menus by using the AppendMenu or InsertMenu functions.

+$#A Menu Display

{ewl msdncd, EWGraphic, group10806 22 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Display.

After a menu has been loaded or created, it must be assigned to a window before Windows can display it. This can be done by creating a class menu (see Class Menus), by specifying the handle of the menu as the hmenu parameter of the CreateWindowEx function, or by calling the SetMenu function.

The TrackPopupMenu function can be used to display a pop-up menu. To do so, the handle of a pop-up menu is passed to the function. The menu's owner window, the screen position where Windows should display the menu, and the mouse button the user must use to choose an item must also be specified.

+$#A Menu Destruction

{ewl msdncd, EWGraphic, group10806 23 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Destruction.

If a menu is assigned to a window and that window is destroyed, Windows automatically destroys the menu, freeing the menu's handle and the memory occupied by the menu. Windows does not automatically destroy a menu that is not assigned to a window. An application must destroy the unassigned menu by calling the DestroyMenu function; otherwise, the menu continues to exist in memory even after the application closes.

+$#A Menu Messages Overview

{ewl msdncd, EWGraphic, group10806 24 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Messages.

Windows reports menu-related activity by sending messages to the window procedure of the window that owns the menu. The first menu-related message the window procedure receives is WM_SYSCOMMAND. Windows sends this message when the user activates the menu by clicking the menu bar or by pressing a key that activates the menu (for more information, see Keyboard Access to Menus). The WM_SYSCOMMAND message includes a flag, either SC_KEYMENU or SC_MOUSEMENU, that indicates the hardware device used to activate the menu.

Next, Windows sends the WM_INITMENU message to the window procedure before Windows displays any pop-up menus, so an application can modify the menus before the user sees them. Windows sends the WM_INITMENU message only once per menu activation.

When the user selects a pop-up item, Windows sends the owner window the WM_INITMENUPOPUP message before displaying the pop-up menu. This message gives the application a second opportunity to modify the pop-up menu before it is displayed.

Each time the user moves the highlighting from one item to another, Windows sends a WM_MENUSELECT message to the window procedure of the menu's owner window. This message identifies the currently selected menu item. Many applications provide an information area at the bottom of their main windows and use this message to display additional information about the selected menu item.

Windows sends a WM_COMMAND message to the window procedure when the user chooses a command item from a menu. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.

+$#A Menu Modifications

{ewl msdncd, EWGraphic, group10806 25 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Menu Modifications.

A number of functions can be used to modify a menu after it has been loaded or created. The AppendMenu and InsertMenu functions are used to add items to a menu. AppendMenu appends a menu item to the end of a menu; InsertMenu inserts a menu item at a specified position in a menu. Both functions permit the attributes of the menu item to be specified, including whether the menu item is enabled, checked, grayed, and so on.

The ModifyMenu function is used to change the appearance or attributes of an existing menu item. For example, the text string or bitmap of a menu item can be enabled, disabled, grayed, checked, or unchecked. ModifyMenu replaces the specified menu item with a new item.

The DeleteMenu or RemoveMenu functions are used to delete a menu item from a menu. If the item being deleted is a pop-up item, DeleteMenu deletes the associated pop-up menu, discarding the menu handle and freeing the memory used by the pop-up menu. The RemoveMenu function deletes a menu item, but if the item activates a pop-up menu, it does not destroy the pop-up menu or its handle, allowing the menu to be reused.

After a menu bar has been modified, the DrawMenuBar function must be called to redraw the menu bar. Otherwise, the modifications will not appear until Windows redraws the owner window.

+$#A Using Menus

• Using a menu template resource.

• Creating a floating pop-up menu.

• Using menu item bitmaps.

• Creating owner-drawn menu items.

• Using custom check mark bitmaps.

+$#A Using a Menu-Template Resource

{ewl msdncd, EWGraphic, group10806 26 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Using a Menu Template Resource.

You typically include a menu in an application by defining a menu template in the application's resource-definition file and then loading the menu at run time. Alternatively, you can create a menu template dynamically in memory. This section describes the format of a menu template, and explains how to load a menu-template resource. For information about defining a menu-template resource, see your tools documentation.

+$#A Extended Menu Template Format

The extended menu template format supports the new menu functionality available in the Win32 API. Like old menu template resources, extended menu template resources have the RC_MENU resource type. Windows distinguishes the two resource formats by the version number, which is the first member of the resource header.

Note The structure definitions in this section are for explanation only. They are not defined in any standard header file.

An extended menu template consists of a header followed by one more more item definitions. The header has the following form:

typedef struct {

WORD wVersion; // must be 1 for extended menu resources

WORD wOffset; // offset of first item; typically 4

DWORD dwHelpId; // Help identifier for menu

} MENUEX_HDR;

Following is a description of each member:

wVersion

Specifies the template version number. This value must be 1 for extended menu templates.

wOffset

Specifies the offset of the first menu-item definition, relative to the end of this member. If the first menu-item definition immediately follows the dwHelpId member, this value should be 4.

dwHelpId

Specifies the Help identifier for the menu.

Following the header are one or more item definitions. Item definitions vary in size depending on the length of the menu text, and are packed on byte boundaries. Each item definition has the following form:

typedef struct {

DWORD dwType; // menu-item type (MFT_* flags)

DWORD dwState; // menu-item state (MFS_* flags)

WORD wId; // application-defined menu-item identifier

BYTE bResInfo; // resource info (MFR_* flags)

char szText[1]; // null-terminated menu text

// dwHelpId - In pop-up menu items, a 32-bit Help identifier

// follows the variable-length szText member.

} MENUEX_ITEM;

Following is a description of each member:

dwType

Specifies the type of menu item. Can be a combination of the MFT_* flags defined in WINDOWS.H. For a list of menu-item types, see the description of the MENUITEMINFO structure.

dwState

Specifies the menu-item state. Can be a combination of the MFS_* flags defined in WINDOWS.H. For a list of menu-item states, see the description of the MENUITEMINFO structure.

wId

Specifies the menu-item identifier. This is an application-defined 16-bit value used to identify the menu item. Pop-up menu items can now have identifiers; previously, only command menu-items could have identifiers.

bResInfo

Specifies the type of menu-item definition. Can be zero or more of these values:

MFR_POPUP | The item definition is for a pop-up menu item.
MFR_END | The item is the last menu item in the menu or submenu.

szText

Specifies the menu-item text. This member is a null-terminated ANSI string. The size of the menu-item definition varies depending on the length of the string.

dwHelpId

Specifies the Help identifier for a submenu. This member follows the menu-item text for pop-up menu items. It is omitted for other menu items.

The following assembly-language example defines an extended menu template:

My_Menu_Template LABEL DWORD

dw 0001h ; template version number

dw 0004h ; Windows version number

dd 0 ; Help id for main menu

; The following item definition specifies a pop-up menu

; item for a File submenu.

dd MFT_STRING ; type

dd 0 ; state

dw 1 ; identifier

db MFR_POPUP ; specifies a pop-up item

db "&File", 0 ; text, including null character

dd 0 ; Help id for the File submenu

; The following definition specifies an Exit command item,

; which appears in the File submenu. Because it is the last

; item in the submenu, the MFR_END flag is specified.

dd MFT_STRING ; type

dd MFS_DEFAULT ; state

dw 2 ; identifier

db MFR_END ; last menu-item in submenu

db "E&xit", 0 ; text, including null character

; The following definition specifies a Help command item.

; Because it is the last item in the menu, the MFR_END flag

; is specified.

dd MFT_STRING ; type

dd 0 ; state

dw 3 ; id

db MFR_END ; last menu-item in main menu

db "&Help", 0 ; text, including null character

+$#A Old Menu Template Format

An old menu template defines a menu, but does not support the new menu functionality available in the Win32 API. An old menu template resource has the RT_MENU resource type. An old menu template consists of a MENUITEMTEMPLATEHEADER structure followed by one or more MENUITEMTEMPLATE structures.

+$#A Loading a Menu-Template Resource

{ewl msdncd, EWGraphic, group10806 27 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Loading a Menu Template Resource.

You can load a menu-template resource by calling the LoadMenu function, specifying the handle of the module that contains the resource and the menu-template's identifier. LoadMenu returns a menu handle that you can use to assign the menu to a window. This window becomes the menu's owner window, receiving all the messages generated by the menu.

You can create a menu from a menu template that is already in memory by using the LoadMenuIndirect function. This is useful if your application generates menu templates dynamically.

Use the SetMenu function to assign a menu to a window, or specify the menu's handle in the hmenu parameter of the CreateWindowEx function when you create a window. Another way that you can assign a menu to a window is to specify a menu template when you register a window class. This identifies the menu as the class menu for the given window class. Whenever you create a window of the given class, Windows automatically assigns the given menu to the window.

You can create a class menu by including the identifier of the menu-template resource as the lpszMenuName member of a WNDCLASS structure and then passing the address of the structure to the RegisterClass function.

+$#A Creating a Class Menu

{ewl msdncd, EWGraphic, group10806 28 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Creating a Class Menu.

The following example shows how to create a class menu for an application, how to create a window that uses the class menu, and how to process menu commands in the window procedure.

HINSTANCE hinst;

int APIENTRY WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hinstance;

HINSTANCE hPrevInstance;

LPSTR lpCmdLine;

int nCmdShow;

{

MSG msg; /* message */

WNDCLASS wc; /* window-class data */

HWND hwnd; /* handle of main window */

/*

* Create the window class for the main window. Specify

* the identifier of the menu-template resource as the

* lpszMenuName member of the WNDCLASS structure to create

* the class menu.

*/

wc.style = 0;

wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hinstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MyMenuResource";

wc.lpszClassName = "MainWClass";

if (!RegisterClass(&wc))

return FALSE;

hinst = hinstance;

/*

* Create the main window. Set the hmenu parameter to NULL so

* that Windows uses the class menu for the window.

*/

hwnd = CreateWindow("MainWClass", "Sample Application",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,

NULL);

if (hwnd == NULL)

return FALSE;

/*

* Make the window visible and send a WM_PAINT message to the

* window procedure.

*/

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

/* Start the main message loop. */

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

UNREFERENCED_PARAMETER(hPrevInstance);

}

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

switch (uMsg) {

.

. /* Process other window messages. */

.

case WM_COMMAND:

/* Test for the identifier of a command item. */

switch(LOWORD(wParam)) {

case IDM_FI_OPEN:

DoFileOpen(); /* application-defined */

break;

case IDM_FI_CLOSE:

DoFileClose(); /* application-defined */

break;

.

. /* Process other menu commands. */

.

default:

break;

}

return 0;

.

. /* Process other window messages. */

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return NULL;

}

+$#A Creating a Floating Pop-up Menu

{ewl msdncd, EWGraphic, group10806 29 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Creating a Floating Pop-up Menu.

To use a floating pop-up menu in an application, pass the handle of a pop-up menu to the TrackPopupMenu function. An application typically calls TrackPopupMenu in a window procedure in response to a user-generated message, such as WM_LBUTTONDOWN or WM_KEYDOWN.

In addition to the pop-up menu handle, TrackPopupMenu requires that you specify the handle of the owner window, the position of the floating pop-up menu (in screen coordinates), and the mouse button the user can use to choose an item.

Specify the position by providing x- and y-coordinates along with the TPM_CENTERALIGN, TPM_LEFTALIGN, or TPM_RIGHTALIGN flag. The flag specifies the position of the menu relative to the x- and y-coordinates.

You should permit the user to choose an item from a pop-up menu by using the same mouse button used to display the menu. To do this, specify either TPM_LEFTBUTTON or TPM_RIGHTBUTTON flags to indicate which mouse button the user can use to select a menu item.

+$#A Creating a Floating Font-Attributes Menu

The example in this section contains portions of code from an application that creates and displays a floating pop-up menu that enables the user to set fonts and font attributes. The application displays the menu in the client area of its main window whenever the user clicks the left mouse button.

Here is the menu template for the pop-up menu that is provided in the application's resource-definition file.

PopupMenu MENU

BEGIN

POPUP "Dummy Popup"

BEGIN

POPUP "Fonts"

BEGIN

MENUITEM "Courier", IDM_FONT_COURIER

MENUITEM "Times Roman", IDM_FONT_TMSRMN

MENUITEM "Swiss", IDM_FONT_SWISS

MENUITEM "Helvetica", IDM_FONT_HELV

MENUITEM "Old English", IDM_FONT_OLDENG

END

POPUP "Sizes"

BEGIN

MENUITEM "7", IDM_SIZE_7

MENUITEM "8", IDM_SIZE_8

MENUITEM "9", IDM_SIZE_9

MENUITEM "10", IDM_SIZE_10

MENUITEM "11", IDM_SIZE_11

MENUITEM "12", IDM_SIZE_12

MENUITEM "14", IDM_SIZE_14

END

POPUP "Styles"

BEGIN

MENUITEM "Bold", IDM_STYLE_BOLD

MENUITEM "Italic", IDM_STYLE_ITALIC

MENUITEM "Strike Out", IDM_STYLE_SO

MENUITEM "Superscript", IDM_STYLE_SUPER

MENUITEM "Subscript", IDM_STYLE_SUB

END

END

END

The following example gives the window procedure and supporting functions used to create and display the floating pop-up menu.

LRESULT APIENTRY MenuWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

RECT rc; /* client area */

POINT pt; /* location of mouse click */

switch (uMsg) {

case WM_LBUTTONDOWN:

/* Get the bounding rectangle of the client area. */

GetClientRect(hwnd, (LPRECT) &rc);

/* Get the client coordinates for the mouse click. */

pt.x = LOWORD(lParam);

pt.y = HIWORD(lParam);

/*

* If the mouse click took place inside the client

* area, execute the application-defined function

* that displays the floating pop-up menu.

*/

if (PtInRect((LPRECT) &rc, pt))

HandlePopupMenu(hwnd, pt);

break;

.

. /* Process other window messages. */

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return NULL;

}

VOID APIENTRY HandlePopupMenu(hwnd, pt)

HWND hwnd;

POINT pt;

{

HMENU hmenu; /* menu template */

HMENU hmenuTrackPopup; /* floating pop-up menu */

/*

* Load the menu template containing the pop-up menu from the

* application's resources.

*/

hmenu = LoadMenu(hinst, "PopupMenu");

if (hmenu == NULL)

return;

/*

* Get the first pop-up menu in the menu template. This is the

* menu that TrackPopupMenu displays.

*/

hmenuTrackPopup = GetSubMenu(hmenu, 0);

/*

* TrackPopup uses screen coordinates, so convert the

* coordinates of the mouse click to screen coordinates.

*/

ClientToScreen(hwnd, (LPPOINT) &pt);

/* Draw and track the floating pop-up menu. */

TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,

pt.x, pt.y, 0, hwnd, NULL);

/* Destroy the menu.*/

DestroyMenu(hmenu);

}

+$#A Using Menu-Item Bitmaps

{ewl msdncd, EWGraphic, group10806 30 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview, Using Menu Item Bitmaps.

Windows can use a bitmap instead of a text string to display a menu item. To use a bitmap, you must set the MF_BITMAP flag for the menu item and specify the handle of the bitmap that Windows should display for the menu item. This section describes how to set the MF_BITMAP flag and retrieve the handle of a bitmap.

+$#A Setting the MF_BITMAP Flag

The MF_BITMAP flag tells Windows to use a bitmap rather than a text string to display a menu item. A menu item's MF_BITMAP flag must be set at run time; you cannot set it in the resource-definition file.

You can use the ModifyMenu, InsertMenu, or AppendMenu function to set the MF_BITMAP flag. To convert a menu item from a text string item to a bitmap item, use ModifyMenu. To add a new bitmap item to a menu, use the MF_BITMAP flag with the InsertMenu or AppendMenu function.

+$#A Creating the Bitmap

{ewl msdncd, EWGraphic, group10806 31 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Creating the Bitmap.

When you set the MF_BITMAP flag for a menu item, you must also specify the handle of the bitmap that Windows should display for the menu item. You can provide the bitmap as a bitmap resource or create the bitmap at run time.

To provide a bitmap resource, use a resource editor, such as ImageEdit, to create a bitmap resource, and then use a resource compiler to add the bitmap to your application's executable file. At run time, use the LoadBitmap function to load the bitmap and obtain its handle.

To create the bitmap at run time, use GDI functions. GDI provides several ways to create a bitmap at run time, but developers typically use the following method:

• Use the CreateCompatibleDC function to create a device context compatible with the device context used by the application's main window.

• Use the CreateCompatibleBitmap function to create a bitmap compatible with the application's main window.

• Use the SelectObject function to select the bitmap into the compatible device context.

• Use GDI drawing functions, such as Ellipse and LineTo, to draw an image into the bitmap.

• Pass the handle of the bitmap to the ModifyMenu, InsertMenu, or AppendMenu function.

For more information about bitmaps, see Bitmaps.

+$#A Adding Lines and Graphs to a Menu

The following code sample shows how to create a menu that contains menu-item bitmaps. It creates two pop-up menus shown in the following illustration. The first is a Chart menu that contains three menu-item bitmaps: a pie chart, a line chart, and a bar chart. The example demonstrates how to load these bitmaps from the application's resource file, and then use the CreatePopupMenu and AppendMenu functions to create the menu and menu items.

The second pop-up menu is a Lines menu. It contains bitmaps showing the line styles provided by the predefined pen in Windows. The line-style bitmaps are created at run time by using GDI functions.

Here are the definitions of the bitmap resources in the application's resource-definition file.

PIE BITMAP pie.bmp

LINE BITMAP line.bmp

BAR BITMAP bar.bmp

Here are the relevant portions of the application's header file.

/* Menu-item identifiers */

#define IDM_SOLID PS_SOLID

#define IDM_DASH PS_DASH

#define IDM_DASHDOT PS_DASHDOT

#define IDM_DASHDOTDOT PS_DASHDOTDOT

#define IDM_PIE 1

#define IDM_LINE 2

#define IDM_BAR 3

/* Line-type flags */

#define SOLID 0

#define DOT 1

#define DASH 2

#define DASHDOT 3

#define DASHDOTDOT 4

/* Count of pens */

#define CPENS 5

/* Chart-type flags */

#define PIE 1

#define LINE 2

#define BAR 3

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);

VOID MakeChartMenu(HWND);

VOID MakeLineMenu(HWND, HPEN, HBITMAP);

The following example shows how menus and menu-item bitmaps are created in an application.

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

static HPEN hpen[CPENS];

static HBITMAP hbmp[CPENS];

int i;

switch (uMsg) {

case WM_CREATE:

/* Create the Chart and Line menus. */

MakeChartMenu(hwnd);

MakeLineMenu(hwnd, hpen, hbmp);

return 0;

.

. /* Process other window messages. */

.

case WM_DESTROY:

for (i = 0; i < CPENS; i++) {

DeleteObject(hbmp[i]);

DeleteObject(hpen[i]);

}

PostQuitMessage(0);

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return NULL;

}

VOID MakeChartMenu(hwnd)

HWND hwnd; /* handle of owner window */

{

HBITMAP hbmpPie; /* handle of pie chart bitmap */

HBITMAP hbmpLine; /* handle of line chart bitmap */

HBITMAP hbmpBar; /* handle of bar chart bitmap */

HMENU hmenuMain; /* handle of main menu */

HMENU hmenuChart; /* handle of chart pop-up menu */

/*

* Load the pie, line, and bar chart bitmaps from the

* resource-definition file.

*/

hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE));

hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE));

hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR));

/*

* Create the Chart pop-up menu and add it to the menu bar.

* Append the Pie, Line, and Bar menu items to the Chart

* pop-up menu.

*/

hmenuMain = GetMenu(hwnd);

hmenuChart = CreatePopupMenu();

AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart,

"Chart");

AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie);

AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE,

(LPCTSTR) hbmpLine);

AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar);

return;

}

VOID MakeLineMenu(hwnd, phpen, phbmp)

HWND hwnd;

HPEN *phpen;

HBITMAP *phbmp;

{

HMENU hmenuLines; /* handle of Lines pop-up menu */

HMENU hmenu; /* handle of main menu */

COLORREF crMenuClr; /* menu-item background color */

HBRUSH hbrBackground; /* handle of background brush */

HBRUSH hbrOld; /* handle of previous brush */

LONG lCheckXY; /* dimensions of check mark bitmap */

WORD wLineX; /* width of line bitmaps */

WORD wLineY; /* height of line bitmaps */

HDC hdcMain; /* handle of main window's DC */

HDC hdcLines; /* handle of compatible DC */

HBITMAP hbmpOld; /* handle of previous bitmap */

int i; /* loop counter */

/* Create the Lines pop-up menu. Add it to the menu bar. */

hmenu = GetMenu(hwnd);

hmenuLines = CreatePopupMenu();

AppendMenu(hmenu, MF_STRING | MF_POPUP,

(UINT) hmenuLines, "&Lines");

/* Create a brush for the menu-item background color. */

crMenuClr = GetSysColor(COLOR_MENU);

hbrBackground = CreateSolidBrush(crMenuClr);

/*

* Create a compatible device context for the line bitmaps,

* and then select the background brush into it.

*/

hdcMain = GetDC(hwnd);

hdcLines = CreateCompatibleDC(hdcMain);

hbrOld = SelectObject(hdcLines, hbrBackground);

/*

* Get the dimensions of the check mark bitmap. The width of

* the line bitmaps will be five times the width of the

* check mark bitmap.

*/

lCheckXY = GetMenuCheckMarkDimensions();

wLineX = LOWORD(lCheckXY) * (WORD) 5;

wLineY = HIWORD(lCheckXY);

/*

* Create the bitmaps and select them, one at a time, into the

* compatible device context. Initialize each bitmap by

* filling it with the menu-item background color.

*/

for (i = 0; i < CPENS; i++) {

phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY);

if (i == 0)

hbmpOld = SelectObject(hdcLines, phbmp[i]);

else

SelectObject(hdcLines, phbmp[i]);

ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER);

}

/* Create the pens. */

phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0));

phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0));

phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));

phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));

/*

* Select a pen and a bitmap into the compatible device

* context, draw a line into the bitmap, and then append

* the bitmap as an item in the Lines menu.

*/

for (i = 0; i < CPENS; i++) {

SelectObject(hdcLines, phbmp[i]);

SelectObject(hdcLines, phpen[i]);

MoveToEx(hdcLines, 0, wLineY / 2, NULL);

LineTo(hdcLines, wLineX, wLineY / 2);

AppendMenu(hmenuLines, MF_BITMAP, i + 1,

(LPCTSTR) phbmp[i]);

}

/*

* Release the main window's device context and destroy the

* compatible device context. Also, destroy the background

* brush.

*/

ReleaseDC(hwnd, hdcMain);

SelectObject(hdcLines, hbrOld);

DeleteObject(hbrBackground);

SelectObject(hdcLines, hbmpOld);

DeleteDC(hdcLines);

return;

}

+$#A Creating Owner-Drawn Menu Items

If you need complete control over the appearance of a menu item, you can use an owner-drawn menu item on your application. This section describes the steps involved in creating and using an owner-drawn menu item.

+$#A Setting the MF_OWNERDRAW Flag

You cannot define an owner-drawn menu item on your application's resource-definition file. Instead, you must create a new menu item or modify an existing one by using the MF_OWNERDRAW menu flag.

You can use the AppendMenu, InsertMenu, or ModifyMenu function to specify an owner-drawn menu item. Use AppendMenu to add a new item to the end of the given menu. Use InsertMenu to place a new item onto a menu, moving the other items down. Use ModifyMenu to change the contents of a menu.

When you call any of these three functions, you can pass a 32-bit value as the lpNewItem parameter. This value can represent any information that is meaningful to your application, and that will be available to your application when the item is to be displayed. For example, the value could contain a pointer to a structure; the structure, in turn, might contain a text string and the handle of a logical font that your application will use to draw the string.

+$#A Responding to the WM_MEASUREITEM Message

Before Windows displays an owner-drawn menu item for the first time, it sends the WM_MEASUREITEM message to the window procedure of the window that owns the item's menu. This message contains a pointer to a MEASUREITEMSTRUCT structure that identifies the item and contains an optional 32-bit value for the item. When the window procedure receives the WM_MEASUREITEM message, it must fill the itemWidth and itemHeight members of the structure before returning from processing the message. Windows uses the information in these members when creating the bounding rectangle in which your application draws the menu item. It also uses the information to detect when the user chooses the item.

+$#A Responding to the WM_DRAWITEM Message

Whenever the item must be drawn (for example, when it is first displayed or when the user chooses it as a command), Windows sends the WM_DRAWITEM message to the window procedure of the menu's owner window. This message contains a pointer to a DRAWITEMSTRUCT structure. Like the MEASUREITEMSTRUCT structure, DRAWITEMSTRUCT contains information about the item and its optional 32-bit data. In addition, DRAWITEMSTRUCT contains flags that indicate the state of the item (such as whether it is grayed or checked) as well as a bounding rectangle and a device context that the application uses to draw the item.

In response to the WM_DRAWITEM message, your application must perform the following actions before returning from processing the message:

1. Determine the type of drawing that is necessary. To do so, check the itemAction member of the DRAWITEMSTRUCT structure.

2. Draw the menu item appropriately, using the bounding rectangle and device context obtained from the DRAWITEMSTRUCT structure. Your application must draw only within the bounding rectangle. For performance reasons, Windows doesn't clip portions of the image that are drawn outside the rectangle.

3. Restore all GDI objects selected for the menu item's device context.

If the menu item is selected, Windows sets the itemAction member of the DRAWITEMSTRUCT structure to ODA_SELECT and sets the ODS_SELECTED flag in the itemState member. This is an application's cue to redraw the menu item to indicate that it is selected.

+$#A Setting Fonts for Menu-Item Text Strings

This section contains an example from an application that uses owner-drawn menu items in a pop-up menu. The pop-up menu contains items that set the attributes of the current font, and the items are displayed using the appropriate font attribute.

Here is how the menu is defined in the resource-definition file. Note that the strings for the Regular, Bold, Italic, and Underline items are assigned at run time, so their strings are empty in the resource-definition file.

MainMenu MENU

BEGIN

POPUP "&Character"

BEGIN

MENUITEM "", IDM_REGULAR

MENUITEM SEPARATOR

MENUITEM "", IDM_BOLD

MENUITEM "", IDM_ITALIC

MENUITEM "", IDM_ULINE

END

END

The application's window procedure processes the messages involved in using owner-drawn menu items. The application uses the WM_CREATE message to do the following:

• Set the MF_OWNERDRAW flag for the menu items.

• Set the text strings for the menu items.

• Obtain handles of the fonts used to draw the items.

• Obtain the text and background color values for selected menu items.

The text strings and font handles are stored in an array of application-defined MYITEM structures. The application-defined GetAFont function creates a font that corresponds to the given font attribute and returns the handle of the font. The handles are destroyed during the processing of the WM_DESTROY message.

During the processing of the WM_MEASUREITEM message, the example gets the width and height of a menu-item string and copies these values into the MEASUREITEMSTRUCT structure. Windows uses the width and height values to calculate the size of the pop-up menu.

During the processing of the WM_DRAWITEM message, the menu item's string is drawn with room left next to the string for the check mark bitmap. If the user selects the item, the selected text and background colors are used to draw the item.

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

typedef struct _MYITEM {

HFONT hfont;

LPSTR psz;

} MYITEM; /* structure for item font and string */

MYITEM *pmyitem; /* pointer to item's font and string */

static MYITEM myitem[CITEMS]; /* array of MYITEMS */

static HMENU hmenu; /* handle of main menu */

static COLORREF crSelText; /* text color of selected item */

static COLORREF crSelBkgnd; /* background color of selected item */

COLORREF crText; /* text color of unselected item */

COLORREF crBkgnd; /* background color unselected item */

LPMEASUREITEMSTRUCT lpmis; /* points to item of data */

LPDRAWITEMSTRUCT lpdis; /* points to item drawing data */

HDC hdc; /* handle of screen DC */

SIZE size; /* menu-item text extents */

DWORD dwCheckXY; /* check mark dimensions */

WORD wCheckX; /* check mark width */

int nTextX; /* width of menu item */

int nTextY; /* height of menu item */

int i; /* loop counter */

HFONT hfontOld; /* handle of old font */

BOOL fSelected = FALSE; /* menu-item selection flag */

switch (uMsg) {

case WM_CREATE:

/*

* Modify the Regular, Bold, Italic, and Underline

* menu items to make them owner-drawn items. Associate

* a MYITEM structure with each item to contain the

* string and font handle for each item.

*/

hmenu = GetMenu(hwnd);

ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND |

MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR,

(LPTSTR) &myitem[REGULAR]);

ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND |

MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]);

ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND |

MF_OWNERDRAW, IDM_ITALIC,

(LPTSTR) &myitem[ITALIC]);

ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND |

MF_OWNERDRAW, IDM_ULINE, (LPTSTR) &myitem[ULINE]);

/*

* Retrieve each item's font handle and copy it into

* the hfont member of each item's MYITEM structure.

* Also, copy each item's string into the structures.

*/

myitem[REGULAR].hfont = GetAFont(REGULAR);

myitem[REGULAR].psz = "Regular";

myitem[BOLD].hfont = GetAFont(BOLD);

myitem[BOLD].psz = "Bold";

myitem[ITALIC].hfont = GetAFont(ITALIC);

myitem[ITALIC].psz = "Italic";

myitem[ULINE].hfont = GetAFont(ULINE);

myitem[ULINE].psz = "Underline";

/*

* Retrieve the text and background colors of the

* selected menu text.

*/

crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);

crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);

return 0;

case WM_MEASUREITEM:

/* Retrieve a device context for the main window. */

hdc = GetDC(hwnd);

/*

* Retrieve pointers to the menu item's

* MEASUREITEMSTRUCT structure and MYITEM structure.

*/

lpmis = (LPMEASUREITEMSTRUCT) lParam;

pmyitem = (MYITEM *) lpmis->itemData;

/*

* Select the font associated with the item into

* the main window's device context.

*/

hfontOld = SelectObject(hdc, pmyitem->hfont);

/*

* Retrieve the width and height of the item's string,

* and then copy the width and height into the

* MEASUREITEMSTRUCT structure's itemWidth and

* itemHeight members.

*/

GetTextExtentPoint32(hdc, pmyitem->psz,

lstrlen(pmyitem->psz), &size);

lpmis->itemWidth = size.cx;

lpmis->itemHeight = size.cy;

/*

* Select the old font back into the device context,

* and then release the device context.

*/

SelectObject(hdc, hfontOld);

ReleaseDC(hwnd, hdc);

return TRUE;

break;

case WM_DRAWITEM:

/*

* Get pointers to the menu item's DRAWITEMSTRUCT

* structure and MYITEM structure.

*/

lpdis = (LPDRAWITEMSTRUCT) lParam;

pmyitem = (MYITEM *) lpdis->itemData;

/*

* If the user has selected the item, use the selected

* text and background colors to display the item.

*/

if (lpdis->itemState & ODS_SELECTED) {

crText = SetTextColor(lpdis->hDC, crSelText);

crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);

fSelected = TRUE;

}

/*

* Remember to leave space in the menu item for the

* check mark bitmap. Retrieve the width of the bitmap

* and add it to the width of the menu item.

*/

dwCheckXY = GetMenuCheckMarkDimensions();

wCheckX = LOWORD(dwCheckXY);

nTextX = wCheckX + lpdis->rcItem.left;

nTextY = lpdis->rcItem.top;

/*

* Select the font associated with the item into the

* item's device context, and then draw the string.

*/

hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);

ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,

&lpdis->rcItem, pmyitem->psz,

lstrlen(pmyitem->psz), NULL);

/*

* Select the previous font back into the device

* context.

*/

SelectObject(lpdis->hDC, hfontOld);

/*

* Return the text and background colors to their

* normal state (not selected).

*/

if (fSelected) {

SetTextColor(lpdis->hDC, crText);

SetBkColor(lpdis->hDC, crBkgnd);

}

return TRUE;

.

. /* Process other messages. */

.

case WM_DESTROY:

/* Destroy the menu items' font handles. */

for (i = 0; i < CITEMS; i++)

DeleteObject(myitem[i].hfont);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return NULL;

}

HFONT GetAFont(fnFont)

int fnFont; /* font-attribute flag */

{

static LOGFONT lf; /* structure for font information */

/*

* Get a handle to the ANSI fixed-pitch font, and copy

* information about the font to a LOGFONT structure.

*/

GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT),

&lf);

/* Set the font attributes, as appropriate. */

if (fnFont == BOLD)

lf.lfWeight = FW_BOLD;

else

lf.lfWeight = FW_NORMAL;

lf.lfItalic = (fnFont == ITALIC);

lf.lfItalic = (fnFont == ULINE);

/* Create the font, and then return its handle. */

return CreateFont(lf.lfHeight, lf.lfWidth,

lf.lfEscapement, lf.lfOrientation, lf.lfWeight,

lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,

lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,

lf.lfPitchAndFamily, lf.lfFaceName);

}

+$#A Using Custom Check Mark Bitmaps

{ewl msdncd, EWGraphic, group10806 32 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Using Custom Check Mark Bitmaps.

A Windows video device driver provides a default check mark bitmap that Windows displays next to a menu item that is checked. You can customize an individual menu item by providing a pair of bitmaps to replace the default check mark bitmap. Windows displays one bitmap when the item is checked and the other when it is unchecked. This section describes the steps involved in creating and using custom check mark bitmaps.

+$#A Creating Custom Check Mark Bitmaps

{ewl msdncd, EWGraphic, group10806 33 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Creating Custom Check Mark Bitmaps.

A custom check mark bitmap must be the same size as the default check mark bitmap. You can retrieve the default check mark bitmap's size by calling the GetMenuCheckMarkDimensions function. The low-order word of this function's return value specifies the width; the high-order word specifies the height.

Bitmap resources can be used to provide check mark bitmaps. However, a bitmap resource is not suitable for all screen resolutions and aspect ratios, so you'll need to use the StretchBlt function to size the bitmap, as appropriate. Depending on the bitmap being sized, the result of the sizing may not be acceptable, because stretching may distort the bitmap.

Instead of using a bitmap resource, GDI functions can be used to provide check mark bitmaps at run time. This involves the following steps, which an application typically performs during the WM_CREATE message:

1. Create a device context in memory that is compatible with the screen.

2. Create a bitmap that is compatible with the display.

3. Select the bitmap into the device context.

4. Draw or copy an image into the bitmap.

Call the CreateCompatibleDC function to create a device context that is compatible with the screen. The function's hdc parameter can specify either NULL or the return value from the GetDC function. CreateCompatibleDC returns the handle of the compatible device context.

Call the CreateCompatibleBitmap function to create an "empty" bitmap that is compatible with the screen. This function's nWidth and nHeight parameters set the size of the bitmap; they should specify the width and height information returned by the GetMenuCheckMarkDimensions function.

The CreateCompatibleBitmap function returns the handle of the bitmap. Pass this handle of the SelectObject function to select the bitmap into the compatible device context. Afterward, you can use the GDI drawing functions such as Rectangle and Ellipse to draw into the bitmap, or you can use functions such as BitBlt and StretchBlt to copy an image into the bitmap.

GDI provides several other ways to create bitmaps. For more information about bitmaps, see Bitmaps.

+$#A Associating Bitmaps with a Menu Item

{ewl msdncd, EWGraphic, group10806 34 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Associating Bitmaps with a Menu Item.

You associate a pair of check mark bitmaps with a menu item by passing the handles of the bitmaps to the SetMenuItemBitmaps function. The hbmUnchecked parameter identifies the unchecked bitmap; the hbmChecked parameter identifies the checked bitmap. If you want to remove one or both check marks from a menu item, you can set the hbmUnchecked or hbmChecked parameter, or both, to NULL.

+$#A Setting the Check Mark Attribute

{ewl msdncd, EWGraphic, group10806 35 /a "SDK.BMP"} Windows NT Overview
For more information, see the Windows 95 overview Setting the Check Mark Attribute.

The CheckMenuItem function sets a menu item's check mark attribute to either checked or unchecked. Specify the MF_CHECKED flag to set the check mark attribute to checked, or specify MF_UNCHECKED to set it to unchecked.

+$#A Simulating Check Boxes in a Menu

This section contains an example that shows how to simulate check boxes in a pop-up menu. The example contains a Character menu whose items allow the user to set the bold, italic, and underline attributes of the current font. When a font attribute is in effect, a check mark is displayed in the check box next to the corresponding menu item; otherwise, it is displayed in an empty check box next to the item.

The example in this section replaces the default check mark bitmap with two bitmaps: a bitmap with a checked box and the bitmap with an empty box. The checked check box bitmap is displayed next to the Bold, Italic, or Underline menu item when the item's check mark attribute is set to MF_CHECKED. The unchecked or empty check box bitmap is displayed when the check mark attribute is set to MF_UNCHECKED.

Windows provides a predefined bitmap that contains the images used for check boxes and radio buttons. The example in this section isolates the checked and empty check boxes, copies them to two separate bitmaps, and then uses them as the checked and unchecked bitmaps for items in the Character menu.

To retrieve the handle of the system-defined check box bitmap, the example in this section calls the LoadBitmap function, specifying NULL as the hinst parameter and OBM_CHECKBOXES as the lpBitmapName parameter. Because the images in the bitmap are all the same size, the example can isolate them by dividing the bitmap's width and height by the number of images in its rows and columns.

The following portion of a resource-definition file shows how the menu items in the Character menu are defined. Note that no font attributes are in effect initially, so the check mark attribute for the Regular item is set to checked and, by default, the check mark attribute of the remaining items is set to unchecked.

#include "men3.h"

MainMenu MENU

BEGIN

POPUP "&Character"

BEGIN

MENUITEM "&Regular", IDM_REGULAR, CHECKED

MENUITEM SEPARATOR

MENUITEM "&Bold", IDM_BOLD

MENUITEM "&Italic", IDM_ITALIC

MENUITEM "&Underline", IDM_ULINE

END

END

Here is the relevant contents of the application's header file.

/* Menu-item identifiers */

#define IDM_REGULAR 0x1

#define IDM_BOLD 0x2

#define IDM_ITALIC 0x4

#define IDM_ULINE 0x8

/* Check mark flags */

#define CHECK 1

#define UNCHECK 2

/* Font-attribute mask */

#define ATTRIBMASK 0xe

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);

HBITMAP GetMyCheckBitmaps(UINT);

BYTE CheckOrUncheckMenuItem(BYTE, HMENU);

The following example shows the portions of the window procedure that create the check mark bitmaps, set the check mark attribute of the Bold, Italic, and Underline menu items, and destroy check mark bitmaps.

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

static HBITMAP hbmpCheck; /* handle of checked bitmap */

static HBITMAP hbmpUncheck; /* handle of unchecked bitmap */

static HMENU hmenu; /* handle of main menu */

BYTE fbFontAttrib; /* font-attribute flags */

switch (uMsg) {

case WM_CREATE:

/*

* Call the application-defined GetMyCheckBitmaps

* function to get the predefined checked and

* unchecked check box bitmaps.

*/

hbmpCheck = GetMyCheckBitmaps(CHECK);

hbmpUncheck = GetMyCheckBitmaps(UNCHECK);

/*

* Set the checked and unchecked bitmaps for the menu

* items.

*/

hmenu = GetMenu(hwndMain);

SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND,

hbmpUncheck, hbmpCheck);

SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND,

hbmpUncheck, hbmpCheck);

SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND,

hbmpUncheck, hbmpCheck);

return 0;

case WM_COMMAND:

switch (LOWORD(wParam)) {

/* Process the menu commands. */

case IDM_REGULAR:

case IDM_BOLD:

case IDM_ITALIC:

case IDM_ULINE:

/*

* CheckOrUncheckMenuItem is an application-

* defined function that sets the menu item

* check marks and returns the user-selected

* font attributes.

*/

fbFontAttrib = CheckOrUncheckMenuItem(

(BYTE) LOWORD(wParam), hmenu);

.

. /* Set the font attributes. */

.

return 0;

.

. /* Process other command messages. */

.

default:

break;

}

break;

.

. /* Process other window messages. */

.

case WM_DESTROY:

/* Destroy the checked and unchecked bitmaps. */

DeleteObject(hbmpCheck);

DeleteObject(hbmpUncheck);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}

HBITMAP GetMyCheckBitmaps(fuCheck)

UINT fuCheck; /* CHECK or UNCHECK flag */

{

COLORREF crBackground; /* background color */

HBRUSH hbrBackground; /* background brush */

HBRUSH hbrTargetOld; /* original background brush */

HDC hdcSource; /* source device context */

HDC hdcTarget; /* target device context */

HBITMAP hbmpCheckboxes; /* handle of check box bitmap */

BITMAP bmCheckbox; /* structure for bitmap data */

HBITMAP hbmpSourceOld; /* handle of original source bitmap */

HBITMAP hbmpTargetOld; /* handle of original target bitmap */

HBITMAP hbmpCheck; /* handle of check mark bitmap */

RECT rc; /* rectangle for check box bitmap */

DWORD dwCheckXY; /* dimensions of check mark bitmap */

WORD wBitmapX; /* width of check mark bitmap */

WORD wBitmapY; /* height of check mark bitmap */

/*

* Get the menu background color and create a solid brush

* with that color.

*/

crBackground = GetSysColor(COLOR_MENU);

hbrBackground = CreateSolidBrush(crBackground);

/*

* Create memory device contexts for the source and

* destination bitmaps.

*/

hdcSource = CreateCompatibleDC((HDC) NULL);

hdcTarget = CreateCompatibleDC(hdcSource);

/*

* Get the size of the Windows default check mark bitmap and

* create a compatible bitmap of the same size.

*/

dwCheckXY = GetMenuCheckMarkDimensions();

wBitmapX = LOWORD(dwCheckXY);

wBitmapY = LOWORD(dwCheckXY);

hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX,

wBitmapY);

/*

* Select the background brush and bitmap into the target DC.

*/

hbrTargetOld = SelectObject(hdcTarget, hbrBackground);

hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck);

/*

* Use the selected brush to initialize the background color

* of the bitmap in the target device context.

*/

PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY);

/*

* Load the predefined check box bitmaps and select it

* into the source DC.

*/

hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL,

(LPTSTR) OBM_CHECKBOXES);

hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes);

/*

* Fill a BITMAP structure with information about the

* check box bitmaps, and then find the upper-left corner of

* the unchecked check box or the checked check box.

*/

GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox);

if (fuCheck == UNCHECK) {

rc.left = 0;

rc.right = (bmCheckbox.bmWidth / 4);

}

else {

rc.left = (bmCheckbox.bmWidth / 4);

rc.right = (bmCheckbox.bmWidth / 4) * 2;

}

rc.top = 0;

rc.bottom = (bmCheckbox.bmHeight / 3);

/*

* Copy the appropriate bitmap into the target DC. If the

* check box bitmap is larger than the default check mark

* bitmap, use StretchBlt to make it fit; otherwise, just

* copy it.

*/

if (((rc.right - rc.left) > (int) wBitmapX) ||

((rc.bottom - rc.top) > (int) wBitmapY))

StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY,

hdcSource, rc.left, rc.top, rc.right - rc.left,

rc.bottom - rc.top, SRCCOPY);

else

BitBlt(hdcTarget, 0, 0, rc.right - rc.left,

rc.bottom - rc.top,

hdcSource, rc.left, rc.top, SRCCOPY);

/*

* Select the old source and destination bitmaps into the

* source and destination DCs, and then delete the DCs and

* the background brush.

*/

SelectObject(hdcSource, hbmpSourceOld);

SelectObject(hdcTarget, hbrTargetOld);

hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld);

DeleteObject(hbrBackground);

DeleteObject(hdcSource);

DeleteObject(hdcTarget);

/* Return the handle of the new check mark bitmap. */

return hbmpCheck;

}

BYTE CheckOrUncheckMenuItem(bMenuItemID, hmenu)

BYTE bMenuItemID;

HMENU hmenu;

{

DWORD fdwMenu;

static BYTE fbAttributes;

switch (bMenuItemID) {

case IDM_REGULAR:

/*

* Whenever the Regular menu item is selected, add a

* check mark to it and then remove check marks from

* any font-attribute menu items.

*/

CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |

MF_CHECKED);

if (fbAttributes & ATTRIBMASK) {

CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND |

MF_UNCHECKED);

CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND |

MF_UNCHECKED);

CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND |

MF_UNCHECKED);

}

fbAttributes = IDM_REGULAR;

return fbAttributes;

case IDM_BOLD:

case IDM_ITALIC:

case IDM_ULINE:

/*

* Toggle the check mark for the selected menu item and

* set the font attribute flags appropriately.

*/

fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID,

MF_BYCOMMAND);

if (!(fdwMenu & MF_CHECKED)) {

CheckMenuItem(hmenu, (UINT) bMenuItemID,

MF_BYCOMMAND | MF_CHECKED);

fbAttributes |= bMenuItemID;

} else {

CheckMenuItem(hmenu, (UINT) bMenuItemID,

MF_BYCOMMAND | MF_UNCHECKED);

fbAttributes ^= bMenuItemID;

}

/*

* If any font attributes are currently selected,

* remove the check mark from the Regular menu item;

* if no attributes are selected, add a check mark

* to the Regular menu item.

*/

if (fbAttributes & ATTRIBMASK) {

CheckMenuItem(hmenu, IDM_REGULAR,

MF_BYCOMMAND | MF_UNCHECKED);

fbAttributes &= (BYTE) ~IDM_REGULAR;

} else {

CheckMenuItem(hmenu, IDM_REGULAR,

MF_BYCOMMAND | MF_CHECKED);

fbAttributes = IDM_REGULAR;

}

return fbAttributes;

}

}

+$#AK Menu Reference

The following functions and messages are associated with menus.

+$#A Menu Functions

Following are the Windows functions used with menus.

AppendMenu
CheckMenuItem
CheckMenuRadioItem
CreateMenu
CreatePopupMenu
DeleteMenu
DestroyMenu
DrawMenuBar
EnableMenuItem
GetMenu
GetMenuCheckMarkDimensions
GetMenuDefaultItem
GetMenuItemCount
GetMenuItemID
GetMenuItemInfo
GetMenuItemRect
GetMenuState
GetMenuString
GetSubMenu
GetSystemMenu
HiliteMenuItem
InsertMenu
InsertMenuItem
IsMenu
LoadMenu
LoadMenuIndirect
MenuItemFromPoint
ModifyMenu
RemoveMenu
SetMenu
SetMenuDefaultItem
SetMenuItemBitmaps
SetMenuItemInfo
TrackPopupMenu
TrackPopupMenuEx

Obsolete or Deleted Functions

ChangeMenu

+$#A Menu Messages

Following are the Windows messages used with menus.

WM_CONTEXTMENU
WM_DRAWITEM
WM_ENTERMENULOOP
WM_EXITMENULOOP
WM_INITMENU
WM_INITMENUPOPUP
WM_MEASUREITEM
WM_MENUCHAR
WM_MENUSELECT
WM_SYSCOMMAND

Créé avec HelpNDoc Personal Edition: Créer des documentations web iPhone


Messages and Message Queues.rtf

+$#AK Messages and Message Queues

This overview describes messages and message queues and explains how to use them in your application.

+$#A About Messages and Message Queues

Unlike traditional applications, applications written for Microsoft® Windows® do not make explicit function calls (such as to getchar) to obtain input. Instead, they wait for Windows to pass input to them.

Windows passes all input for an application to the various windows in the application. Each window has a function, called a window procedure, that Windows calls whenever it has input for the window. The window procedure processes the input and returns control to Windows. For more information about window procedures, see Window Procedures.

+$#A Messages

Windows passes input to a window procedure in the form of messages. Messages are generated by Windows and by applications. Windows generates a message at each input event ⎯ for example, when the user types, moves the mouse, or clicks a control such as a scroll bar. Windows also generates messages in response to changes in the system brought about by an application, such as when an application changes the pool of system font resources or resizes one of its windows. An application can generate messages to direct its own windows to perform tasks or to communicate with windows in other applications.

Windows sends a message to a window procedure with a set of four parameters: a window handle, a message identifier, and two 32-bit values called message parameters. The window handle identifies the window for which the message is intended. Windows uses it to determine which window procedure to send the message to.

A message identifier is a named constant that identifies the purpose of a message. When a window procedure receives a message, it uses a message identifier to determine how to process the message. For example, the message identifier WM_PAINT tells the window procedure that the window's client area has changed and must be repainted.

Message parameters specify data or the location of data used by a window procedure when processing a message. The meaning and value of the message parameters depend on the message. A message parameter can contain an integer, packed bit flags, a pointer to a structure containing additional data, and so on. When a message does not use message parameters, it typically sets them to NULL. A window procedure must check the message identifier to determine how to interpret the message parameters.

+$#A Message Routing

Windows uses two methods to route messages to a window procedure: posting messages to a first-in, first-out queue called a message queue, a system-defined memory object that temporarily stores messages, and sending messages directly to a window procedure.

Messages posted to a message queue are called queued messages. They are primarily the result of user input entered through the mouse or keyboard, such as WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR messages. Other queued messages include the timer, paint, and quit messages: WM_TIMER, WM_PAINT, and WM_QUIT. Most other messages, which are sent directly to a window procedure, are called nonqueued messages.

+$#A Queued Messages

Windows can display any number of windows at a time. To route mouse and keyboard input to the appropriate window, Windows uses message queues.

Windows maintains a single system message queue and any number of thread message queues, one for each GUI thread. To avoid the overhead of creating a message queue for non-GUI threads, all threads are created initially without a message queue. The system creates a thread's message queue only when the thread makes its first call to one of the Win32 USER or GDI functions.

Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the mouse or keyboard converts the input into messages and places them in the system message queue. Windows removes the messages, one at a time, from the system message queue, examines them to determine the destination window, and then posts them to the message queue of the thread that created the destination window. A thread's message queue receives all mouse and keyboard messages for the windows created by the thread. The thread removes messages from its queue and directs Windows to send them to the appropriate window procedure for processing. For more information about threads, see Processes and Threads.

With the exception of the WM_PAINT message, Windows always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first-in, first-out sequence. The WM_PAINT message, however, is kept in the queue and is forwarded to the window procedure only when the queue contains no other messages. Multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.

The system posts a message to a thread's message queue by filling an MSG structure and then copying it to the message queue. Information in MSG includes: the handle of the window for which the message is intended, the message identifier, the two message parameters, the time the messate was posted, and the mouse cursor position. A thread can post a message to its own message queue or to the queue of another thread by using the PostMessage or PostThreadMessage function.

An application can remove a message from its queue by using the GetMessage function. To examine a message without removing it from its queue, an application can use the PeekMessage function. This function fills MSG with information about the message.

After removing a message from its queue, an application can use the DispatchMessage function to direct Windows to send the message to a window procedure for processing. DispatchMessage takes a pointer to MSG that was filled by a previous call to the GetMessage or PeekMessage function. DispatchMessage passes the window handle, the message identifier, and the two message parameters to the window procedure, but it does not pass the time the message was posted or mouse cursor position. An application can retrieve this information by calling the GetMessageTime and GetMessagePos functions while processing a message.

+$#A Nonqueued Messages

Nonqueued messages are sent immediately to the destination window procedure, bypassing the system message queue and thread message queue. Windows typically sends nonqueued messages to notify a window of events that affect it. For example, when the user activates a new application window, Windows sends the window a series of messages, including WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR. These messages notify the window that it has been activated, that keyboard input is being directed to the window, and that the mouse cursor has been moved within the borders of the window. Nonqueued messages can also result when an application calls certain Windows functions. For example, Windows sends the WM_WINDOWPOSCHANGED message after an application uses the SetWindowPos function to move a window.

An application can send a message by calling the SendMessage, SendNotifyMessage, or SendDlgItemMessage function. For more information about sending messages, see Sending a Message.

+$#A Message Handling

An application must remove and process messages posted to the message queues of its threads. A single-threaded application usually uses a message loop in its WinMain function to remove and send messages to the appropriate window procedures for processing. Applications with multiple threads can include a message loop in each thread that creates a window. The following sections describe how a message loop works and explain the role of a window procedure.

+$#A Message Loop

A simple message loop consists of one function call to each of these three functions: GetMessage, TranslateMessage, and DispatchMessage. The GetMessage function retrieves a message from the queue and copies it to an MSG structure. GetMessage returns TRUE unless it encounters the WM_QUIT message, in which case it returns FALSE and ends the loop. In a single-threaded application, ending the message loop is often the first step in closing the application. An application can end its own loop by using the PostQuitMessage function, typically in response to the WM_DESTROY message in the window procedure of the application's main window.

If you specify a window handle in GetMessage, only messages for the specified window are retrieved from the queue. GetMessage can also filter messages in the queue, retrieving only those messages that fall within a specified range. For more information about filtering messages, see Message Filtering.

A thread's message loop must include TranslateMessage, if the thread is to receive character input from the keyboard. Windows generates virtual-key messages (WM_KEYDOWN and WM_KEYUP) each time the user presses a key. A virtual-key message contains a virtual-key code that identifies which key was pressed, but not its character value. To retrieve this value, the message loop must contain TranslateMessage, which translates the virtual-key message into a character message (WM_CHAR) and places it back into the application message queue. The character message can then be removed upon a subsequent iteration of the message loop and dispatched to a window procedure.

The DispatchMessage function sends a message to the window procedure associated with the window handle specified in the MSG structure. If the window handle is HWND_TOPMOST, DispatchMessage sends the message to the window procedures of all top-level windows in the system. If the window handle is NULL, DispatchMessage does nothing with the message.

An application's main thread starts its message loop after initializing the application and creating at least one window. Once started, the message loop continues to retrieve messages from the thread's message queue and to dispatch them to the appropriate windows. The message loop ends when the GetMessage function removes the WM_QUIT message from the message queue.

Only one message loop is needed for a message queue, even if an application contains many windows. DispatchMessage always dispatches the message to the proper window; this is because each message in the queue is an MSG structure that contains the handle of the window to which the message belongs.

An application can modify its message loop in a variety of ways. For example, it can retrieve messages from the queue without dispatching them to a window. This is useful for applications that post messages not specifying a window. (These messages apply to the application rather than to a specific window because they have NULL window handles.) An application can also direct GetMessage to search for specific messages, leaving other messages in the queue. This is useful for applications that must temporarily bypass the usual first-in, first-out order of the message queue.

An application that uses accelerator keys must be able to translate keyboard messages into command messages. To do this, the application's message loop must include a call to the TranslateAccelerator function. For more information about accelerator keys, see Keyboard Accelerators.

+$#A Window Procedure

A window procedure is a function that receives and processes all messages sent to the window. Every window class has a window procedure, and every window created with that class uses that same window procedure to respond to messages.

The system sends a message to a window procedure by passing the message data as arguments to the procedure. The window procedure then performs an appropriate action for the message; it checks the message identifier and, while processing the message, uses the information specified by the message parameters.

A window procedure does not usually ignore a message. If it does not process a message, it must send the message back to the system for default processing. The window procedure does this by calling the DefWindowProc function, which performs a default action and returns a message result. The window procedure must then return this value as its own message result. Most window procedures process just a few types of messages and pass the others on to the system by calling DefWindowProc.

Because a window procedure is shared by all windows belonging to the same class, it can process messages for several different windows. To identify the specific window affected by the message, a window procedure can examine the window handle passed with a message. For more information about window procedures, see Window Procedures.

+$#A Posting and Sending Messages

Any application can post and send messages. Like the system, an application posts a message by copying it to a message queue and sends a message by passing the message data as arguments to a window procedure. To post and send messages, an application uses the PostMessage and SendMessage functions.

An application typically posts a message to notify a specific window to perform a task. PostMessage creates an MSG structure for the message and copies the message to the message queue. The application's message loop eventually retrieves the message and dispatches it to the appropriate window procedure.

An application typically sends a message to notify a window procedure to perform a task immediately. The SendMessage function sends the message to the window procedure corresponding to the given window. The function waits until the window procedure completes processing and then returns the message result. Parent and child windows often communicate by sending messages to each other. For example, a parent window that has an edit control as its child window can set the text of the control by sending a message to it. The control can notify the parent window of changes to the text that are carried out by the user by sending messages back to the parent.

Occasionally, an application may be required to send or post a message to all top-level windows in the system. For example, if the application changes the system time, it must notify all top-level windows about the change by sending a WM_TIMECHANGE message. An application can send or post a message to all top-level windows by calling the SendMessage or PostMessage function and specifying HWND_TOPMOST in the hwnd parameter.

An application can post a message without specifying a window. If the application supplies a NULL window handle when calling PostMessage, the message is posted to the queue associated with the current thread. Because no window handle is specified, the application must process the message in the message loop. This is one way to create a message that applies to the entire application, instead of to a specific window.

By using the InSendMessage function, a window procedure can determine whether it is processing a message sent by another thread. This capability is useful when message processing depends on the origin of the message.

A common programming error is to assume that the PostMessage function always posts a message. This is not true when the message queue is full. An application should check the return value of the PostMessage function to determine whether the message has been posted and, if it has not been, repost it.

+$#A Message Types

This section describes the two types of Windows messages: system defined and application defined.

+$#A System-Defined Messages

The system uses system-defined messages to control the operations of applications and to provide input and other information for applications to process. The system sends or posts a system-defined message when it communicates with an application. An application can also send or post system-defined messages. Applications usually use these messages to control the operation of control windows created by using preregistered window classes.

Each system message has a unique message identifier and a corresponding symbolic constant (defined in the Windows header files) that states the purpose of the message. For example, the WM_PAINT constant requests that a window paint its contents.

Symbolic constants specify the category to which system-defined messages belong. The prefix of the constant identifies the type of window that can interpret and process the message. Following are the prefixes and their related message categories.

Prefix | Message category
BM | Button control
CB | Combo box control
DM | Default push button control
EM | Edit control
LB | List box control
SBM | Scroll bar control
WM | General window

General window messages cover a wide range of information and requests, including messages for mouse and keyboard input, menu and dialog box input, window creation and management, and dynamic data exchange (DDE).

+$#A Application-Defined Messages

An application can create messages to use in its own windows or to communicate with windows in other processes. If an application creates its own messages, the window procedure that receives them must interpret the messages and provide appropriate processing.

Windows reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER − 1) and 0x8000 through 0xBFFF for system-defined messages. Applications cannot use these values for private messages.

Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers defined by an application for its own use. Values in the range 0xC000 through 0xFFFF are available for message identifiers defined by an application for use in communicating with windows in other applications.

Windows returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function to register a message. The message identifier returned by this function is guaranteed to be unique throughout the system. If your application creates messages to communicate with windows in other applications, it should use RegisterWindowMessage to register the message. Use of this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.

+$#A Message Filtering

An application can choose specific messages to retrieve from the message queue (while ignoring other messages) by using the GetMessage or PeekMessage function to specify a message filter. The filter is a range of message identifiers (specified by a first and last identifier), a window handle, or both. GetMessage and PeekMessage use a message filter to select which messages to retrieve from the queue. Message filtering is useful if an application must search the message queue for messages that have arrived later in the queue.

Any application that filters messages must ensure that a message satisfying the message filter can be posted. For example, if an application filters for a WM_CHAR message in a window that does not receive keyboard input, the GetMessage function does not return. This effectively "hangs" the application.

To filter for keyboard, mouse, and DDE messages, an application can use the WM_KEYFIRST and WM_KEYLAST, WM_MOUSEFIRST and WM_MOUSELAST messages, and WM_DDE_FIRST and WM_DDE_LAST constants.

+$#A Message Deadlocks

A thread that calls the SendMessage function to send a message to another thread cannot continue executing until the window procedure that receives the message returns. If the receiving thread yields control while processing the message, the sending thread cannot continue executing, because it is waiting for SendMessage to return. This situation is called a deadlock. The receiving thread need not yield control explicitly; calling any of the following functions can cause a thread to yield control.

DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
GetMessage
MessageBox
PeekMessage

A window procedure can determine whether a message it has received was sent by another thread by calling the InSendMessage function. Before calling any of the functions in the preceding list while processing a message, the window procedure should first call InSendMessage. If this function returns TRUE, the window procedure must call the ReplyMessage function before any function that causes the thread to yield control.

+$#A Using Messages and Message Queues

• Creating a message loop

• Examining a message queue

• Posting a message

• Sending a message

+$#A Creating a Message Loop

Windows automatically creates a message queue for each thread. If the thread creates one or more windows, a message loop must be provided; this message loop retrieves messages from the thread's message queue and dispatches them to the appropriate window procedures.

Because Windows directs messages to individual windows in an application, a thread must create at least one window before starting its message loop. Most Windows-based applications contain a single thread that creates windows. A typical application registers the window class for its main window, creates and shows the main window, and then starts its message loop ⎯ all in the WinMain function.

You create a message loop by using the GetMessage and DispatchMessage functions. If your application must obtain character input from the user, include the TranslateMessage function in the loop. TranslateMessage translates virtual-key messages into character messages. The following example shows the message loop in the WinMain function of a simple Windows-based application.

HINSTANCE hinst;

HWND hwndMain;

int APIENTRY WinMain(hInstance, hPrevInstance, lpszCmdLine,

nCmdShow)

HINSTANCE hInstance;

HINSTANCE hPrevInstance;

LPSTR lpszCmdLine;

int nCmdShow;

{

MSG msg;

WNDCLASS wc;

UNREFERENCED_PARAMETER(lpszCmdLine);

/* Register the window class for the main window. */

if (!hPrevInstance) {

wc.style = 0;

wc.lpfnWndProc = (WNDPROC) WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon((HINSTANCE) NULL,

IDI_APPLICATION);

wc.hCursor = LoadCursor((HINSTANCE) NULL,

IDC_ARROW);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MainMenu";

wc.lpszClassName = "MainWndClass";

if (!RegisterClass(&wc))

return FALSE;

}

hinst = hInstance; /* save instance handle */

/* Create the main window. */

hwndMain = CreateWindow("MainWndClass", "Sample",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,

(HMENU) NULL, hinst, (LPVOID) NULL);

/*

* If the main window cannot be created, terminate

* the application.

*/

if (!hwndMain)

return FALSE;

/* Show the window and paint its contents. */

ShowWindow(hwndMain, nCmdShow);

UpdateWindow(hwndMain);

/* Start the message loop. */

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

/* Return the exit code to Windows. */

return msg.wParam;

}

The GetMessage, TranslateMessage, and DispatchMessage functions take a pointer to an MSG structure as a parameter. If a message is available, GetMessage copies it to the MSG structure. If the message is a virtual-key message (such as WM_KEYDOWN or WM_SYSKEYDOWN), TranslateMessage generates a character message (WM_CHAR or WM_SYSCHAR) and places it in the message queue. DispatchMessage uses the members of the MSG structure as arguments for the window procedure; DispatchMessage does not return until the window procedure finishes processing the message.

If a thread supports accelerators, its message loop must include the TranslateAccelerator function. This function checks for key combinations that match an entry in the thread's accelerator table. When it finds a match, TranslateAccelerator translates the key combination into a WM_COMMAND message and dispatches it to the window procedure. For more information about accelerators, see Keyboard Accelerators.

If a thread uses a modeless dialog box, the message loop must include the IsDialogMessage function so that the dialog box can receive keyboard input. For more information about dialog boxes, see Dialog Boxes.

The following example shows a message loop for a thread that uses accelerators and displays a modeless dialog box. When TranslateAccelerator or IsDialogMessage returns TRUE (indicating that the message has been processed), TranslateMessage and DispatchMessage are not called. The reason for this is that TranslateAccelerator and IsDialogMessage perform all necessary translating and dispatching of messages.

HWND hwndMain;

HWND hwndDlgModeless = NULL;

MSG msg;

HACCEL haccel;

.

. /* Perform initialization and create a main window. */

.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

if (hwndDlgModeless == (HWND) NULL ||

!IsDialogMessage(hwndDlgModeless, &msg) &&

!TranslateAccelerator(hwndMain, haccel,

&msg)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

+$#A Examining a Message Queue

Occasionally, an application needs to examine the contents of a thread's message queue from outside the thread's message loop. For example, if an application's window procedure performs a lengthy drawing operation, you may want the user to be able to interrupt the operation. Unless your application periodically examines the message queue during the operation for mouse and keyboard messages, it will not respond to user input until after the operation has completed. The reason for this is that the DispatchMessage function in the thread's message loop does not return until the window procedure finishes processing a message.

You can use the PeekMessage function to examine a message queue during a lengthy operation. PeekMessage is similar to the GetMessage function; both check a message queue for a message that matches the filter criteria and then copy the message to an MSG structure. The main difference between the two functions is that GetMessage does not return until a message matching the filter criteria is placed in the queue, whereas PeekMessage returns immediately regardless of whether a message is in the queue.

The following example shows how to use PeekMessage to examine a message queue for mouse clicks and keyboard input during a lengthy operation.

HWND hwnd;

BOOL fDone;

MSG msg;

/*

* Begin the operation and continue until it is complete

* or until the user clicks the mouse or presses a key.

*/

fDone = FALSE;

while (!fDone) {

fDone = DoLengthyOperation(); /* application-defined */

/*

* Remove any messages that may be in the queue. If the

* queue contains any mouse or keyboard

* messages, end the operation.

*/

while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {

switch(msg.message) {

case WM_LBUTTONDOWN:

case WM_RBUTTONDOWN:

case WM_KEYDOWN:

.

. /* Perform any required cleanup. */

.

fDone = TRUE;

}

}

}

Other functions, including GetQueueStatus and GetInputState, also allow you to examine the contents of a thread's message queue. GetQueueStatus returns an array of flags that indicates the types of messages in the queue; using it is the fastest way to discover whether the queue contains any messages. GetInputState returns TRUE if the queue contains mouse or keyboard messages. Both of these functions can be used to determine whether the queue contains messages that need to be processed.

+$#A Posting a Message

You can post a message to a message queue by using the PostMessage function. PostMessage places a message at the end of a thread's message queue and returns immediately, without waiting for the thread to process the message. The function's parameters include a window handle, a message identifier, and two message parameters. Windows copies these parameters to an MSG structure, fills the time and pt members of the structure, and places the structure in the message queue.

Windows uses the window handle passed with the PostMessage function to determine which thread message queue should receive the message. If the handle is HWND_TOPMOST, Windows posts the message to the thread message queues of all top-level windows.

You can use the PostThreadMessage function to post a message to a specific thread message queue. PostThreadMessage is similar to PostMessage, except the first parameter is a thread identifier rather than a window handle. You can retrieve the thread identifier by calling the GetCurrentThreadId function.

Use the PostQuitMessage function to exit a message loop. PostQuitMessage posts the WM_QUIT message to the currently executing thread. The thread's message loop terminates and returns control to Windows when it encounters the WM_QUIT message. An application usually calls PostQuitMessage in response to the WM_DESTROY message, as shown in the following example.

case WM_DESTROY:

.

. /* Perform cleanup tasks. */

.

PostQuitMessage(0);

break;

+$#A Sending a Message

The SendMessage function is used to send a message directly to a window procedure. SendMessage calls a window procedure and waits for that procedure to process the message and return a result.

A message can be sent to any window in the system; all that is required is a window handle. Windows uses the handle to determine which window procedure should receive the message.

If a window procedure yields control when processing a message sent by another thread, a message deadlock may result. (For more information about message deadlocks, see Message Deadlocks.) Before processing a message that may have been sent from another thread, a window procedure should first call the InSendMessage function. If this function returns TRUE, the window procedure should call ReplyMessage before any function that causes the thread to yield control, as shown in the following example.

case WM_USER + 5:

if (InSendMessage())

ReplyMessage(TRUE);

DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);

break;

A number of messages can be sent to controls in a dialog box. These control messages set the appearance, behavior, and content of controls or retrieve information about controls. For example, the CB_ADDSTRING message can add a string to a combo box, and the BM_SETCHECK message can set the check state of a check box or radio button.

Use the SendDlgItemMessage function to send a message to a control, specifying the identifier of the control and the handle of the dialog box window that contains the control. The following example, taken from a dialog box procedure, copies a string from a combo box's edit control into its list box. The example uses SendDlgItemMessage to send a CB_ADDSTRING message to the combo box.

HWND hwndCombo;

int cTxtLen;

PSTR pszMem;

switch (uMsg) {

case WM_COMMAND:

switch (LOWORD(wParam)) {

case IDD_ADDCBITEM:

/*

* Get the handle of the combo box and the

* length of the string in the edit control

* of the combo box.

*/

hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);

cTxtLen = GetWindowTextLength(hwndCombo);

/*

* Allocate memory for the string and copy

* the string into the memory.

*/

pszMem = (PSTR) VirtualAlloc((LPVOID) NULL,

(DWORD) (cTxtLen + 1), MEM_COMMIT,

PAGE_READWRITE);

GetWindowText(hwndCombo, pszMem,

cTxtLen + 1);

/*

* Add the string to the list box of the

* combo box and remove the string from the

* edit control of the combo box.

*/

if (*pszMem != NULL) {

SendDlgItemMessage(hwndDlg, IDD_COMBO,

CB_ADDSTRING, 0,

(DWORD) ((LPSTR) pszMem));

SetWindowText(hwndCombo, (LPSTR) NULL);

}

/* Free the memory and return. */

VirtualFree(pszMem, 0, MEM_RELEASE);

return TRUE;

.

. /* Process other dialog box commands. */

.

}

.

. /* Process other dialog box messages. */

.

}

+$#A Message and Message Queue Functions

Following are the functions used with messages and message queues.

BroadcastSystemMessage
DefWindowProc
DispatchMessage
GetInputState
GetMessage
GetMessageExtraInfo
GetMessagePos
GetMessageTime
GetQueueStatus
InSendMessage
PeekMessage
PostAppMessage
PostMessage
PostQuitMessage
PostThreadMessage
RegisterWindowMessage
ReplyMessage
SendAsyncProc
SendMessage
SendMessageCallback
SendMessageTimeout
SendNotifyMessage
SetMessageExtraInfo
TranslateMessage
WaitMessage

Obsolete or Deleted Functions

SetMessageQueue

Créé avec HelpNDoc Personal Edition: Générateur d'aide complet


Metafiles.rtf

+$#AK Metafiles

Applications written for the Microsoft® Windows® operating system use two graphics tools to store pictures: metafiles and bitmaps. For information about bitmaps, see Bitmaps. This overview describes metafiles.

+$#A About Metafiles

A metafile is a collection of structures that store a picture in a device-independent format. Device independence is the one feature that sets metafiles apart from bitmaps: unlike a bitmap, a metafile guarantees device independence. For example, when an application creates a picture measuring 2 by 2 inches on a VGA display and stores that picture in a metafile, the picture maintains its original dimensions when printed on a 300 dpi laser printer or copied over a network and displayed in another application that is running on an 8514/A video display. There is a drawback to metafiles, however; they are generally drawn more slowly than bitmaps. Therefore, if an application requires fast drawing and device independence is not an issue, it should use bitmaps instead of metafiles.

Internally, a metafile is an array of variable-length structures called metafile records. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw the picture. These records are stored in the metafile after a special metafile device context (DC) is created. This DC is then used for all drawing operations required to create the picture. When Windows processes a GDI function associated with a metafile DC, it converts the function into the appropriate data and stores this data in a record appended to the metafile.

After a picture is complete and the last record is stored in the metafile, the metafile can be passed to another application by means of the clipboard, embedded within another file, stored on disk, or played repeatedly. A metafile is played when its records are converted to device commands and processed by the appropriate device.

There are two types of metafiles: enhanced and Windows. An enhanced metafile is used in applications written using the Microsoft® Win32® application programming interface (API). The enhanced format consists of a header, a table of handles to GDI objects, a private palette, and an array of metafile records. Enhanced metafiles provide true device independence. (You can think of the picture stored in an enhanced metafile as a "snapshot" of the video display taken at a particular moment. This "snapshot" maintains its dimensions no matter where it appears: on a printer, a plotter, or the desktop, or in the client area of any Win32-based application.)

A Windows metafile is used for applications written using the Windows version 3.x API. The Windows format consists of a header and an array of metafile records. Windows-format metafiles are limited in their capabilities and should rarely be used ⎯ the Windows-metafile API is supported to maintain backward compatibility with applications that were written to run with Windows version 3.x.

+$#A Enhanced-Format Metafiles

A developer can use enhanced metafiles to store a picture created by using the Win32 GDI functions (including new path and transformation functions). Because the enhanced metafile format is standardized for the Win32 API, pictures that are stored in this format can be copied from one Win32 application to another; and, because the pictures are truly device independent, they are guaranteed to maintain their shape and proportion on any output device.

+$#A Enhanced Metafiles

An enhanced metafile is an array of records. A metafile record is a variable-length ENHMETARECORD structure. This structure identifies the record type, specifies the record length, and contains additional data that is dependent on the record type.

The first record in an enhanced metafile is always the enhanced-metafile header. The header specifies the following information.

• Size of the metafile, in bytes

• Dimensions of the picture frame, in device units

• Dimensions of the picture frame, in .01-millimeter units

• Number of records in the metafile

• Offset to an optional text description

• Size of the optional palette

• Resolution of the original device, in pixels

• Resolution of the original device, in millimeters

An optional text description can follow the header record. The text description describes the picture and the author's name. The optional palette specifies the colors used to create the enhanced metafile. The remaining records identify the GDI functions used to create the picture. The following hexadecimal output corresponds to a record generated for a call to the SetMapMode function.

00000011 0000000C 00000004

The value 0x00000011 specifies the record type (corresponds to the EMR_SETMAPMODE constant defined in the file WINGDI.H). The value 0x0000000C specifies the length of the record, in bytes. The value 0x00000004 identifies the mapping mode (corresponds to the MM_LOENGLISH constant defined in the file WINGDI.H).

+$#A Enhanced Metafile Operations

A developer creates an enhanced metafile by using the CreateEnhMetaFile function and supplying the appropriate arguments. The Win32 API uses these arguments to maintain picture dimensions, to determine whether the metafile should be stored on a disk or in memory, and so on.

To maintain picture dimensions across output devices, the Win32 API requires the resolution of the reference device. This reference device is the device on which the picture first appeared, and the reference DC is the DC associated with the reference device. When calling the CreateEnhMetaFile function, the application must supply a handle that identifies this DC. The application can get this handle by calling the GetDC or CreateDC function or specify NULL as the handle to use the current display device for the reference device.

Most applications store pictures permanently and therefore create an enhanced metafile that is stored on a disk; however, there are some instances when this is not necessary. For example, a word-processing application that provides chart-drawing capabilities could store a user-defined chart in memory as an enhanced metafile and then copy the enhanced metafile bits from memory into the user's document file. An application that requires a metafile that is stored permanently on a disk must supply the filename when it calls the CreateEnhMetaFile function. If the developer does not supply a filename, Windows automatically treats the metafile as a temporary file and stores it in memory.

The developer can add an optional text description to a metafile containing information about the picture and the author. An application can display these strings in the File Open dialog box to provide the user with information about metafile content that will help in selecting the appropriate file. If an application includes the text description, the application must supply a pointer to the string when it calls the CreateEnhMetaFile function.

When the CreateEnhMetaFile function is successful, it returns a handle that identifies a special metafile DC. A metafile DC is unique in that it is associated with a file rather than with an output device. When Windows processes a GDI function that received a handle to a metafile DC, it converts the GDI function into an enhanced-metafile record and appends the record to the end of the enhanced metafile.

After a picture is complete and the last record is appended to the enhanced metafile, the application can close the file by calling the CloseEnhMetaFile function. This function closes and deletes the special metafile DC and returns a handle identifying the enhanced metafile. The application can use this handle to accomplish the following tasks.

• Display the picture stored in the enhanced metafile.

• Create copies of the enhanced metafile.

• Enumerate, edit, or copy the individual records in the enhanced metafile.

• Retrieve the optional description that is stored in the enhanced metafile.

• Retrieve a copy of the enhanced-metafile header.

• Retrieve a binary version of the enhanced metafile.

• Enumerate the colors in the optional palette.

• Convert an enhanced-format metafile into a Windows-format metafile.

Some applications create temporary backup (or duplicate) copies of a file before enabling the user to alter the original. An application can create a backup copy of an enhanced metafile by calling the CopyEnhMetaFile function, supplying a handle that identifies the enhanced metafile, and supplying a pointer to the name of the new file.

Most drawing, illustration, and computer-aided design (CAD) applications require a means of editing a picture stored in an enhanced metafile. Although editing an enhanced metafile is a complex task, a developer can use the EnumEnhMetaFile function in combination with other Win32 functions to provide this capability in your application. The EnumEnhMetaFile function and its associated callback function enable the application to process individual records in an enhanced metafile.

Some applications display the text description of an enhanced metafile with the corresponding filename in the File Open dialog box. An application can determine whether this string exists in an enhanced metafile by retrieving the metafile header and examining one of its members. The GetEnhMetaFileHeader function can retrieve the metafile header. If the string exists, the application retrieves it by calling the GetEnhMetaFileDescription function.

Some applications retrieve the contents of a metafile by calling the GetEnhMetaFileBits function; however, before retrieving the contents, the application must specify the size of the file. To get the size, the application can use the GetEnhMetaFileHeader function and examine the appropriate member.

To achieve consistent colors when a picture is displayed on various output devices, an application can call the CreatePalette function and store a logical palette in an enhanced metafile. Other applications that display the picture stored in the enhanced metafile retrieve this palette and call the RealizePalette function before displaying the picture. To determine whether a palette is stored in an enhanced metafile, an application can retrieve the metafile header and examine the appropriate member. If a palette exists, the application can call the GetEnhMetaFilePaletteEntries function to retrieve the logical palette.

+$#A Windows-Format Metafiles

The Win32 API supports the Windows metafile format to maintain compatibility with applications written for Windows version 3.x. Following are the limitations of this format.

• A Windows-format metafile is application and device dependent. Changes in the application's mapping modes or the device resolution affect the appearance of metafiles created in this format.

• A Windows-format metafile does not contain a comprehensive header that describes the original picture dimensions, the resolution of the device on which the picture was created, an optional text description, or an optional palette.

• A Windows-format metafile does not support the new curve, path, and transformation functions.

• Some Windows-format metafile records cannot be scaled.

• The metafile DC associated with a Windows-format metafile cannot be queried (that is, an application cannot retrieve device-resolution data, font metrics, and so on).

To convert a Windows-format metafile to an enhanced-format metafile, an application can call the GetMetaFileBitsEx function to retrieve the data from the Windows-format metafile and then call the SetWinMetaFileBits function to convert this data into an enhanced-format metafile.

A developer writing a Win32-based application should avoid using the Windows-format functions and use the enhanced-format functions instead.

+$#A Using Metafiles

• Creating an enhanced metafile

• Displaying a picture and storing it in an enhanced metafile

• Opening an enhanced metafile and displaying its contents

• Editing an enhanced metafile

+$#A Creating an Enhanced Metafile

This section contains an example that demonstrates the creation of an enhanced metafile that is stored on a disk, using a filename specified by the user.

The example uses a DC for the application window as the reference DC. (Windows stores the resolution data for this device in the enhanced-metafile's header.) The application retrieves a handle identifying this DC by calling the GetDC function.

The example uses the dimensions of the application's client area to define the dimensions of the picture frame. Using the rectangle dimensions returned by the GetClientRect function, the application converts the device units to .01-millimeter units and passes the converted values to the CreateEnhMetaFile function.

The example displays a Save As common dialog box that enables the user to specify the filename of the new enhanced metafile. The system appends the three-character .EMF extension to this filename and passes the name to the CreateEnhMetaFile function.

The example also embeds a text description of the picture in the enhanced-metafile header. This description is specified as a resource in the string table of the application's resource file. However, in a working application, this string would be retrieved from a custom control in a common dialog box or from a separate dialog box displayed solely for this purpose.

/* Obtain a handle to a reference DC. */

hdcRef = GetDC(hWnd);

/*

* Determine the picture frame dimensions.

* iWidthMM is the display width in millimeters.

* iHeightMM is the display height in millimeters.

* iWidthPels is the display width in pixels.

* iHeightPels is the display height in pixels

*/

iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);

iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);

iWidthPels = GetDeviceCaps(hdcRef, HORZRES);

iHeightPels = GetDeviceCaps(hdcRef, VERTRES);

/*

* Retrieve the coordinates of the client

* rectangle, in pixels.

*/

GetClientRect(hWnd, &rect);

/*

* Convert client coordinates to .01-mm units.

* Use iWidthMM, iWidthPels, iHeightMM, and

* iHeightPels to determine the number of

* .01-millimeter units per pixel in the x-

* and y-directions.

*/

rect.left = (rect.left * iWidthMM * 100)/iWidthPels;

rect.top = (rect.top * iHeightMM * 100)/iHeightPels;

rect.right = (rect.right * iiWidthMM * 100)/iWidthPels;

rect.bottom = (rect.bottom * iHeightMM * 100)/iHeightPels;

/* Load the filename filter from the string table. */

LoadString(hInst, IDS_FILTERSTRING,

(LPSTR)szFilter, sizeof(szFilter));

/*

* Replace the '%' separators that are embedded

* between the strings in the string-table entry

* with '\0'.

*/

for (i=0; szFilter[i]!='\0'; i++)

if (szFilter[i] == '%')

szFilter[i] = '\0';

/* Load the dialog title string from the table. */

LoadString(hInst, IDS_TITLESTRING,

(LPSTR)szTitle, sizeof(szTitle));

/* Initialize the OPENFILENAME members. */

szFile[0] = '\0';

Ofn.lStructSize = sizeof(OPENFILENAME);

Ofn.hwndOwner = hWnd;

Ofn.lpstrFilter = szFilter;

Ofn.lpstrFile= szFile;

Ofn.nMaxFile = sizeof(szFile);

Ofn.lpstrFileTitle = szFileTitle;

Ofn.nMaxFileTitle = sizeof(szFileTitle);

Ofn.lpstrInitialDir = (LPSTR)NULL;

Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;

Ofn.lpstrTitle = szTitle;

/*

* Display the Filename common dialog box. The

* filename specified by the user is passed

* to the CreateEnhMetaFile function and used to

* store the metafile on disk.

*/

GetSaveFileName(&Ofn);

/* Load the description from the string table. */

LoadString(hInst, IDS_DESCRIPTIONSTRING,

(LPSTR)szDescription, sizeof(szDescription));

/*

* Replace the '%' string separators that are

* embedded between strings in the string-table

* entry with '\0'.

*/

for (i=0; szDescription[i]!='\0'; i++)

if (szDescription[i] == '%')

szDescription[i] = '\0';

/* Create the metafile DC. */

hdcMeta = CreateEnhMetaFile(hdcRef,

(LPTSTR) Ofn.lpstrFile,

&rect, (LPSTR)szDescription);

if (!hdcMeta)

errhandler("CreateEnhMetaFile", hWnd);

/* Release the reference DC. */

ReleaseDC(hWnd, hdcRef);

+$#A Displaying a Picture and Storing It in an Enhanced Metafile

This section contains an example demonstrating the creation of a picture and the process of storing the corresponding records in a metafile. The example draws a picture to the display or stores it in a metafile. If a display DC handle is given, it draws a picture to the screen using various GDI functions. If an enhanced metafile DC is given, it stores the same picture in the enhanced metafile.

void DrawOrStore(HWND hwnd, HDC hdcMeta, HDC hdcDisplay)

{

RECT rect;

HDC hDC;

int fnMapModeOld;

HBRUSH hbrOld;

/* Draw it to the display DC or store it in the metafile DC. */

if (hdcMeta)

hDC = hdcMeta;

else

hDC = hdcDisplay;

/* Set the mapping mode in the DC. */

fnMapModeOld = SetMapMode(hDC, MM_LOENGLISH);

/* Find the midpoint of the client area. */

GetClientRect(hwnd, (LPRECT)&rect);

DPtoLP(hDC, (LPPOINT)&rect, 2);

/* Select a gray brush. */

hbrOld = SelectObject(hDC, GetStockObject(GRAY_BRUSH));

/* Draw a circle with a one inch raduis. */

Ellipse(hDC, (rect.right/2 - 100), (rect.bottom/2 + 100),

(rect.right/2 + 100), (rect.bottom/2 - 100));

/* Perform additional drawing here. */

/* Set the device context back to its original state. */

SetMapMode(hDC, fnMapModeOld);

SelectObject(hDC, hbrOld);

}

+$#A Opening an Enhanced Metafile and Displaying Its Contents

This section contains an example demonstrating how an application opens an enhanced metafile stored on disk and displays the associated picture in the client area.

The example uses the Open common dialog box to enable the user to select an enhanced metafile from a list of existing files. It then passes the name of the selected file to the GetEnhMetaFile function, which returns a handle identifying the file. This handle is passed to the PlayEnhMetaFile function in order to display the picture.

LoadString(hInst, IDS_FILTERSTRING,

(LPSTR)szFilter, sizeof(szFilter));

/*

* Replace occurrences of '%' string separator

* with '\0'.

*/

for (i=0; szFilter[i]!='\0'; i++)

if (szFilter[i] == '%')

szFilter[i] = '\0';

LoadString(hInst, IDS_DEFEXTSTRING,

(LPSTR)szDefExt, sizeof(szFilter));

/*

* Use the OpenFilename common dialog box

* to obtain the desired filename.

*/

szFile[0] = '\0';

Ofn.lStructSize = sizeof(OPENFILENAME);

Ofn.hwndOwner = hWnd;

Ofn.lpstrFilter = szFilter;

Ofn.lpstrCustomFilter = (LPSTR)NULL;

Ofn.nMaxCustFilter = 0L;

Ofn.nFilterIndex = 1L;

Ofn.lpstrFile = szFile;

Ofn.nMaxFile = sizeof(szFile);

Ofn.lpstrFileTitle = szFileTitle;

Ofn.nMaxFileTitle = sizeof(szFileTitle);

Ofn.lpstrInitialDir = (LPSTR) NULL;

Ofn.lpstrTitle = (LPSTR)NULL;

Ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

Ofn.nFileOffset = 0;

Ofn.nFileExtension = 0;

Ofn.lpstrDefExt = szDefExt;

GetOpenFileName(&Ofn);

/* Open the metafile. */

hemf = GetEnhMetaFile(Ofn.lpstrFile);

/* Retrieve a handle to a window DC. */

hDC = GetDC(hWnd);

/* Retrieve the client rectangle dimensions. */

GetClientRect(hWnd, &rct);

/* Draw the picture. */

PlayEnhMetaFile(hDC, hemf, &rct);

/* Release the metafile handle. */

DeleteEnhMetaFile(hemf);

/* Release the window DC. */

ReleaseDC(hWnd, hDC);

+$#A Editing an Enhanced Metafile

To edit a picture stored in an enhanced metafile, an application must perform the following tasks.

1. Use hit-testing to capture the cursor coordinates and retrieve the position of the object (line, arc, rectangle, ellipse, polygon, or irregular shape) that the user wants to alter.

2. Convert these coordinates to logical (or world) units.

3. Call the EnumEnhMetaFile function and examine each metafile record.

4. Determine whether a given record corresponds to a GDI drawing function.

5. If it does, determine whether the coordinates stored in the record correspond to the line, arc, ellipse, or other graphics element that intersects the coordinates specified by the user.

6. Upon finding the record that corresponds to the output that the user wants to alter, erase the object on the screen that corresponds to the original record.

7. Delete the corresponding record from the metafile, saving a pointer to its location.

8. Permit the user to redraw or replace the object.

9. Convert the GDI functions used to draw the new object into one or more enhanced-metafile records.

10. Store these records in the enhanced metafile.

+$#A Metafile Functions

Following are the functions used with enhanced format metafiles.

CloseEnhMetaFile
CopyEnhMetaFile
CreateEnhMetaFile
DeleteEnhMetaFile
EnhMetaFileProc
EnumEnhMetaFile
GdiComment
GetEnhMetaFile
GetEnhMetaFileBits
GetEnhMetaFileDescription
GetEnhMetaFileHeader
GetEnhMetaFilePaletteEntries
GetWinMetaFileBits
PlayEnhMetaFile
PlayEnhMetaFileRecord
SetEnhMetaFileBits
SetWinMetaFileBits

The following functions are provided for compatibility with Windows-format metafiles:

CloseMetaFile
CopyMetaFile
CreateMetaFile
DeleteMetaFile
EnumMetaFile
EnumMetaFileProc
GetMetaFile
GetMetaFileBitsEx
PlayMetaFile
PlayMetaFileRecord
SetMetaFileBitsEx

Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle


Mouse Input.rtf

+$#AK Mouse Input

This overview describes how Windows generates mouse input and explains how an application receives and processes that input.

+$#A About Mouse Input

The mouse is an important, but optional, user-input device for applications written for Microsoft® Windows®. A well-written Windows-based application should include a mouse interface, but it should not depend on the mouse as the sole means of acquiring user input. The application should provide full keyboard support as well.

A Windows-based application receives mouse input in the form of messages that are sent or posted to its windows.

+$#A Mouse Cursor

When the user moves the mouse, the system moves a bitmap on the screen called the mouse cursor. The mouse cursor contains a single-pixel point called the hot spot, a point that the system tracks and recognizes as the position of the cursor. When a mouse event occurs, the window that contains the hot spot typically receives the mouse message resulting from the event. The window need not be active or have the keyboard focus to receive a mouse message.

The system maintains a variable that controls the mouse speed ⎯ that is, the distance the cursor moves when the user moves the mouse. You can use the SystemParametersInfo function with the SPI_GETMOUSE or SPI_SETMOUSE flag to retrieve or set the mouse speed. For more information about mouse cursors, see Cursors.

+$#A Mouse Capture

The system typically posts a mouse message to the window that contains the cursor hot spot at the time a mouse event occurs. An application can change this behavior by using the SetCapture function to route mouse messages to a specific window. The window receives all mouse messages until the application calls the ReleaseCapture function or specifies another capture window, or until the user clicks a window created by another thread.

Whenever the mouse capture changes, Windows sends a WM_CAPTURECHANGED message to the window that is losing the mouse capture. The lParam parameter of the message specifies the handle of the window that is gaining the mouse capture.

Only the foreground window can capture mouse input. When a background window attempts to capture mouse input, it receives messages only for mouse events that occur when the cursor hot spot is within the visible portion of the window.

Capturing mouse input is useful if a window must receive all mouse input, even when the cursor moves outside the window. For example, an application typically tracks the cursor position after a mouse "button down" event, following the cursor until a "button up" event occurs. If an application has not captured mouse input and the user releases the mouse button outside the window, the window does not receive the button up message.

A thread can use the GetCapture function to determine whether one of its windows has captured the mouse. If one of the thread's windows has captured the mouse, GetCapture retrieves the handle of the window.

+$#A Mouse Configuration

Although the mouse is an important input device for Windows-based applications, not every user necessarily has a mouse. An application can determine whether the system includes a mouse by passing the SM_MOUSEPRESENT value to the GetSystemMetrics function.

Windows supports a mouse having up to three buttons. On a three-button mouse, the buttons are designated as the left, middle, and right buttons. Windows messages and named constants related to the mouse buttons use the letters L, M, and R to identify the buttons. The button on a single-button mouse is considered to be the left button. Although Windows supports a mouse with multiple buttons, most applications use the left button primarily and use the others little, if at all.

An application can determine the number of buttons on the mouse by passing the SM_CMOUSEBUTTONS value to the GetSystemMetrics function. To configure the mouse for a left-handed user, the application can use the SwapMouseButton function to reverse the meaning of the left and right mouse buttons. Passing the SPI_SETMOUSEBUTTONSWAP value to the SystemParametersInfo function is another way to reverse the meaning of the buttons. Note, however, that the mouse is a shared resource, so reversing the meaning of the buttons affects all applications.

+$#A Mouse Messages

The mouse generates an input event whenever the user moves the mouse, or presses or releases a mouse button. Windows converts mouse input events into messages and posts them to the appropriate thread's message queue. When mouse messages are posted faster than a thread can process them, Windows discards all but the most recent mouse message.

A window receives a mouse message when a mouse event occurs while the cursor is within the borders of the window, or when the window has captured the mouse. Mouse messages are divided into two groups: client area messages and nonclient area messages. Typically, an application processes client area messages and ignores nonclient area messages.

+$#A Client Area Mouse Messages

A window receives a client area mouse message when a mouse event occurs within the window's client area. The system posts the WM_MOUSEMOVE message to the window when the user moves the cursor within the client area. It posts one of the following messages when the user presses or releases a mouse button while the cursor is within the client area.

Message | Meaning
WM_LBUTTONDBLCLK | The left mouse button was double-clicked.
WM_LBUTTONDOWN | The left mouse button was pressed.
WM_LBUTTONUP | The left mouse button was released.
WM_MBUTTONDBLCLK | The middle mouse button was double-clicked.
WM_MBUTTONDOWN | The middle mouse button was pressed.
WM_MBUTTONUP | The middle mouse button was released.
WM_RBUTTONDBLCLK | The right mouse button was double-clicked.
WM_RBUTTONDOWN | The right mouse button was pressed.
WM_RBUTTONUP | The right mouse button was released.

Message Parameters

The lParam parameter of a client area mouse message indicates the position of the cursor hot spot. The low-order word indicates the x-coordinate of the hot spot, and the high-order word indicates the y-coordinate. The coordinates are given in client coordinates. In the client coordinate system, all points on the screen are given relative to the coordinates (0,0) of the upper left corner of the client area.

The wParam parameter contains flags that indicate the status of the other mouse buttons and the CTRL and SHIFT keys at the time of the mouse event. You can check for these flags when mouse-message processing depends on the state of another mouse button or of the CTRL or SHIFT key. The lParam parameter can be a combination of the following values.

Value | Meaning
MK_CONTROL | The CTRL key is down.
MK_LBUTTON | The left mouse button is down.
MK_MBUTTON | The middle mouse button is down.
MK_RBUTTON | The right mouse button is down.
MK_SHIFT | The SHIFT key is down.

Double-Click Messages

The system generates a double-click message when the user clicks a mouse button twice in quick succession. When the user clicks a button, the system establishes a rectangle centered around the cursor hot spot. It also marks the time at which the click occurred. When the user clicks the same button a second time, the system determines whether the hot spot is still within the rectangle and calculates the time elapsed since the first click. If the hot spot is still within the rectangle and the elapsed time does not exceeded the double-click time-out value, the system generates a double-click message.

An application can get and set double-click time-out values by using the GetDoubleClickTime and SetDoubleClickTime functions, respectively. Alternatively, the application can set the double-click time-out value by using the SPI_SETDOUBLECLICKTIME flag with the SystemParametersInfo function. It can also set the size of the rectangle that Windows uses to detect double-clicks by passing the SPI_SETDOUBLECLKWIDTH and SPI_SETDOUBLECLKHEIGHT flags to SystemParametersInfo. Note, however, that setting the double-click time-out value and rectangle affects all applications.

An application-defined window does not, by default, receive double-click messages. Because of the system overhead involved in generating double-click messages, these messages are generated only for windows belonging to classes that have the CS_DBLCLKS class style. Your application must set this style when registering the window class. For more information about window classes, see Window Classes.

A double-click message is always the third message in a four-message series. The first two messages are the button down and button up messages generated by the first click. The second click generates the double-click message followed by another button up message. For example, double-clicking the left mouse button generates the following message sequence:

WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP

Because a window always receives a button down message before receiving a double-click message, an application typically uses a double-click message to extend a task it began during a button down message. For example, when the user clicks a color in the color palette of Windows Paintbrush™, Paintbrush displays the selected color next to the palette. When the user double-clicks a color, Paintbrush displays the color and opens the Edit Colors dialog box.

+$#A Nonclient Area Mouse Messages

A window receives a nonclient area mouse message when a mouse event occurs in any part of a window except the client area. A window's nonclient area consists of its border, menu bar, title bar, scroll bar, System menu (also called the Control menu), Minimize button, and Maximize button.

Windows generates nonclient area messages primarily for its own use. For example, Windows uses nonclient area messages to change the cursor to a two-headed arrow when the cursor hot spot moves into a window's border. A window must pass nonclient area mouse messages to the DefWindowProc function to take advantage of the built-in mouse interface found in Windows.

There is a corresponding nonclient area mouse message for each client area mouse message. The names of these messages are similar except that the named constants for the nonclient area messages include the letters "NC". For example, moving the cursor in the nonclient area generates a WM_NCMOUSEMOVE message, and pressing the left mouse button while the cursor is in the nonclient area generates a WM_NCLBUTTONDOWN message.

The lParam parameter of a nonclient area mouse message is a POINTS structure that contains the x- and y-coordinates of the cursor hot spot. Unlike coordinates of client area mouse messages, the coordinates are given in screen coordinates rather than client coordinates. In the screen coordinate system, all points on the screen are relative to the coordinates (0,0) of the upper left corner of the screen.

The wParam parameter contains a hit-test value, a value that indicates where in the nonclient area the mouse event occurred. The following section explains the purpose of hit-test values.

+$#A The WM_NCHITTEST Message

Whenever a mouse event occurs, the system sends a WM_NCHITTEST message to either the window that contains the cursor hot spot or the window that has captured the mouse. Windows uses this message to determine whether to send a client area or nonclient area mouse message. An application that must receive mouse movement and mouse button messages must pass the WM_NCHITTEST message to the DefWindowProc function.

The lParam parameter of the WM_NCHITTEST message contains the screen coordinates of the cursor hot spot. The DefWindowProc function examines the coordinates and returns a hit-test value that identifies the location of the hot spot. The hit-test value can be one of the following values.

Value | Location of hot spot
HTBORDER | In the border of a window that does not have a sizing border
HTBOTTOM | In the lower horizontal border of a window
HTBOTTOMLEFT | In the lower left corner of a window border
HTBOTTOMRIGHT | In the lower right corner of a window border
HTCAPTION | In a title bar
HTCLIENT | In a client area
HTCLOSE | In close button.
HTERROR | On the screen background or on a dividing line between windows (same as HTNOWHERE, except that the DefWindowProc function produces a system beep to indicate an error)
HTGROWBOX | In a size box (same as HTSIZE)
HTHELP | In Help button.
HTHSCROLL | In a horizontal scroll bar
HTLEFT | In the left border of a window
HTMENU | In a menu
HTMAXBUTTON | In Maximize button.
HTMINBUTTON | In Minimize button.
HTNOWHERE | On the screen background or on a dividing line between windows
HTREDUCE | In a Minimize button
HTRIGHT | In the right border of a window
HTSIZE | In a size box (same as HTGROWBOX)
HTSYSMENU | In a System menu or in a Close button in a child window
HTTOP | In the upper horizontal border of a window
HTTOPLEFT | In the upper left corner of a window border
HTTOPRIGHT | In the upper right corner of a window border
HTTRANSPARENT | In a window currently covered by another window
HTVSCROLL | In the vertical scroll bar
HTZOOM | In a Maximize button

If the cursor is in the client area of a window, DefWindowProc returns the HTCLIENT hit-test value to the window procedure. When the window procedure returns this code to the system, Windows converts the screen coordinates of the cursor hot spot to client coordinates, and then posts the appropriate client area mouse message.

The DefWindowProc function returns one of the other hit-test values when the cursor hot spot is in a window's nonclient area. When the window procedure returns one of these hit-test values, Windows posts a nonclient area mouse message, placing the hit-test value in the message's wParam parameter and the cursor coordinates in the lParam parameter.

+$#A Window Activation

When the user clicks an inactive top-level window or the child window of a inactive top-level window, Windows sends the WM_MOUSEACTIVATE message (among others) to the top-level or child window. Windows sends this message after posting the WM_NCHITTEST message to the window, but before posting the button down message. When WM_MOUSEACTIVATE is passed to the DefWindowProc function, Windows activates the top-level window and then posts the button down message to the top-level or child window.

By processing WM_MOUSEACTIVATE, a window can control whether the top-level window becomes the active window as a result of a mouse click, and whether the window that was clicked receives the subsequent button down message. It does so by returning one of the following values after processing WM_MOUSEACTIVATE.

Value | Meaning
MA_ACTIVATE | Activates the window and does not discard the mouse message.
MA_NOACTIVATE | Does not activate the window and does not discard the mouse message.
MA_ACTIVATEANDEAT | Activates the window and discards the mouse message.
MA_NOACTIVATEANDEAT | Does not activate the window but discards the mouse message.

+$#A Using Mouse Input

• Tracking the mouse cursor

• Drawing lines with the mouse

• Processing a double-click message

• Selecting a line of text

+$#A Tracking the Mouse Cursor

Windows-based applications often perform tasks that involve tracking the position of the mouse cursor. Most drawing applications, for example, track it during drawing operations, allowing the user to draw in a window's client area by dragging the mouse. Word-processing applications also track the cursor, enabling the user to select a word or block of text by clicking and dragging the mouse.

Tracking the cursor typically involves processing the WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP messages. A window determines when to begin tracking the cursor by checking the cursor position provided in the lParam parameter of the WM_LBUTTONDOWN message. For example, a word-processing application would begin tracking the cursor only if the WM_LBUTTONDOWN message occurred while the cursor was on a line of text, but not if it was past the end of the document.

A window tracks the position of the cursor by processing the stream of WM_MOUSEMOVE messages posted to the window as the mouse moves. Processing the WM_MOUSEMOVE message typically involves a repetitive painting or drawing operation in the client area. For example, a drawing application might redraw a line repeatedly as the mouse moves. A window uses the WM_LBUTTONUP message as a signal to stop tracking the cursor.

+$#A Drawing Lines with the Mouse

The example in this section demonstrates how to track the mouse cursor. It contains portions of a window procedure that enables the user to draw lines in a window's client area by dragging the mouse.

When the window procedure receives a WM_LBUTTONDOWN message, it captures the mouse and saves the coordinates of the cursor, using the coordinates as the starting point of the line. It also uses the ClipCursor function to confine the cursor to the client area during the line drawing operation.

During the first WM_MOUSEMOVE message, the window procedure draws a line from the starting point to the current position of the cursor. During subsequent WM_MOUSEMOVE messages, the window procedure erases the previous line by drawing over it with an inverted pen color. Then it draws a new line from the starting point to the new position of the cursor.

The WM_LBUTTONUP message signals the end of the drawing operation. The window procedure releases the mouse capture and frees the mouse from the client area.

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

HDC hdc; /* handle of device context */

RECT rcClient; /* client area rectangle */

POINT ptClientUL; /* client upper left corner */

POINT ptClientLR; /* client lower right corner */

static POINTS ptsBegin; /* beginning point */

static POINTS ptsEnd; /* new endpoint */

static POINTS ptsPrevEnd; /* previous endpoint */

static BOOL fPrevLine = FALSE; /* previous line flag */

switch (uMsg) {

case WM_LBUTTONDOWN:

/* Capture mouse input. */

SetCapture(hwndMain);

/*

* Retrieve the screen coordinates of the client area,

* and convert them into client coordinates.

/*

GetClientRect(hwndMain, &rcClient);

ptClientUL.x = rcClient.left;

ptClientUL.y = rcClient.top;

/*

* Add one to the right and bottom sides, because the

* coordinates retrieved by GetClientRect do not

* include the far left and lowermost pixels.

*/

ptClientLR.x = rcClient.right + 1;

ptClientLR.y = rcClient.bottom + 1;

ClientToScreen(hwndMain, &ptClientUL);

ClientToScreen(hwndMain, &ptClientLR);

/*

* Copy the client coordinates of the client area

* to the rcClient structure. Confine the mouse cursor

* to the client area by passing the rcClient structure

* to the ClipCursor function.

*/

SetRect(&rcClient, ptClientUL.x, ptClientUL.y,

ptClientLR.x, ptClientLR.y);

ClipCursor(&rcClient);

/*

* Convert the cursor coordinates into a POINTS

* structure, which defines the beginning point of the

* line drawn during a WM_MOUSEMOVE message.

*/

ptsBegin = MAKEPOINTS(lParam);

return 0;

case WM_MOUSEMOVE:

/*

* When moving the mouse, the user must hold down

* the left mouse button to draw lines.

*/

if (wParam & MK_LBUTTON) {

/*

* Retrieve a device context (DC) for the client

* area.

*/

hdc = GetDC(hwndMain);

/*

* The following function ensures that pixels of

* the previously drawn line are set to white and

* those of the new line are set to black.

*/

SetROP2(hdc, R2_NOTXORPEN);

/*

* If a line was drawn during an earlier

* WM_MOUSEMOVE message, draw over it. This erases

* the line by setting the color of its pixels to

* white.

*/

if (fPrevLine) {

MoveToEx(hdc, ptsBegin.x, ptsBegin.y,

(LPPOINT) NULL);

LineTo(hdc, ptsPrevEnd.x, ptsPrevEnd.y);

}

/*

* Convert the current cursor coordinates to a

* POINTS structure, and then draw a new line.

*/

ptsEnd = MAKEPOINTS(lParam);

MoveToEx(hdc, ptsBegin.x, ptsBegin.y,

(LPPOINT) NULL);

LineTo(hdc, ptsEnd.x, ptsEnd.y);

/*

* Set the previous line flag, save the ending

* point of the new line, and then release the DC.

*/

fPrevLine = TRUE;

ptsPrevEnd = ptsEnd;

ReleaseDC(hwndMain, hdc);

}

break;

case WM_LBUTTONUP:

/*

* The user has finished drawing the line. Reset the

* previous line flag, release the mouse cursor, and

* release the mouse capture.

*/

fPrevLine = FALSE;

ClipCursor(NULL);

ReleaseCapture();

return 0;

case WM_DESTROY:

PostQuitMessage(0);

break;

.

. /* Process other messages. */

.

+$#A Processing a Double-Click Message

To receive double-click messages, a window must belong to a window class that has the CS_DBLCLKS class style. You set this style when registering the window class, as shown in the following example.

BOOL InitApplication(hInstance)

HINSTANCE hInstance;

{

WNDCLASS wc;

wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC) MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_IBEAM);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MainMenu";

wc.lpszClassName = "MainWClass";

return RegisterClass(&wc);

}

A double-click message is always preceded by a button down message. For this reason, applications typically use a double-click message to extend a task that it began during a button down message.

+$#A Selecting a Line of Text

The example in this section is taken from a simple word-processing application. It includes code that enables the user to set the position of the caret by clicking anywhere on a line of text, and to select (highlight) a line of text by double-clicking anywhere on the line.

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

WPARAM wParam;

LPARAM lParam;

{

HDC hdc; /* handle of device context */

TEXTMETRIC tm; /* font size data */

int i, j; /* loop counters */

int cCR = 0; /* count of carriage returns */

char ch; /* character from input buffer */

static int nBegLine; /* beginning of selected line */

static int nCurrentLine = 0; /* currently selected line */

static int nLastLine = 0; /* last text line */

static int nCaretPosX = 0; /* x-coordinate of caret */

static int cch = 0; /* number of characters entered */

static int nCharWidth = 0; /* exact width of a character */

static char szHilite[128]; /* text string to highlight */

static DWORD dwCharX; /* average width of characters */

static DWORD dwLineHeight; /* line height */

static POINTS ptsCursor; /* coordinates of mouse cursor */

static COLORREF crPrevText; /* previous text color */

static COLORREF crPrevBk; /* previous background color */

static PTCHAR pchInputBuf; /* address of input buffer */

static BOOL fTextSelected = FALSE; /* text-selection flag */

switch (uMsg) {

case WM_CREATE:

/* Get the metrics of the current font. */

hdc = GetDC(hwndMain);

GetTextMetrics(hdc, &tm);

ReleaseDC(hwndMain, hdc);

/* Save the average character width and height. */

dwCharX = tm.tmAveCharWidth;

dwLineHeight = tm.tmHeight;

/* Allocate a buffer to store keyboard input. */

pchInputBuf = (LPSTR) GlobalAlloc(GPTR,

BUFSIZE * sizeof(TCHAR));

return 0;

case WM_CHAR:

switch (wParam) {

case 0x08: /* backspace */

case 0x0A: /* linefeed */

case 0x1B: /* escape */

MessageBeep(0xFFFFFFFF);

return 0;

case 0x09: /* tab */

/* Convert tabs to four consecutive spaces. */

for (i = 0; i < 4; i++)

SendMessage(hwndMain, WM_CHAR, 0x20, 0);

return 0;

case 0x0D: /* carriage return */

/*

* Record the carriage return, and position the

* caret at the beginning of the new line.

*/

pchInputBuf[cch++] = 0x0D;

nCaretPosX = 0;

nCurrentLine += 1;

break;

default: /* displayable character */

ch = (char) wParam;

HideCaret(hwndMain);

/*

* Retrieve the character's width, and display the

* character.

*/

hdc = GetDC(hwndMain);

GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,

&nCharWidth);

TextOut(hdc, nCaretPosX,

nCurrentLine * dwLineHeight, &ch, 1);

ReleaseDC(hwndMain, hdc);

/* Store the character in the buffer. */

pchInputBuf[cch++] = ch;

/*

* Calculate the new horizontal position of

* the caret. If the new position exceeds the

* maximum, insert a carriage return and

* reposition the caret at the beginning of

* the next line.

*/

nCaretPosX += nCharWidth;

if ((DWORD) nCaretPosX > dwMaxCharX) {

nCaretPosX = 0;

pchInputBuf[cch++] = 0x0D;

++nCurrentLine;

}

ShowCaret(hwndMain);

break;

}

SetCaretPos(nCaretPosX, nCurrentLine * dwLineHeight);

nLastLine = max(nLastLine, nCurrentLine);

break;

.

. /* Process other messages. */

.

case WM_LBUTTONDOWN:

/*

* If a line of text is currently highlighted, redraw

* the text to remove the highlighting.

*/

if (fTextSelected) {

hdc = GetDC(hwndMain);

SetTextColor(hdc, crPrevText);

SetBkColor(hdc, crPrevBk);

TextOut(hdc, 0, nCurrentLine * dwLineHeight,

szHilite, lstrlen(szHilite));

ReleaseDC(hwndMain, hdc);

ShowCaret(hwndMain);

fTextSelected = FALSE;

}

/* Save the current mouse-cursor coordinates. */

ptsCursor = MAKEPOINTS(lParam);

/*

* Determine which line the cursor is on, and save

* the line number. Do not allow line numbers greater

* than the number of the last line of text. The

* line number is later multiplied by the average height

* of the current font. The result is used to set the

* y-coordinate of the caret.

*/

nCurrentLine = min((int)(ptsCursor.y / dwLineHeight),

nLastLine);

/*

* Parse the text input buffer to find the first

* character in the selected line of text. Each

* line ends with a carriage return, so it is possible

* to count the carriage returns to find the selected

* line.

*/

cCR = 0;

nBegLine = 0;

if (nCurrentLine != 0) {

for (i = 0; (i < cch) &&

(cCR < nCurrentLine); i++) {

if (pchInputBuf[i] == 0x0D)

++cCR;

}

nBegLine = i;

}

/*

* Starting at the beginning of the selected line,

* measure the width of each character, summing the

* width with each character measured. Stop when the

* sum is greater than the x-coordinate of the cursor.

* The sum is used to set the x-coordinate of the caret.

*/

hdc = GetDC(hwndMain);

nCaretPosX = 0;

for (i = nBegLine;

(pchInputBuf[i] != 0x0D) && (i < cch); i++) {

ch = pchInputBuf[i];

GetCharWidth32(hdc, (int) ch, (int) ch, &nCharWidth);

if ((nCaretPosX + nCharWidth) > ptsCursor.x)

break;

else

nCaretPosX += nCharWidth;

}

ReleaseDC(hwndMain, hdc);

/* Set the caret to the user-selected position. */

SetCaretPos(nCaretPosX, nCurrentLine * dwLineHeight);

break;

case WM_LBUTTONDBLCLK:

/* Copy the selected line of text to a buffer. */

for (i = nBegLine, j = 0; (pchInputBuf[i] != 0x0D) &&

(i < cch); i++)

szHilite[j++] = pchInputBuf[i];

szHilite[j] = '\0';

/*

* Hide the caret, invert the background and foreground

* colors, and then redraw the selected line.

*/

HideCaret(hwndMain);

hdc = GetDC(hwndMain);

crPrevText = SetTextColor(hdc, RGB(255, 255, 255));

crPrevBk = SetBkColor(hdc, RGB(0, 0, 0));

TextOut(hdc, 0, nCurrentLine * dwLineHeight, szHilite,

lstrlen(szHilite));

SetTextColor(hdc, crPrevText);

SetBkColor(hdc, crPrevBk);

ReleaseDC(hwndMain, hdc);

fTextSelected = TRUE;

break;

.

. /* Process other messages. */

.

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}

+$#AK Mouse Input Reference

The following functions and messages are associated with mouse input.

+$#A Mouse Input Functions

Following are the functions used with mouse input.

DragDetect
GetCapture
GetDoubleClickTime
mouse_event
ReleaseCapture
SetCapture
SetDoubleClickTime
SwapMouseButton

+$#A Mouse Input Messages

Following are the messages used with mouse input.

WM_CAPTURECHANGED
WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_MBUTTONDBLCLK
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MOUSEACTIVATE
WM_MOUSEMOVE
WM_NCHITTEST
WM_NCLBUTTONDBLCLK
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCMBUTTONDBLCLK
WM_NCMBUTTONDOWN
WM_NCMBUTTONUP
WM_NCMOUSEMOVE
WM_NCRBUTTONDBLCLK
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
WM_RBUTTONDBLCLK
WM_RBUTTONDOWN
WM_RBUTTONUP

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide HTML facilement


Multiple Document Interface.rtf

+$#AK Multiple Document Interface

The multiple document interface (MDI) is a specification that defines the standard user interface for applications written for Microsoft® Windows®.

Windows provides extensive support for MDI applications. This overview describes the structure of an MDI application and how to take advantage of the built-in MDI support found in the Microsoft® Win32® application program interface (API).

+$#A About the Multiple Document Interface

An MDI application enables the user to work with more than one document at the same time. Each document is displayed in a separate child window within the client area of the application's main window. Typical MDI applications include word-processing applications that allow the user to work with multiple text documents, and spreadsheet applications that allow the user to work with multiple charts and spreadsheets.

+$#A Frame, Client, and Child Windows

An MDI application has three kinds of windows: a frame window, a client window, as well as a number of child windows. The frame window is like the main window of the application: it has a sizing border, a title bar, a System menu (also known as the Control menu), a Minimize button, and a Maximize button. The application must register a window class for the frame window and provide a window procedure to support it.

An MDI application does not display output in the client area of the frame window. Instead, it displays the MDI client window. An MDI client window is a special type of child window belonging to the preregistered window class MDICLIENT. The client window is a child of the frame window; it serves as the background for child windows. It also provides support for creating and manipulating child windows. For example, an MDI application can create, activate, or maximize child windows by sending messages to the MDI client window.

When the user opens or creates a document, the client window creates a child window for the document. The client window is the parent window of all MDI child windows in a given application. Each child window has a sizing border, a title bar, a System menu, a Minimize button, and a Maximize button. Because a child window is clipped, it is confined to the client window and cannot appear outside it.

An MDI application can support more than one kind of document. For example, a typical spreadsheet application enables the user to work with both charts and spreadsheets. For each type of document that it supports, an MDI application must register a child window class and provide a window procedure to support the windows belonging to that class. For more information about window classes, see Window Classes." For more information about window procedures, see Window Procedures.

Following is a typical MDI application. It is named Multipad.

{ewc msdncd, EWGraphic, group10805 0 /a "SDK_01.bmp"}

+$#A Child Window Creation

To create a child window, an MDI application either calls the CreateMDIWindow function or sends the WM_MDICREATE message to the MDI client window. A more efficient way to create an MDI child window is to call the CreateWindowEx function, specifying the WS_EX_MDICHILD extended style. A thread in an MDI application can use CreateMDIWindow or CreateWindowEx to create a child window in a different thread. The WM_MDICREATE message is used only in the context of the same thread.

To destroy a child window, an MDI application sends a WM_MDIDESTROY message to the MDI client window.

+$#A Child Window Activation

Any number of child windows can appear in the client window at any one time, but only one can be active. The active child window is positioned in front of all other child windows, and its border is highlighted.

The user can activate an inactive child window by clicking it. An MDI application activates a child window by sending a WM_MDIACTIVATE message to the MDI client window. As the client window processes this message, it sends a WM_MDIACTIVATE message to the window procedure of the child window to be activated and to the window procedure of the child window being deactivated.

Windows keeps track of each child window's position in the stack of overlapping windows. This stacking is known as the Z order. The user can activate the next child window in the Z order by choosing the Next command from the System menu in the active window. An application activates the next (or previous) child window in the Z order by sending a WM_MDINEXT message to the client window.

To retrieve the handle of the active child window, the MDI application sends a WM_MDIGETACTIVE message to the client window.

+$#A Multiple Document Menus

The frame window of an MDI application should include a menu bar with a Window menu item and its corresponding pop-up menu. The Window pop-up menu should include command items that arrange the child windows within the client window or that close all child windows. The Window menu of a typical MDI application might include the items in the following table.

Menu item | Purpose
Tile | Arranges child windows in a tile format so that each appears in its entirety in the client window.
Cascade | Arranges child windows in a cascade format. The child windows overlap one another, but the title bar of each is visible.
Arrange Icons | Arranges the icons of minimized child windows along the bottom of the client window.
Close All | Closes all child windows.

Whenever a child window is created, Windows automatically appends a new menu item to the Window menu. The text of the menu item is the same as the text on the menu bar of the new child window. By choosing the menu item, the user can activate the corresponding child window. When a child window is destroyed, Windows automatically removes the corresponding menu item from the Window menu.

Windows can add up to ten menu items to the Window menu. When the tenth child window is created, Windows adds the More Windows item to the Window menu. Choosing this item causes the Select Window dialog box to appear. The dialog box contains a list box with the titles of all MDI child windows currently available. The user can activate a child window by choosing its title from the list box.

If your MDI application supports several types of child windows, tailor the menu bar to reflect the operations associated with the active window. To do this, provide separate menu resources for each type of child window the application supports. When a new type of child window is activated, the application should send a WM_MDISETMENU message to the client window, passing to it the handle of the corresponding menu.

When no child window exists, the menu bar should contain only items used to create or open a document.

When the user is navigating through an MDI application's menus by using cursor keys, the keys behave differently than when the user is navigating through a typical application's menus. In an MDI application, control passes from the application's System menu to the System menu of the active child window, and then to the first item on the menu bar.

+$#A Accelerators

To receive and process accelerator keys for its child windows, an MDI application must include the TranslateMDISysAccel function in its message loop. The loop must call TranslateMDISysAccel before calling the TranslateAccelerator or DispatchMessage function.

Accelerator keys on the System menu for an MDI child window are different from those for a non-MDI child window. In an MDI child window, the ALT+ − (minus) key combination opens the System menu, the CTRL+F4 key combination closes the active child window, and the CTRL+F6 key combination activates the next child window.

+$#A Child Window Size and Arrangement

An MDI application controls the size and position of its child windows by sending messages to the MDI client window. To maximize the active child window, the application sends the WM_MDIMAXIMIZE message to the client window. When a child window is maximized, its client area completely fills the MDI client window. In addition, Windows automatically hides the child window's title bar, and adds the child window's System menu icon and Restore icon to the MDI application's menu bar. The application can restore the client window to its original (premaximized) size and position by sending the client window a WM_MDIRESTORE message.

An MDI application can arrange its child windows in either a cascade or tile format. When the child windows are cascaded, the windows appear in a stack. The window on the bottom of the stack occupies the upper left corner of the screen, and the remaining windows are offset vertically and horizontally so that the left border and title bar of each child window is visible. To arrange child windows in the cascade format, an MDI application sends the WM_MDICASCADE message. Typically, the application sends this message when the user chooses the Cascade command from the Window menu.

When the child windows are tiled, Windows displays each child window in its entirety ⎯ overlapping none of the windows. All of the windows are sized, as necessary, to fit within the client window. To arrange child windows in the tile format, an MDI application sends a WM_MDITILE message to the client window. Typically, the application sends this message when the user chooses the Tile command from the Window menu.

An MDI application should provide a different icon for each type of child window it supports. The application specifies an icon when registering the child window class. Windows automatically displays a child window's icon in the lower portion of the client window when the child window is minimized. An MDI application directs Windows to arrange child window icons by sending a WM_MDIICONARRANGE message to the client window. Typically, the application sends this message when the user chooses the Arrange Icons command from the Window menu.

+$#A Icon Title Windows

Because MDI child windows may be minimized, an MDI application must avoid manipulating icon title windows as if they were normal MDI child windows. Icon title windows appear when the application enumerates child windows of the MDI client window. Icon title windows differ from other child windows, however, in that they are owned by an MDI child window.

To determine whether a child window is an icon title window, use the GetWindow function with the GW_OWNER index. Nontitle windows return NULL. Note that this test is insufficient for top-level windows, because menus and dialog boxes are owned windows.

+$#A Child Window Data

Because the number of child windows varies depending on how many documents the user opens, an MDI application must be able to associate data (for example, the name of the current file) with each child window. There are two ways to do this:

• Store child window data in the window structure.

• Use window properties.

+$#A Data in the Window Structure

When an MDI application registers a window class, it may reserve extra space in the window structure for application data specific to this particular class of windows. To store and retrieve data in this extra space, the application uses the GetWindowWord, SetWindowWord, GetWindowLong, and SetWindowLong functions.

To maintain a large amount of data for a child window, an application can allocate memory for a data structure and then store the handle of the memory containing the structure in the extra space associated with the child window.

+$#A Multiple Document Window Properties

An MDI application can also store per-document data by using window properties. (Per-document data is data specific to the type of document contained in a particular child window.) Properties are different from extra space in the window structure in that you need not allocate extra space when registering the window class. A window can have any number of properties. Also, where offsets are used to access the extra space in window structures, properties are referred to by string names. For more information about window properties, see Window Properties.

+$#A Using the Multiple Document Interface

This section explains how to perform the following tasks:

• Registering child and frame window classes.

• Creating frame and child windows.

• Writing the main message loop.

• Writing the frame window procedure.

• Writing the child window procedure.

• Creating a child window.

To illustrate these tasks, this section includes examples from Multipad, a typical MDI application.

+$#A Registering Child and Frame Window Classes

A typical MDI application must register two window classes: one for its frame window and one for its child windows. If an application supports more than one type of document (for example, a spreadsheet and a chart), it must register a window class for each type.

The class structure for the frame window is similar to the class structure for the main window in non-MDI applications. The class structure for the MDI child windows differs slightly from the structure for child windows in non-MDI applications as follows:

• The class structure should have an icon, because the user can minimize an MDI child window as if it were a normal application window.

• The menu name should be NULL, because an MDI child window cannot have its own menu.

• The class structure should reserve extra space in the window structure. With this space, the application can associate data, such as a filename, with a particular child window.

The following example shows how Multipad registers its frame and child window classes.

BOOL APIENTRY InitializeApplication()

{

WNDCLASS wc;

/* Register the frame window class. */

wc.style = 0;

wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInst;

wc.hIcon = LoadIcon(hInst, IDMULTIPAD);

wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);

wc.lpszMenuName = IDMULTIPAD;

wc.lpszClassName = szFrame;

if (!RegisterClass (&wc) )

return FALSE;

/* Register the MDI child window class. */

wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;

wc.hIcon = LoadIcon(hInst, IDNOTE);

wc.lpszMenuName = (LPCTSTR) NULL;

wc.cbWndExtra = CBWNDEXTRA;

wc.lpszClassName = szChild;

if (!RegisterClass(&wc))

return FALSE;

return TRUE;

}

+$#A Creating Frame and Child Windows

After registering its window classes, an MDI application can create its windows. First, it creates its frame window by using the CreateWindow or CreateWindowEx function. After creating its frame window, the application creates its client window, again by using CreateWindow or CreateWindowEx. The application should specify MDICLIENT as the client window's class name; MDICLIENT is a preregistered window class defined by Windows. The lpvParam parameter of CreateWindow or CreateWindowEx should point to a CLIENTCREATESTRUCT structure. This structure contains the members described in the following table:

Member | Description
hWindowMenu | Identifies the Window menu used for controlling MDI child windows. As child windows are created, the application adds their titles to the Window menu as menu items. The user can then activate a child window by choosing its title from the Window menu.
idFirstChild | Specifies the identifier of the first MDI child window. The first MDI child window created is assigned this identifier. Additional windows are created with incremented window identifiers. When a child window is destroyed, Windows immediately reassigns the window identifiers to keep their range contiguous.

When a child window's title is added to the Window menu, Windows assigns an identifier to the child window. When the user chooses a child window's title, the frame window receives a WM_COMMAND message with the identifier in the wParam parameter. You should specify a value for the idFirstChild member that does not conflict with menu-item identifiers in the frame window's menu.

Multipad's frame window procedure creates the MDI client window while processing the WM_CREATE message. The following example shows how the client window is created.

case WM_CREATE:

{

CLIENTCREATESTRUCT ccs;

/*

* Retrieve the handle of the Window menu and assign the

* first child window identifier.

*/

ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), WINDOWMENU);

ccs.idFirstChild = IDM_WINDOWCHILD;

/* Create the MDI client window. */

hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR) NULL,

WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,

0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);

ShowWindow(hwndMDIClient, SW_SHOW);

}

break;

Titles of child windows are added to the bottom of the Window menu. If the application adds strings to the Window menu by using the AppendMenu function, these strings can be overwritten by the titles of the child windows when the Window menu is repainted. (The Window menu is repainted whenever a child window is created or destroyed.) An MDI application that adds strings to its Window menu should use the InsertMenu function and verify that the titles of child windows have not overwritten these new strings.

Use the WS_CLIPCHILDREN style to create the MDI client window to prevent the window from painting over its child windows.

+$#A Writing the Main Message Loop

The main message loop of an MDI application is similar to that of a non-MDI application handling accelerator keys. The difference is that the MDI message loop calls the TranslateMDISysAccel function before checking for application-defined accelerator keys or before dispatching the message.

The following example shows the message loop of a typical MDI application.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&

!TranslateAccelerator(hwndFrame, hAccel, &msg)){

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

The TranslateMDISysAccel function translates WM_KEYDOWN messages into WM_SYSCOMMAND messages and sends them to the active MDI child window. If the message is not an MDI accelerator message, the function returns FALSE, in which case the application uses the TranslateAccelerator function to determine whether any of the application-defined accelerator keys were pressed. If not, the loop dispatches the message to the appropriate window procedure.

+$#A Writing the Frame Window Procedure

The window procedure for an MDI frame window is similar to that of a non-MDI application's main window. The difference is that a frame window procedure passes all messages it does not handle to the DefFrameProc function rather than to the DefWindowProc function. In addition, the frame window procedure must also pass some messages that it does handle, including those listed in the following table.

Message | Response
WM_COMMAND | Activates the MDI child window that the user chooses. This message is sent when the user chooses an MDI child window from the Window menu of the MDI frame window. The window identifier accompanying this message identifies the MDI child window to be activated.
WM_MENUCHAR | Opens the System menu of the active MDI child window when the user presses the ALT+ − (minus) key combination.
WM_SETFOCUS | Passes the keyboard focus to the MDI client window, which in turn passes it to the active MDI child window.
WM_SIZE | Resizes the MDI client window to fit in the new frame window's client area. If the frame window procedure sizes the MDI client window to a different size, it should not pass the message to the DefWindowProc function.

The frame window procedure in Multipad is called MPFrameWndProc. The handling of other messages by MPFrameWndProc is similar to that of non-MDI applications. WM_COMMAND messages in Multipad are handled by the locally defined CommandHandler function. For command messages Multipad does not handle, CommandHandler calls the DefFrameProc function. If Multipad doesn't use DefFrameProc by default, the user can't activate a child window from the Window menu, because the WM_COMMAND message sent by choosing the window's menu item (command) would be lost.

+$#A Writing the Child Window Procedure

Like the frame window procedure, an MDI child window procedure uses a special function for processing messages by default. All messages that the child window procedure does not handle must be passed to the DefMDIChildProc function rather than to the DefWindowProc function. In addition, some window-management messages must be passed to DefMDIChildProc, even if the application handles the message, in order for MDI to function correctly. Following are the messages the application must pass to DefMDIChildProc.

Message | Response
WM_CHILDACTIVATE | Performs activation processing when MDI child windows are sized, moved, or displayed. This message must be passed.
WM_GETMINMAXINFO | Calculates the size of a maximized MDI child window, based on the current size of the MDI client window.
WM_MENUCHAR | Passes the message to the MDI frame window.
WM_MOVE | Recalculates MDI client scroll bars, if they are present.
WM_SETFOCUS | Activates the child window, if it is not the active MDI child window.
WM_SIZE | Performs operations necessary for changing the size of a window, especially for maximizing or restoring an MDI child window. Failing to pass this message to the DefMDIChildProc function produces highly undesirable results.
WM_SYSCOMMAND | Handles System menu commands: SC_NEXTWINDOW, SC_PREVWINDOW, SC_MOVE, SC_SIZE, and SC_MAXIMIZE.

+$#A Creating a Child Window

To create an MDI child window, an application can either call the CreateMDIWindow function or send an WM_MDICREATE message to the MDI client window. (The application can use the CreateWindowEx function with the WS_EX_MDICHILD style to create MDI child windows.) A single-threaded MDI application can use either method to create a child window. A thread in a multithreaded MDI application must use the CreateMDIWindow or CreateWindowEx function to create a child window in a different thread.

The lParam parameter of a WM_MDICREATE message is a far pointer to an MDICREATESTRUCT structure. The structure includes four dimension members: x and y, which indicate the horizontal and vertical positions of the window, and cx and cy, which indicate the horizontal and vertical extents of the window. Any of these members may be assigned explicitly by the application, or they may be set to CW_USEDEFAULT, in which case Windows selects a position, size, or both, according to a cascading algorithm. In any case, all four members must be initialized. Multipad uses CW_USEDEFAULT for all dimensions.

The last member of the MDICREATESTRUCT structure is the style member, which may contain style bits for the window. To create an MDI child window that can have any combination of window styles, specify the MDIS_ALLCHILDSTYLES window style. When this style is not specified, an MDI child window has the WS_MINIMIZE, WS_MAXIMIZE, WS_HSCROLL, and WS_VSCROLL styles as default settings.

Multipad creates its MDI child windows by using its locally defined AddFile function (located in the source file MPFILE.C). The AddFile function sets the title of the child window by assigning the szTitle member of the window's MDICREATESTRUCT structure to either the name of the file being edited or to "Untitled." The szClass member is set to the name of the MDI child window class registered in Multipad's InitializeApplication function. The hOwner member is set to the application's instance handle.

The following example shows the AddFile function in Multipad.

HWND APIENTRY AddFile(pName)

CHAR * pName;

{

HWND hwnd;

CHAR sz[160];

MDICREATESTRUCT mcs;

if (!pName) {

/*

* If the pName parameter is NULL, load the "Untitled"

* string from the STRINGTABLE resource and set the szTitle

* member of MDICREATESTRUCT.

*/

LoadString(hInst, IDS_UNTITLED, sz, sizeof(sz));

mcs.szTitle = (LPCTSTR) sz;

}

else

/*

* Title the window with the full path and filename,

* obtained by calling the OpenFile function with the

* OF_PARSE flag, which is called before AddFile().

*/

mcs.szTitle = of.szPathName;

mcs.szClass = szChild;

mcs.hOwner = hInst;

/* Use the default size for the child window. */

mcs.x = mcs.cx = CW_USEDEFAULT;

mcs.y = mcs.cy = CW_USEDEFAULT;

/*

* Give the child window the default style. The styleDefault

* variable is defined in MULTIPAD.C.

*/

mcs.style = styleDefault;

/* Tell the MDI client window to create the child window. */

hwnd = (HWND) SendMessage (hwndMDIClient, WM_MDICREATE, 0,

(LONG) (LPMDICREATESTRUCT) &mcs);

/*

* If the file is found, read its contents into the child

* window's client area.

*/

if (pName) {

if (!LoadFile(hwnd, pName)) {

/* Cannot load the file; close the window. */

SendMessage(hwndMDIClient, WM_MDIDESTROY,

(DWORD) hwnd, 0L);

}

}

return hwnd;

}

The pointer passed in the lParam parameter of the WM_MDICREATE message is passed to the CreateWindow function and appears as the first member in the CREATESTRUCT structure, passed in the WM_CREATE message. In Multipad, the child window initializes itself during WM_CREATE message processing by initializing document variables in its extra data and by creating the edit control's child window.

+$#AK Multiple Document Interface Reference

The following functions and messages are used in MDI applications.

+$#A Multiple Document Interface Functions

CreateMDIWindow
DefFrameProc
DefMDIChildProc
TranslateMDISysAccel

+$#A Multiple Document Interface Messages

WM_MDIACTIVATE
WM_MDICASCADE
WM_MDICREATE
WM_MDIDESTROY
WM_MDIGETACTIVE
WM_MDIICONARRANGE
WM_MDIMAXIMIZE
WM_MDINEXT
WM_MDIREFRESHMENU
WM_MDIRESTORE
WM_MDISETMENU
WM_MDITILE

Créé avec HelpNDoc Personal Edition: Générateur de documentation et EPub facile


_DEBUT_3081_A.rtf

+$#AK Handling Errors with Low-Level Audio Functions

Low-level audio functions return a nonzero error code. A set of functions convert these error codes into a textual description of the error. The application must still look at the error value itself to determine how to proceed, but textual descriptions of errors can be used in dialog boxes describing errors to users.

The following functions can be used to get textual descriptions of low-level audio errors.

Function | Description
midiInGetErrorText | Retrieves a textual description of a specified MIDI input error.
midiOutGetErrorText | Retrieves a textual description of a specified MIDI output error.
waveInGetErrorText | Retrieves a textual description of a specified waveform input error.
waveOutGetErrorText | Retrieves a textual description of a specified waveform output error.

The only low-level audio functions that don't return error codes are the device-numbering functions (auxGetNumDevs, midiInGetNumDevs, and so on). These functions return a value of zero if no devices are present in a system, or if any errors are encountered by the function.

+$#A Playing Waveform Audio

If your application plays waveform audio, you can use the Media Control Interface (MCI) to control waveform output devices. If the MCI waveform playback services don't meet the needs of your application, you can manage waveform playback by using the low-level waveform services.

+$#A Waveform Output Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for the waveform output functions.

Type | Description
HKL | Handle to a keyboard layout.
HWAVEOUT | Handle to an audio waveform output device.
WAVEOUTCAPS | Structure used to inquire about the capabilities of a particular waveform output device.
WAVEFORMAT | Structure that specifies the data formats supported by a particular waveform output device. This structure is also used for waveform input devices.
WAVEHDR | Structure that is a header for a block of waveform output data. This structure is also used for waveform input devices.

+$#A Querying Waveform Output Devices

Before playing a waveform, call the waveOutGetDevCaps function to determine the waveform output capabilities of the playback device as described earlier in this section. This function takes a pointer to a WAVEOUTCAPS structure, which it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.

In addition, the WAVEOUTCAPS structure provides information on the standard waveform formats and features supported by the device driver.

+$#A Determining Standard Format Support

The dwFormats member of the WAVEOUTCAPS structure specifies the standard waveform formats supported by a device. The MMSYSTEM.H header file defines the following standard waveform format identifiers for the dwFormats member.

Format identifier | Waveform format
WAVE_FORMAT_1M08 | 8-bit mono at 11.025 kHz
WAVE_FORMAT_1S08 | 8-bit stereo at 11.025 kHz
WAVE_FORMAT_1M16 | 16-bit mono at 11.025 kHz
WAVE_FORMAT_1S16 | 16-bit stereo at 11.025 kHz
WAVE_FORMAT_2M08 | 8-bit mono at 22.05 kHz
WAVE_FORMAT_2S08 | 8-bit stereo at 22.05 kHz
WAVE_FORMAT_2M16 | 16-bit mono at 22.05 kHz
WAVE_FORMAT_2S16 | 16-bit stereo at 22.05 kHz
WAVE_FORMAT_4M08 | 8-bit mono at 44.1 kHz
WAVE_FORMAT_4S08 | 8-bit stereo at 44.1 kHz
WAVE_FORMAT_4M16 | 16-bit mono at 44.1 kHz
WAVE_FORMAT_4S16 | 16-bit stereo at 44.1 kHz

The value of the dwFormats member is the result of a logical OR operation on the flags listed above.

This information on standard-format support also applies to the WAVEINCAPS structure used with waveform input devices. For information about the WAVEINCAPS structure, see Querying Waveform Input Devices.

To determine whether a specific format is supported by a device, use the waveOutOpen function with the WAVE_FORMAT_QUERY flag as shown in the next section.

+$#A Determining Low-Level Audio Nonstandard Format Support

Waveform devices can support nonstandard formats not listed in the preceding table. To see whether a particular format (standard or nonstandard) is supported by a device, you can call the waveOutOpen function with the WAVE_FORMAT_QUERY flag. The WAVE_FORMAT_QUERY flag tells waveOutOpen to check whether the requested format is supported. The wave device is not actually opened. The requested format is specified by the structure pointed to by the lpFormat parameter passed to waveOutOpen. For information about setting up this structure, see Specifying Waveform Data Formats. The following example uses this technique to determine whether a given waveform device supports a given format.

/*

* Determines whether the given waveform output device supports

* a given wave-form format. Returns 0 if the format is supported,

* WAVEERR_BADFORMAT if the format is not supported, and one of

* the MMSYSERR_ error codes if there are other errors in opening

* the given waveform device.

*/

UINT IsFormatSupported(LPPCMWAVEFORMAT lpPCMWaveFormat,

UINT wDeviceID)

{

return (waveOutOpen(

NULL, // NULL for query

wDeviceID, // device identifier

(LPWAVEFORMAT)lpPCMWaveFormat, // requested format

NULL, // no callback

NULL, // no instance data

WAVE_FORMAT_QUERY)); // query only

}

This technique to determine nonstandard format support also applies to waveform input devices. The only difference is that the waveInOpen function is used in place of waveOutOpen to query for format support.

Note To determine whether a particular waveform-data format is supported by any of the waveform devices in a system, use the technique illustrated in the previous example, but specify the WAVE_MAPPER constant for the wDeviceID parameter. For more information about using the WAVE_MAPPER constant, see Selecting a Waveform Output Device.

+$#A Determining Capabilities of Waveform Output Devices

Waveform output devices vary in the capabilities they support. The dwSupport member of the WAVEOUTCAPS structure indicates whether a given device supports capabilities such as volume and pitch changes. MMSYSTEM.H defines the following flags for the dwSupport member.

Flag | Description
WAVECAPS_PITCH | Pitch-change support
WAVECAPS_PLAYBACKRATE | Playback-rate-change support
WAVECAPS_VOLUME | Volume-control support
WAVECAPS_LRVOLUME | Individual volume-control support for both left and right channels

The dwSupport member is a logical OR of the flags listed in the preceding table.

For more information about playback volume levels, see Changing Waveform Playback Volume. For more information about pitch and playback rates, see Changing Pitch and Playback Rate.

+$#A Opening Waveform Output Devices

Use the waveOutOpen function to open a waveform output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Selecting a Waveform Output Device

Some multimedia computers have multiple waveform output devices. Unless you know you want to open a specific waveform output device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen function chooses the device in the system best capable of playing the given data format.

+$#A Specifying Waveform Data Formats

When you call the waveOutOpen function to open a device driver for playback or determine whether the driver supports a particular data format, use the lpFormat parameter to specify a pointer to a structure containing the requested waveform data format.

The WAVEFORMAT structure specifies format information common to all types of waveform data formats. Currently the only format supported is pulse code modulated (PCM), but in the future other types such as adaptive differential pulse code modualtion (ADPCM) may be supported.

In addition to the general information in the WAVEFORMAT structure, specific information is needed to describe a PCM waveform data format completely. For PCM waveform data, the PCMWAVEFORMAT structure includes a WAVEFORMAT structure along with an additional member containing PCM-specific information.

+$#A Using the PCMWAVEFORMAT Structure

For PCM audio data, use the PCMWAVEFORMAT structure to specify the data format. The following example shows how to set up a PCMWAVEFORMAT structure for 11.025 kilohertz (kHz) 8-bit mono and for 44.1 kHz 16-bit stereo. After setting up PCMWAVEFORMAT, the example calls the IsFormatSupported function to verify that the PCM waveform output device supports the format. The source code for IsFormatSupported is given in an example in "Determining Nonstandard Format Support."

UINT wReturn;

PCMWAVEFORMAT pcmWaveFormat;

/* Set up PCMWAVEFORMAT for 11 kHz 8-bit mono. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;

pcmWaveFormat.wf.nChannels = 1;

pcmWaveFormat.wf.nSamplesPerSec = 11025L;

pcmWaveFormat.wf.nAvgBytesPerSec = 11025L;

pcmWaveFormat.wf.nBlockAlign = 1;

pcmWaveFormat.wBitsPerSample = 8;

/* See if format is supported by any device in system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)

MessageBox(hMainWnd, "11 kHz 8-bit mono is supported.",

"", MB_ICONINFORMATION);

else if (wReturn == WAVERR_BADFORMAT)

MessageBox(hMainWnd, "11 kHz 8-bit mono NOT supported.",

"", MB_ICONINFORMATION);

else

MessageBox(hMainWnd, "Error opening waveform device.",

"Error", MB_ICONEXCLAMATION);

/* Set up PCMWAVEFORMAT for 44.1 kHz 16-bit stereo. */

pcmWaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;

pcmWaveFormat.wf.nChannels = 2;

pcmWaveFormat.wf.nSamplesPerSec = 44100L;

pcmWaveFormat.wf.nAvgBytesPerSec = 176400L;

pcmWaveFormat.wf.nBlockAlign = 4;

pcmWaveFormat.wBitsPerSample = 32;

/* See if format is supported by any device in the system. */

wReturn = IsFormatSupported(&pcmWaveFormat, WAVE_MAPPER);

/* Report results. */

if (wReturn == 0)

MessageBox(hMainWnd, "44.1 kHz 16-bit stereo is supported.",

"", MB_ICONINFORMATION);

else if (wReturn == WAVERR_BADFORMAT)

MessageBox(hMainWnd, "44.1 kHz 16-bit stereo NOT supported.",

"", MB_ICONINFORMATION);

else

MessageBox(hMainWnd, "Error opening waveform device.",

"Error", MB_ICONEXCLAMATION);

+$#A Getting Format Information from a WAVE File

The easiest way to get waveform-format information from a WAVE file is by using the multimedia file input and output (I/O) services. To do this, use the mmioDescend function to locate the "fmt" chunk containing the format information and the mmioRead function to read the format chunk directly into the proper format structure. (Chunks are the basic building blocks of RIFF files.) The following example illustrates this technique. For more information about multimedia file I/O, see Multimedia File Input/Output.

void GetFormatInfo(void)

{

HMMIO hmmio;

MMCKINFO mmckinfoParent;

MMCKINFO mmckinfoSubchunk;

DWORD dwFmtSize;

HANDLE hFormat;

WAVEFORMAT *pFormat;

.ifdef REF

.

.

.

.endif

/* Open the given file for reading using buffered I/O. */

.ifdef REF

...

.endif

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure it's a WAVE file.

*/

.ifdef REF

...

.endif

/*

* Now, find the format chunk (form type "fmt "). It should be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

MessageBox(hwndApp, "WAVE file is corrupted.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

/*

* Get the size of the format chunk;

* allocate and lock memory for it.

*/

dwFmtSize = mmckinfoSubchunk.cksize;

hFormat = LocalAlloc(LMEM_MOVEABLE, dwFmtSize);

if (hFormat == NULL) {

MessageBox(hwndApp, "Out of memory.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

pFormat = (WAVEFORMAT *) LocalLock(hFormat);

if (pFormat == NULL) {

MessageBox(hwndApp, "Failed to lock memory for format chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/* Read the format chunk. */

if (mmioRead(hmmio, (LPSTR)pFormat,

dwFmtSize) != (LRESULT)dwFmtSize) {

MessageBox(hwndApp, "Failed to read format chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/* Make sure it's a PCM file. */

if (pFormat->wFormatTag != WAVE_FORMAT_PCM) {

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

MessageBox(hwndApp, "The file is not a PCM file.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

/*

* Make sure the system has a waveform output

* device capable of playing this format.

*/

if (waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMAT)pFormat,

NULL, 0L, WAVE_FORMAT_QUERY)) {

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

MessageBox(hwndApp,

"The waveform device can't play this format.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

.ifdef REF

.

.

.

.endif

}

+$#A Writing Waveform Data

After successfully opening a waveform output device driver, you can begin waveform playback.

The waveOutWrite function writes a data block to a waveform output device. Use the WAVEHDR structure to specify the waveform data block you are sending. This structure contains a pointer to a locked data block, the length of the data block, and assorted flags.

After you send a data block to an output device by using waveOutWrite, you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the system for the completion of each data block so that you know when to send additional blocks. For details about different techniques for monitoring data block completion, see Managing Audio Data Blocks.

+$#A Example of Writing Waveform Data

The following example illustrates the steps required to allocate and set up a WAVEHDR data structure and write a block of data to a waveform output device.

/*

* Global variables. Must be visible to window-procedure function

* so it can unlock and free the data block after it has been played.

*/

HANDLE hData = NULL; // handle of waveform data memory

HPSTR lpData = NULL; // pointer to waveform data memory

void WriteWaveData(void)

{

HWAVEOUT hWaveOut;

HGLOBAL hWaveHdr;

LPWAVEHDR lpWaveHdr;

HMMIO hmmio;

UINT wResult;

HANDLE hFormat;

WAVEFORMAT *pFormat;

DWORD dwDataSize;

.ifdef REF

.

.

.

.endif

/* Open a waveform device for output using window callback. */

if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,

(LPWAVEFORMAT)pFormat,

(LONG)hwndApp, 0L, CALLBACK_WINDOW)) {

MessageBox(hwndApp,

"Failed to open waveform output device.",

NULL, MB_OK | MB_ICONEXCLAMATION);

LocalUnlock(hFormat);

LocalFree(hFormat);

mmioClose(hmmio, 0);

return;

}

/*

* Allocate and lock memory for the waveform data. The memory

* for waveform data must be globally allocated with

* GMEM_MOVEABLE and GMEM_SHARE flags.

*/

hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );

if (!hData)

{

MessageBox(hwndApp, "Out of memory.",

NULL, MB_OK | MB_ICONEXCLAMATION);

mmioClose(hmmio, 0);

return;

}

if ((lpData = GlobalLock(hData)) == NULL) {

MessageBox(hwndApp, "Failed to lock memory for data chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

GlobalFree(hData);

mmioClose(hmmio, 0);

return;

}

/* Read the waveform data subchunk. */

if(mmioRead(hmmio, (HPSTR) lpData, dwDataSize) != (LRESULT)dwDataSize) {

MessageBox(hwndApp, "Failed to read data chunk.",

NULL, MB_OK | MB_ICONEXCLAMATION);

GlobalUnlock(hData);

GlobalFree(hData);

mmioClose(hmmio, 0);

return;

}

/*

* Allocate and lock memory for the header. This memory must

* also be globally allocated with GMEM_MOVEABLE and

* GMEM_SHARE flags.

*/

hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,

(DWORD) sizeof(WAVEHDR));

if (hWaveHdr == NULL) {

GlobalUnlock(hData);

GlobalFree(hData);

MessageBox(hwndApp, "Not enough memory for header.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);

if (lpWaveHdr == NULL) {

GlobalUnlock(hData);

GlobalFree(hData);

MessageBox(hwndApp,

"Failed to lock memory for header.",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

/* After allocation, set up and prepare header. */

lpWaveHdr->lpData = lpData;

lpWaveHdr->dwBufferLength = dwDataSize;

lpWaveHdr->dwFlags = 0L;

lpWaveHdr->dwLoops = 0L;

waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));

/*

* Now the data block can be sent to the output device. The

* waveOutWrite function returns immediately and waveform

* data is sent to the output device in the background.

*/

wResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));

if (wResult != 0) {

waveOutUnprepareHeader(hWaveOut, lpWaveHdr,

sizeof(WAVEHDR));

GlobalUnlock( hData);

GlobalFree(hData);

MessageBox(hwndApp, "Failed to write block to device",

NULL, MB_OK | MB_ICONEXCLAMATION);

return;

}

.ifdef REF

.

.

.

.endif

}

+$#A PCM Waveform Data Format

The lpData member in the WAVEHDR structure points to the waveform data samples. For 8-bit PCM data, each sample is represented by a single unsigned data byte. For 16-bit PCM data, each sample is represented by a 16-bit signed value. The following table summarizes the maximum, minimum, and midpoint values for PCM waveform data.

Data format | Maximum value | Minimum value | Midpoint value
8-bit PCM | 255 (0xFF) | 0 | 128 (0x80)
16-bit PCM | 32767 (0x7FFF) | -32768 (0x8000) | 0

The order of the data bytes varies between 8-bit and 16-bit, and between mono and stereo formats. The following tables show data packing for the first four words of different PCM waveform data formats.

Data packing for 8-bit mono PCM:

Sample 1 | Sample 2 | Sample 3 | Sample 4
Channel 0 | Channel 0 | Channel 0 | Channel 0

Data packing for 8-bit stereo PCM:

Sample 1 |  | Sample 2 |
Channel 0 (left) | Channel 1 (right) | Channel 0 (left) | "Channel 1 (right)"
Low-order byte | High-order byte | Low-order byte | High-order byte

Data packing for 16-bit mono PCM:

Sample 1 |  | Sample 2 |
Channel 0 low byte | Channel 0 high byte | Channel 0 low byte | Channel 0 high byte

Data packing for 16-bit stereo PCM:

Sample 1 |  |  |
Channel 0 (left) byte | Channel 0 (left) byte | Channel 1 (right) byte | Channel 1 (right) byte

+$#A Waveform Looping

Waveform looping is controlled by the dwLoops and dwFlags members in the WAVEHDR structures passed to the device with waveOutWrite. Use the WHDR_BEGINLOOP and WHDR_ENDLOOP flags in the dwFlags member to specify the beginning and ending data blocks for looping.

To loop on a single block, specify both flags for the same block. To specify the number of loops, use the dwLoops member in the WAVEHDR structure for the first block in the loop.

You can stop waveform looping with the waveOutBreakLoop function. The blocks making up the loop are played to the end before the loop is stopped.

+$#A Using Window Messages to Manage Waveform Playback

The following messages can be sent to a window-procedure function for managing waveform playback.

Message | Description
MM_WOM_CLOSE | Sent when the device is closed using waveOutClose.
MM_WOM_DONE | Sent when the device driver is finished with a data block sent using waveOutWrite.
MM_WOM_OPEN | Sent when the device is opened using waveOutOpen.

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. For the MM_WOM_DONE message, lParam specifies a pointer to a WAVEHDR structure identifying the completed data block. The lParam parameter is unused for the MM_WOM_CLOSE and MM_WOM_OPEN messages.

The most useful message is MM_WOM_DONE. When this message signals that playback of a data block is complete, you can clean up and free the data block. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_WOM_OPEN and MM_WOM_CLOSE messages.

The following example shows how to process the MM_WOM_DONE message. The example assumes the application does not play multiple data blocks, so it can close the output device after playing a single data block.

/* WndProc--Main window-procedure function. */

LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

switch (msg) {

.ifdef REF

.

.

.

.endif

case MM_WOM_DONE:

/* A data block has been played and can now be freed. */

waveOutUnprepareHeader((HWAVEOUT) wParam,

(LPWAVEHDR) lParam, sizeof(WAVEHDR) );

GlobalUnlock(hData);

GlobalFree(hData);

waveOutClose((HWAVEOUT) wParam);

break;

}

return DefWindowProc(hWnd,msg,wParam,lParam);

}

+$# Using a Low-Level Callback Function to Manage Waveform Playback

The syntax of the low-level callback function for waveform output devices is as follows:

void CALLBACK waveOutCallback(hWaveOut, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of waveform output callback functions.

Message | Description
WOM_CLOSE | Sent when the device is closed using the waveOutClose function.
WOM_DONE | Sent when the device driver is finished with a data block sent using the waveOutWrite function.
WOM_OPEN | Sent when the device is opened using the waveOutOpen function.

These messages are similar to the messages sent to window-procedure functions, the parameters are different. A handle of the open waveform device is passed as a parameter to the callback function, along with the doubleword of instance data that was passed using waveOutOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WOM_DONE message, dwParam1 specifies a pointer to a WAVEHDR structure identifying the completed data block and dwParam2 is not used. For the WOM_OPEN and WOM_CLOSE messages, neither of the parameters is used.

After the driver is finished with a data block, you can clean up and free the data block, as described in "Allocating and Preparing Audio Data Blocks." Because of the suggested restrictions on low-level audio callback functions, it is better to not do this from within the callback. See Using a Callback Function to Process Driver Messages for details about suggested restrictions on using callback functions.

+$#A Getting the Current Playback Position

While a waveform is playing, you can monitor the current playback position within the waveform. The Win32 API provides the waveOutGetPosition function for this purpose.

This function takes three parameters: a handle of a waveform output device, a pointer to an MMTIME structure, and an unsigned integer specifying the size of the MMTIME structure.

For waveform devices, the preferred time format in which to represent the current position is samples. Thus the current position of a waveform device is specified as the number of samples for one channel from the beginning of the waveform.

To query the current position of a waveform device, set the wType member of the MMTIME structure to TIME_SAMPLES and pass the structure to waveOutGetPosition.

+$#A Stopping, Pausing, and Restarting Playback

You can stop or pause playback while a waveform is playing. Once playback has been paused, you can restart it. The Win32 API provides the following functions for controlling waveform playback.

Function | Description
waveOutPause | Pauses playback on a waveform output device.
waveOutReset | Stops playback on a waveform output device and marks all pending data blocks as done.
waveOutRestart | Resumes playback on a paused waveform output device.

Use the waveOutPause function to pause a waveform device that is currently playing. To resume playback on a paused device, use the waveOutRestart function. These functions take a single parameter: the waveform output device handle returned by the waveOutOpen function. Pausing a waveform device may not be instantaneous ⎯ the driver can finish playing the current block before pausing playback.

Typically, as soon as the first waveform data block is sent by using the waveOutWrite function, the waveform device begins playing. If you don't want the waveform to start playing immediately, call waveOutPause before calling waveOutWrite. When you want to begin playing the waveform, call waveOutRestart.

To stop a waveform from playing, use the waveOutReset function. This function differs from waveOutPause in that it also marks all pending data blocks as being done. You can not use waveOutRestart to restart a device that has been stopped by using waveOutReset. You must use waveOutWrite to send the first data block to resume playback on the device.

+$#A Closing Waveform Output Devices

After waveform playback is complete, call the waveOutClose function to close the waveform device. If waveOutClose is called while a waveform is playing, the close operation fails and the function returns an error code indicating that the device was not closed. If you don't want to wait for playback to end before closing the device, call the waveOutReset function before closing. This stops playback and allows the device to be closed. Be sure to clean up the preparation on all data blocks before closing the waveform output device.

+$#A Changing Waveform Playback Volume

Windows provides the following functions to query and set the volume level of waveform output devices.

Function | Description
waveOutGetVolume | Gets the volume level of the specified waveform device.
waveOutSetVolume | Sets the volume level of the specified waveform device.

Not all waveform devices support volume changes. Some devices support individual volume control on both the left and right channels. For information about how to determine the volume-control capabilities of waveform devices, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure that your application opens an audio device before changing its volume. Also, be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value of which the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left (or mono) channel.

For devices that don't support left- and right-channel volume control, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Volume-level values range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same when increasing the volume level from 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

The waveOutGetVolume function returns the volume as a doubleword value at an address specified by a pointer parameter.

+$#A Changing Pitch and Playback Rate

Some waveform output devices can vary the pitch and the playback rate of waveform data. Not all waveform devices support pitch and playback-rate changes. For information about how to determine if a particular waveform device supports pitch and playback-rate changes, see Determining Capabilities of Waveform Output Devices.

Following are the differences between changing pitch and playback rate:

• Changing the playback rate is performed by the device driver and does not require specialized hardware. The sample rate is not changed, but the driver interpolates by skipping or synthesizing samples. For example, if the playback rate is changed by a factor of two, the driver skips every other sample.

• Changing the pitch requires specialized hardware. The playback rate and sample rate are not changed.

Windows provides the following functions to query and set waveform pitch and playback rates.

Function | Description
waveOutGetPitch | Gets the pitch for the specified waveform output device.
waveOutGetPlaybackRate | Gets the playback rate for the specified waveform output device.
waveOutSetPitch | Sets the pitch for the specified waveform output device.
waveOutSetPlaybackRate | Sets the playback rate for the specified waveform output device.

The pitch and playback rates are changed by a factor specified with a fixed-point number packed into a doubleword value. The high-order 16 bits specify the integer part, and the low-order 16 bits specify the fractional part of the number. For example, the value 1.5 is represented as 0x00018000L. The value 0.75 is represented as 0x0000C000L. A value of 1.0 (0x00010000) means the pitch or playback rate is unchanged.

+$#A Recording Low-Level Waveform Audio

If the MCI waveform-recording services don't meet the needs of your application, you can handle waveform recording using the low-level waveform services.

+$#A Waveform Input Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the audio functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H defines the following data types for waveform-input functions.

Type | Description
HWAVEIN | Handle of an open waveform input device.
WAVEINCAPS | Data structure used to inquire about the capabilities of a particular waveform input device.
WAVEFORMAT | Structure that specifies the data formats supported by a particular waveform input device. This structure is also used for waveform output devices.
WAVEHDR | Structure that is a header for a block of waveform input data. This structure is also used for waveform output devices.

+$#A Querying Waveform Input Devices

Before recording a waveform, you should call the waveInGetDevCaps function to determine the waveform input capabilities of the system. This function takes a pointer to a WAVEINCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the WAVEINCAPS structure provides information about the standard waveform formats that the device supports.

+$#A Opening Waveform Input Devices

Use the waveInOpen function to open a waveform input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Selecting a Waveform Input Device

Some multimedia computers will have multiple waveform input devices. Unless you know you want to open a specific waveform input device in a system, you should use the WAVE_MAPPER constant for the device identifier when you open a device. The waveOutOpen function will choose the device in the system best able to record in the given data format.

+$#A Managing Waveform Recording

Once you open a waveform input device, you can begin recording waveform data. Waveform data is recorded into application-supplied buffers specified by a WAVEHDR structure. This is the same structure used for waveform playback described in Writing Waveform Data. Memory for the WAVEHDR structure and its accompanying data buffer must be allocated and prepared, as shown in Allocating and Preparing Audio Data Blocks.

Windows provides the following functions to manage waveform recording:

-
Function | Description
waveInAddBuffer | Sends a buffer to the device driver so it can be filled with recorded waveform data.
waveInReset | Stops waveform recording and marks all pending buffers as done.
waveInStart | Starts waveform recording.
waveInStop | Stops waveform recording.

Use the waveInAddBuffer function to send data buffers to the device driver. As the buffers are filled with recorded waveform data, the application is notified with either a window message or with a callback message, depending on the flag specified when the device was opened.

Use the waveInStart function to begin recording. Before beginning to record, send at least one buffer to the driver or incoming data may be lost. To stop waveform recording, use the waveInStop function.

Before closing the device by using the waveOutClose function, call the waveOutReset function to mark any pending data blocks as being done.

+$#A Using Window Messages to Manage Waveform Recording

The following messages can be sent to a window-procedure function for managing waveform recording.

Message | Description
MM_WIM_CLOSE | Sent when the device is closed using the waveInClose function.
MM_WIM_DATA | Sent when the device driver is finished with a data buffer sent using the waveInAddBuffer function.
MM_WIM_OPEN | Sent when the device is opened using the waveInOpen function.

There is a wParam and lParam parameter associated with each of these messages. The wParam parameter always specifies a handle of the open waveform device. The lParam parameter is unused for the MM_WIM_CLOSE and MM_WIM_OPEN messages.

For the MM_WIM_DATA message, lParam specifies a pointer to a WAVEHDR structure that identifies the data buffer. This data buffer may not be completely filled with waveform data, because recording can stop before the buffer is filled. Use the dwBytesRecorded member of the WAVEHDR structure to determine the amount of valid data present in the buffer.

The most useful message is MM_WIM_DATA. Unless you need to allocate memory or initialize variables, you probably don't need to use the MM_WIM_OPEN and MM_WIM_CLOSE messages. When the device driver is finished with a data block, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks.

+$# Using a Low-Level Callback Function to Manage Waveform Recording

The syntax of the low-level callback function for waveform input devices is as follows.

void CALLBACK waveInCallback(hWaveIn, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of waveform input callback functions.

Message | Description
WIM_CLOSE | Sent when the device is closed using the waveInClose function.
WIM_OPEN | Sent when the device is opened using the waveInOpen function.

These messages resemble messages sent to window-procedure functions, but their parameters are different. A handle of an open waveform device is passed as a parameter to the callback function, along with the doubleword value of instance data that was passed using waveInOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the WIM_CLOSE and WIM_OPEN messages, these parameters are not used. For the WIM_DONE message, dwParam1 specifies a pointer to a WAVEHDR structure identifying the completed data block and dwParam2 is not used.

After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions of low-level audio callback functions, it is better not to do this within the callback function. It is better to set events or semaphores and do this outside the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages.

+$#A Playing MIDI Audio

To play MIDI files, use the MCI sequencer. If the MCI sequencer services don't meet the needs of your application, you can manage MIDI playback using the low-level MIDI services.

+$#A Low-Level Audio MIDI Output Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all low-level audio functions. You must include this header file in any source module that uses these functions. MMSYSTEM.H defines the following data types for low-level MIDI output function:

Type | Description
HMIDIOUT | Handle of a MIDI output device.
MIDIHDR | Structure is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIOUTCAPS | Structure used to inquire about the capabilities of a particular MIDI output device.

+$#A Querying Low-Level MIDI Output Devices

Before playing MIDI audio, you should call the midiOutGetDevCaps function to determine the capabilities of the MIDI output hardware present in the system. This function takes a pointer to a MIDIOUTCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver. In addition, the MIDIOUTCAPS structure provides information on the device technology, the number of voices and notes supported, the MIDI channels that the device responds to, and features supported by the driver.

+$#A Determining the Technology of the Device

MIDI output devices can be either internal synthesizers or external MIDI output ports. The wTechnology member specifies the technology of the device. MMSYSTEM.H defines the following flags to identify device technology.

Flag | Description
MOD_MIDIPORT | The device is an external MIDI output port.
MOD_SQSYNTH | Device is an internal square-wave synthesizer.
MOD_FMSYNTH | Device is an internal FM synthesizer.
MOD_SYNTH | Device is an internal synthesizer (generic).
MOD_MAPPER | Device is the MIDI Mapper.

+$#A Determining Capabilities of Internal Synthesizers

If the device is an internal synthesizer, additional device information is available in the wVoices, wNotes, and wChannelMask members of the MIDIOUTCAPS structure. If the device is an external output port, these members are unused.

The wVoices member specifies the number of voices the device supports. Each voice can have a different sound or timbre. Voices are differentiated by MIDI channel. For example, a four-voice synthesizer uses four MIDI channels. The wNotes member specifies the polyphony of the device ⎯ the maximum number of notes that can be played simultaneously. The wChannelMask member is a bit representation of the MIDI channels that the device responds to. For example, if the device responds to the first eight MIDI channels, wChannelMask is 0x00FF.

The dwSupport member of the MIDIOUTCAPS structure indicates whether the device driver supports volume changes and patch caching. MMSYSTEM.H defines the following flags for the dwSupport member.

Flag | Description
MIDICAPS_VOLUME | Indicates the driver supports volume control.
MIDICAPS_LRVOLUME | Indicates the driver supports individual volume control for the left and right channels.
MIDICAPS_CACHE | Indicates the driver supports patch caching.

Volume changes are only supported by internal synthesizer devices. External MIDI output ports don't support volume changes. For information about changing volume, see Changing Internal MIDI Synthesizer Volume.

+$#A Opening Low-Level Audio MIDI Output Devices

Use the midiOutOpen function to open a MIDI output device for playback. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Sending MIDI Messages

Once you open a MIDI output device, you can begin sending it MIDI messages by using the following functions.

Function | Description
midiOutLongMsg | Sends a buffer of MIDI data to the specified MIDI output device. Use this function to send multiple MIDI events, including system-exclusive messages, to a MIDI device.
midiOutShortMsg | Sends a MIDI message to a specified MIDI output device. This function can use running status by omitting the status byte.
midiOutReset | Turns off all notes on all channels for a specified MIDI output device. Any pending system-exclusive buffers are marked as done and returned to the application.

Use midiOutShortMsg to send any MIDI message (except for system-exclusive messages). This function takes an HMIDIOUT parameter specifying the MIDI output device to which to send the message, and a doubleword value for the MIDI message. The message is packed into the doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte | Not used
 | Low-order byte | MIDI data 2 (optional)
Low-order word | High-order Byte | MIDI data 1 (optional)
 | Low-order byte | MIDI status

The two MIDI data bytes are optional, depending on the MIDI status byte. The following example uses midiOutShortMsg to send a given MIDI event to a given MIDI output device.

/* Sends a given MIDI event to the given output device */

UINT sendMIDIEvent(hMidiOut, bStatus, bData1, bData2)

HMIDIOUT hMidiOut; // handle of the output device

BYTE bStatus; // MIDI status byte

BYTE bData1; // first MIDI data byte

BYTE bData2; // second MIDI data byte

{

union {

DWORD dwData;

BYTE bData[4];

} u;

/* Construct the MIDI message */

u.bData[0] = bStatus;

u.bData[1] = bData1;

u.bData[2] = bData2;

u.bData[3] = 0;

/* Send the message */

return midiOutShortMsg(hMidiOut, u.dwData);

}

Note MIDI output drivers are not required to verify data before sending it to an output port. It is up to applications to ensure that only valid data is sent using the midiOutShortMsg function.

+$#A Sending Buffered Messages

MIDI system-exclusive messages are the only MIDI messages that will not fit into a single doubleword value. System exclusive messages can be any length. Windows provides the midiOutLongMsg function for sending one or more messages, including system-exclusive messages, to MIDI output devices.

Use the MIDIHDR structure to specify MIDI system-exclusive data blocks. This structure contains a pointer to a locked data block, the data-block length, and assorted flags.

Memory for the MIDIHDR structure and the data block pointed to by lpData must be allocated and prepared, as shown in "Allocating and Preparing Audio Data Blocks."

After you send a system-exclusive data block using midiOutLongMsg, you must wait until the device driver is finished with the data block before freeing it. If you are sending multiple data blocks, you must monitor the completion of each data block so you know when to send additional blocks. (If your application uses a callback function or callback window, it will receive the MOM_DONE message when the driver finishes with the data block.) For information on different techniques for monitoring data-block completion, see Managing Audio Data Blocks.

Note Any MIDI status byte other than a system-real-time message will terminate a system-exclusive message. If you are using multiple data blocks to send a single system-exclusive message, do not send any MIDI messages other than system-real-time messages between data blocks.

+$#A Using Window Messages to Manage Buffered Playback

The following messages can be sent to a window-procedure function for managing MIDI system-exclusive playback.

Message | Description
MM_MOM_CLOSE | Sent when the device is closed using the midiOutClose function.
MM_MOM_DONE | Sent when the device driver is finished with a data block sent using the midiOutLongMsg function.
MM_MOM_OPEN | Sent when the device is opened using the midiOutOpen function.

A wParam and lParam parameter is associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. For the MM_MOM_DONE message, lParam specifies a pointer to a MIDIHDR structure identifying the completed data block. The lParam parameter is unused for the MM_MOM_CLOSE and MM_MOM_OPEN messages.

The most useful message is the MM_MOM_DONE message. Unless you need to allocate memory or initialize variables, you probably don't need to process the MM_MOM_OPEN and MM_MOM_CLOSE messages. When playback of a data block is completed, you can clean up and free the data block as described in Allocating and Preparing Audio Data Blocks.

+$#A Using a Callback to Manage Buffered Playback

This syntax of the low-level callback function for MIDI output devices is as follows.

void CALLBACK midiOutCallback(hMidiOut, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent to the wMsg parameter of MIDI output callback functions.

Message | Description
MOM_CLOSE | Sent when the device is closed using the midiOutClose function.
MOM_DONE | Sent when the device driver is finished with a data block sent using the midiOutLongMsg function.
MOM_OPEN | Sent when the device is opened using the midiOutOpen function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback function along with the doubleword value of instance data passed using midiOutOpen.

The callback function has two message-specific parameters: dwParam1 and dwParam2. For the MOM_OPEN and MOM_CLOSE messages, these parameters are not used. For the MOM_DONE message, dwParam1 specifies a pointer to a MIDIHDR structure identifying the completed data block and dwParam2 is not used.

After the driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. For details about the suggested restrictions on using callback functions, see Using a Callback Function to Process Driver Messages.

+$#A Sending MIDI Messages Using Running-Status

The MIDI 1.0 Specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. You can send MIDI messages using running status by using the midiOutShortMsg function by packing the message into a doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte | Not used
 | Low-order byte | Not used
Low-order word | High-order Byte | MIDI data 2 (optional)
 | Low-order byte | MIDI data 1

+$#A Resetting Low-Level Audio MIDI Output

The midiOutReset function will turn off all notes, and mark any pending system-exclusive buffers as done and return them to the application. It may be useful in an application that uses MIDI output to provide the user with the ability to reset MIDI output.

Note Terminating a system-exclusive message without sending an EOX (end-of-exclusive) byte may cause problems for the receiving device. The midiOutReset function does not send an EOX byte when it terminates a system-exclusive message; applications are responsible for doing this.

+$#A Changing Low-Level Audio Internal MIDI Synthesizer Volume

Windows provides the following functions to query and set the volume level of internal MIDI synthesizer devices.

Function | Description
midiOutGetVolume | Gets the volume level of the specified internal MIDI synthesizer device.
midiOutSetVolume | Sets the volume level of the specified internal MIDI synthesizer device.

Not all MIDI output devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume change, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, be sure your application opens an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restore the volume level to its previous level as soon as possible.

Volume is specified in a doubleword value; the high-order 16 bits specify the relative volume of the right channel, and the low-order 16 bits specify the relative volume of the left channel.

For devices that don't support individual volume changes on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored. Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. The perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

When querying volume levels using midiOutGetVolume, the volume is returned in a doubleword location specified by a pointer parameter.

+$# Preloading Low-Level Audio Patches With Internal MIDI Synthesizers

Some internal MIDI synthesizer devices can't keep all of their patches loaded simultaneously. These devices must preload their patch data.

Windows provides the following functions to request that a synthesizer preload and cache specified patches.

Function | Description
midiOutCachePatches | Requests that an internal MIDI synthesizer device preload and cache specified melodic patches.
midiOutCacheDrumPatches | Requests that an internal MIDI synthesizer device preload and cache specified key-based percussion patches.

The midiOutCachePatches function takes a pointer to a PATCHARRAY to indicate the patches to be cached.

Each element in the array corresponds to a patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if patch number 0 is used by physical MIDI channels 0 and 8, set element 0 of the array to 0x0101.

The midiOutCacheDrumPatches function takes a pointer to a KEYARRAY to indicate the key-based percussion patches to be cached.

Each element in the array corresponds to a key-based percussion patch with each of the 16 bits representing one of the 16 MIDI channels. Bits are set for each of the channels that use that particular patch. For example, if the percussion patch for key number 60 is used by physical MIDI channels 9 and 15, set element 60 of the array to 0x8200.

+$#A Using the MIDI Mapper with Low-Level MIDI Functions

The MIDI Mapper provides standard patch services for device-independent playback of MIDI files. Applications that use MIDI files for audio should use the MIDI Mapper. For information about the MIDI Mapper and about authoring device-independent MIDI files, see High-Level Audio.

To use the MIDI Mapper, open it by using the midiOutOpen function with the wDeviceID parameter set to the constant MIDIMAPPER. You can then send MIDI messages to the MIDI Mapper by using the midiOutShortMsg or midiOutLongMsg function.

+$#A Recording Low-Level MIDI Audio

To record MIDI audio data, you must use low-level MIDI input functions. MCI does not currently provide a device handler for recording MIDI audio.

+$#A Low-Level Audio MIDI Input Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all of the low-level audio functions. MMSYSTEM.H defines the following data types for low-level MIDI input functions.

Type | Description
HMIDIIN | A handle of a MIDI input device.
MIDIHDR | A structure that is a header for a block of MIDI system-exclusive data. This structure is used for input as well as output.
MIDIINCAPS | A structure used to inquire about the capabilities of a MIDI input device.

+$#A Querying MIDI Input Devices for Low-Level Audio

Before recording MIDI audio, you should call the midiInGetDevCaps function to determine the capabilities of the MIDI input hardware present in the system. This function takes a pointer to a MIDIINCAPS structure that it fills with information about the capabilities of a given device. This information includes the manufacturer and product identifiers, a product name for the device, and the version number of the device driver.

+$#A Opening Low-Level MIDI Input Devices

Use the midiInOpen function to open a MIDI input device for recording. This function opens the device associated with the specified device identifier and returns a handle of the open device by writing the handle of a specified memory location.

+$#A Managing MIDI Recording of Low-Level Audio

Once you open a MIDI input device, you can begin recording MIDI data. Win32 provides the following functions for managing MIDI recording.

Function | Description
midiInAddBuffer | Sends a buffer to the device driver so it can be filled with recorded MIDI data.
midiInReset | Stops MIDI recording and marks all pending buffers as done.
midiInStart | Starts MIDI recording and resets the time stamp to zero.
midiInStop | Stops MIDI recording.

Use the midiInAddBuffer function to send data buffers to the device driver for recording system-exclusive messages. Before you use midiInAddBuffer, you must allocate and prepare memory for the MIDIHDR data structure and the data block pointed to by the lpData member. For information about allocating and preparing this memory, see Allocating and Preparing Audio Data Blocks.

Use the midiInStart function to begin recording. To record system-exclusive messages, send at least one buffer to the driver with midiInAddBuffer before starting to record. As the buffers are filled with recorded data, the application is notified by one of the techniques discussed in "Managing Audio Data Blocks."

To stop recording, use the midiInStop function. Before closing the device by using the midiInClose function, call midiInReset to mark any pending data blocks as being done. Before freeing the memory used by a data block, call the midiInUnprepareHeader function to clean up the preparation performed previously.

You must use either a window-procedure function or a low-level callback function to receive MIDI data. If you want time-stamped data, you must use a low-level callback function.

To record system-exclusive messages, you must supply the device driver with data buffers. These buffers are specified by a MIDIHDR structure. This is the same structure used for MIDI buffered playback as described in "Sending Buffered Messages." Memory for the MIDIHDR structure and its accompanying data buffer must be allocated and prepared as shown in Allocating and Preparing Audio Data Blocks.

+$#A Using Window Messages to Manage MIDI Recording

The following messages can be sent to a window-procedure function for managing MIDI recording.

Message | Description
MM_MIM_CLOSE | Sent when the device is closed using the midiInClose function.
MM_MIM_DATA | Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_ERROR | Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MM_MIM_LONGDATA | Sent when either a complete MIDI system-exclusive message is received, or when a data buffer sent using the midiInAddBuffer function is filled with system-exclusive data.
MM_MIM_LONGERROR | Sent when an invalid MIDI system-exclusive message is received.
MM_MIM_OPEN | Sent when the device is opened using the midiInOpen function.

A wParam and an lParam parameter are associated with each of these messages. The wParam parameter always specifies a handle of the open MIDI device. The lParam parameter is unused for the MM_MIM_CLOSE and MM_MIM_OPEN messages.

For the MM_MIM_DATA message, lParam specifies the received MIDI data. This data is packed into a doubleword value as follows.

Word | Byte | Contents
High-order word | High-order byte |
 | Low-order byte | MIDI data 2 (optional)
Low-order word | High-order Byte | MIDI data 1 (optional)
 | Low-order byte | MIDI status

For the MM_MIM_LONGDATA message, lParam specifies a pointer to a MIDIHDR structure that identifies the data buffer for system-exclusive messages. The data buffer might not be completely filled ⎯ you usually don't know the size of the system-exclusive messages before recording them, so you must allocate a buffer large enough for the largest expected message. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.

+$# Using a Low-Level Callback Function to Manage MIDI Recording

This syntax of the low-level callback function for MIDI input devices is as follows:

void CALLBACK midiInCallback(hMidiIn, wMsg,
dwInstance, dwParam1, dwParam2)

The following messages can be sent in the wMsg parameter of MIDI input callback functions.

Message | Description
MIM_CLOSE | Sent when the device is closed using the midiInClose function.
MIM_DATA | Sent when a complete MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_ERROR | Sent when an invalid MIDI message is received (this message is used for all MIDI messages except system-exclusive messages).
MIM_LONGERROR | Sent when an invalid MIDI system-exclusive message is received.
MIM_LONGDATA | Sent when either a complete MIDI system-exclusive message is received, or when a data buffer is filled with system-exclusive data.
MIM_OPEN | Sent when the device is opened using the midiInOpen function.

These messages are similar to those sent to window-procedure functions, but the parameters are different. A handle of the open MIDI device is passed as a parameter to the callback, along with the doubleword value of instance data that was passed using the midiInOpen value.

The callback has two message-specific parameters: dwParam1 and dwParam2. For the MIM_OPEN and MIM_CLOSE messages, these parameters are unused.

For the MIM_DATA message, dwParam1 specifies the received MIDI data and dwParam2 specifies a time stamp for the data. The data is packed into a doubleword value, as shown in the previous section on using window messages.

For the MIM_LONGDATA message, dwParam1 specifies a pointer to a MIDIHDR structure that identifies the data buffer for system-exclusive messages. As with the MIM_DATA message, dwParam2 specifies a time stamp for the data. The data buffer may not be completely filled. Use the dwBytesRecorded member of the MIDIHDR structure to determine the amount of valid data present in the buffer.

After the device driver is finished with a data block, you can clean up and free the data block. Because of the suggested restrictions on low-level audio callback functions, it is better not to do this from within the callback function. It is better to set events or semaphores and do this outside of the callback function. See Using a Callback Function to Process Driver Messages for details about the suggested restrictions on using callback functions.

+$#A Receiving Time-Stamped MIDI Messages

Because of the delay between when the device driver receives a MIDI message and the time the application receives the message, MIDI input device drivers time-stamp the MIDI message with the time the message was received. MIDI time stamps are defined as the time the first byte of the message was received and are specified in milliseconds. The midiInStart function resets the time stamps for a device to zero.

As stated earlier, to receive time stamps with MIDI input, you must use a low-level callback function. The dwParam2 parameter of the callback function specifies the time stamp for data associated with the MIM_DATA and MIM_LONGDATA messages.

+$#A Receiving Running-Status Messages

The MIDI 1.0 specification allows the use of running-status when a message has the same status byte as the previous message. When running status is used, the status byte of subsequent messages can be omitted. All MIDI input device drivers are required to expand messages using running status to complete messages ⎯ you always receive complete MIDI messages from a MIDI input device driver.

+$#A Auxiliary Audio Devices

Auxiliary audio devices are audio devices whose output is mixed with the MIDI and waveform output devices in a multimedia computer. An example of an auxiliary audio device is the compact disc audio output from a CD-ROM drive.

Control for auxiliary audio devices is provided by a software-controlled audio mixer. The mixer can reside on the motherboard of a multimedia computer, or it can be on an add-in sound card. The following illustration shows the conceptual audio-signal routing in a multimedia computer.

In this multimedia computer, there are two auxiliary audio devices: the CD-ROM and the external audio input. The external audio input is an input jack that allows the user to connect other audio devices.

+$#A Querying Low-Level Auxiliary Audio Devices

Not all multimedia systems have auxiliary audio support. You can use the auxGetNumDevs function to determine the number of controllable auxiliary devices present in a system.

To get information on a particular auxiliary audio device, use the auxGetDevCaps function. This function takes a pointer to an AUXCAPS structure that it fills with information about the capabilities of a given device.

+$#A Determining Auxiliary Audio Device Types

The MMSYSTEM.H header file defines the following flags for the wTechnology member of the AUXCAPS structure to determine the device type of an auxiliary audio device.

Flag | Description
AUXCAPS_CDAUDIO | The device is an internal CD-ROM drive.
AUXCAPS_AUXIN | The device is an auxiliary audio input jack.

+$#A Determining Capabilities of Auxiliary Audio Devices

The dwSupport member of the AUXCAPS structure indicates whether the device driver supports volume changes. The MMSYSTEM.H header file defines the following flags for the dwSupport member.

Flag | Description
AUXCAPS_VOLUME | Volume-control support
AUXCAPS_LRVOLUME | Individual volume-control support for both the left and right channels

+$#A Changing Auxiliary Audio-Device Volume

Windows provides the following functions to query and set the volume for auxiliary audio devices.

Function | Description
auxGetVolume | Gets the volume level of the specified auxiliary audio device.
auxSetVolume | Sets the volume level of the specified auxiliary audio device.

Not all auxiliary audio devices support volume changes. Some devices can support individual volume changes on both the left and the right channels. For information about how to determine whether a particular device supports volume changes, see Determining Capabilities of Waveform Output Devices.

Note Unless your application is designed to be a master volume-control application providing the user with volume control for all audio devices in a system, you should open an audio device before changing its volume. Also be sure your application queries the volume level before changing it and restores the volume level to its previous level as soon as possible.

The volume level is specified (as it was in the waveform and MIDI volume-control functions) in a doubleword value with the high-order 16 bits specifying the relative volume of the right channel and the low-order 16 bits specifying the relative volume of the left channel. For devices that don't support individual volume control on both the left and right channels, the low-order 16 bits specify the volume level, and the high-order 16 bits are ignored.

Values for the volume level range from 0x0 (silence) to 0xFFFF (maximum volume) and are interpreted logarithmically. This means the perceived volume increase is the same from volume level 0x5000 to 0x6000 as it is from 0x4000 to 0x5000.

When querying volume levels with the auxGetVolume function, the volume is returned in a doubleword location specified by a pointer parameter.

+$#A Audio Clipboard Formats

In addition to the standard and nonstandard waveform formats previously presented in this section, there are two Clipboard formats that can be used to represent audio data: CF_WAVE and CF_RIFF. Use the CF_WAVE format to represent data in one of the standard formats, such as 11 kHz or 22 kHz PCM. Use the CF_RIFF format to represent more complex data formats that cannot be represented as standard wave files.

+$#A Low-Level Audio Functions

Following are the functions used with low-level audio services.

auxGetDevCaps
auxGetNumDevs
auxGetVolume
auxOutMessage
auxSetVolume
midiInAddBuffer
midiInClose
midiInGetDevCaps
midiInGetErrorText
midiInGetID
midiInGetNumDevs
midiInMessage
midiInOpen
midiInPrepareHeader
midiInReset
midiInStart
midiInStop
midiInUnprepareHeader
midiOutCacheDrumPatches
midiOutCachePatches
midiOutClose
midiOutGetDevCaps
midiOutGetErrorText
midiOutGetID
midiOutGetNumDevs
midiOutGetVolume
midiOutLongMsg
midiOutMessage
midiOutOpen
midiOutPrepareHeader
midiOutReset
midiOutSetVolume
midiOutShortMsg
midiOutUnprepareHeader
MidiProc
waveInAddBuffer
waveInClose
waveInGetDevCaps
waveInGetErrorText
waveInGetID
waveInGetNumDevs
waveInGetPosition
waveInMessage
waveInOpen
waveInPrepareHeader
waveInReset
waveInStart
waveInStop
waveInUnprepareHeader
waveOutBreakLoop
waveOutClose
waveOutGetDevCaps
waveOutGetErrorText
waveOutGetID
waveOutGetNumDevs
waveOutGetPitch
waveOutGetPlaybackRate
waveOutGetPosition
waveOutGetVolume
waveOutMessage
waveOutOpen
waveOutPause
waveOutPrepareHeader
waveOutReset
waveOutRestart
waveOutSetPitch
waveOutSetPlaybackRate
waveOutSetVolume
waveOutUnprepareHeader
waveOutWrite
WaveProc

+$#A Media Control Interface

The Media Control Interface (MCI) provides applications written for Microsoft® Windows® with device-independent capabilities for controlling media devices such as audio hardware, videodisc players, and animation players. This overview presents an introduction to MCI and shows how to begin using it in Windows-based applications.

+$#A About the Media Control Interface

MCI provides a device-independent, extensible interface for controlling virtually any type of media device. Because of the high level of device independence provided by MCI, you are encouraged to use MCI rather than low-level functions to access the multimedia capabilities of Windows.

Click on a topic below to view it:

• The Architecture of MCI

• The MCI Command Set

• MCI Programming Interfaces

• The Command-Message interface

• The Command-String Interface (an introduction)

For more information about using MCI to control different types of audio devices, see High-Level Audio.

+$#A The Architecture of MCI

To provide extensibility, MCI is designed around an architecture that uses special MCI device drivers to interpret and execute MCI commands.

MCI device drivers can control media hardware directly or through the low-level multimedia functions provided by Windows. Most commonly used devices, such as waveform audio and Musical Instrument Digital Interface (MIDI) devices, are controlled through the low-level multimedia functions.

+$#A The MCI Programming Interfaces

MCI provides two programming interfaces: command string and command message.

+$#A The Command-String Interface

The command-string interface allows you to use English-language commands to communicate with MCI devices. For example, the following command string plays a WAVE file named TIMPANI.WAV.

play timpani.wav

The command-string interface is designed for use with high-level programming and authoring environments such as Microsoft® Visual Basic® and Asymetrix® ToolBook®. Use the command-string interface for applications that provide a text-based interface to let users control MCI devices.

+$#A The Command-Message Interface

The command-message interface uses a message-passing paradigm to communicate with MCI devices. The following example performs the same operation as the command-string example in The Command-String Interface.

WORD wDeviceID;

MCI_PLAY_PARMS mciPlayParms;

mciSendCommand(wDeviceID, /* device identifier */

MCI_PLAY, /* command message */

0, /* flags */

(DWORD) (LPVOID) &mciPlayParms); /* structure */

The command-message interface is designed to be used by applications requiring a C-language interface to control multimedia devices. Use the command-message interface for applications that directly control multimedia devices.

+$#A The MCI Command Set

The MCI command set provides a generic core set of commands to control different types of media devices. For example, MCI uses the same command to begin playback of a waveform audio file, a videodisc track, and an animation sequence. Some devices have unique capabilities, such as the capability of an animation player to use a frame-based time format. For such devices, MCI provides extended commands that are unique to a particular type of device.

The commands in the command-string interface provide a good overview of the MCI command set. Each of these commands is represented by a similar command message in the command-message interface. For example, the close command string is the equivalent of the MCI_CLOSE command message.

Following are some commonly used commands.

Command | Description
capability | Requests information about the capabilities of a device.
close | Closes a device after it has been used.
info | Requests information about a device (for example, a description of the hardware associated with the device).
open | Opens and initializes a device.
pause | Pauses playing or recording on a device.
play | Begins playing on a device.
record | Begins recording on a device.
resume | Resumes playing or recording on a paused device.
seek | Changes the current position in the media.
set | Changes control settings, such as the time format the device is using.
status | Requests information about the status of a device (for example, whether the device is playing or paused).
stop | Stops playing or recording on a device.

+$#A About MCI Devices

MCI device drivers can be classified as either simple devices or compound devices. Simple devices do not require a data file for playback. Videodisc players and compact disc (CD) audio players are examples of simple devices. Compound devices require a data file for playback. MIDI sequencers and waveform audio players are examples of compound devices. The data file associated with a compound device is known as a device element. Examples of device elements are MIDI files and WAVE files.

+$#A Standard MCI Device Types

A device type identifies a class of MCI devices that responds to a common set of commands. Following are the currently defined device types.

Device type | Description
animation | Animation device
cdaudio | CD audio player
dat | Digital audio tape player
digitalvideo | Digital video in a window (not based on graphics device interface)
other | Undefined MCI device
overlay | Overlay device (analog video in a window)
scanner | Image scanner
sequencer | MIDI sequencer
videodisc | Videodisc player
waveaudio | Audio device that plays digitized waveform files

+$#A MCI Device Names

For any given device type, there might be several MCI drivers that share the command set but operate on different data formats. For example, for animation devices there are several MCI drivers that use the same command set but use different file formats. To uniquely identify an MCI driver, MCI uses device names.

Device names are identified in the [mci32] section of the registry. This section identifies all MCI device drivers to Windows. The following is part of a typical [mci] section.

[mci32]

WaveAudio=mciwave.dll

Sequencer=mciseq.dll

AVIVideo=mciavi.dll

CDAudio=mcicda.dll

The key name (on the left side of the equal sign) is the device name. The value corresponding to the key name (on the right side of the equal sign) identifies the filename of the MCI driver. The device name is often the same as the device type for the driver, as is the case for the WaveAudio, Sequencer, and CDAudio devices in the preceding example. The AVIVideo device is a digital video device, but it uses a unique device name.

If an MCI driver is installed using a device name that already exists in the [mci32] section, Windows appends an integer to the device name of the new driver, creating a unique device name. In the preceding example, a driver installed using the cdaudio device name would be assigned device name cdaudio1. A subsequent cdaudio device would be assigned device name cdaudio2.

+$#A Opening MCI Devices

Before using an MCI device, you must initialize it by opening it. Opening a device loads its driver into memory (if it isn't already loaded) and establishes a device identifier to identify the device in subsequent commands. (The device identifier is not used in the command-string interface.) There are several ways to specify the device you want to open:

• With simple devices, you can open the device by specifying the device name.

• With compound devices, you can open the device by specifying the device name, the device element, or both.

For example, the following command string opens a CD audio device (a simple device) by specifying the device name.

open cdaudio

The next command string opens a waveform audio device (a compound device) by specifying the device name and a device element.

open bells.wav type waveaudio

You can also open a compound device by specifying only the device element, as shown in the following example.

open bells.wav

If you specify only a device element when opening a compound device, MCI uses the filename extension of the device element filename to determine which device to open. The registry contains an [mci extensions] section that associates filename extensions and corresponding MCI device types. The following is part of an [mci extensions] section.

[mci extensions]

Wav=WaveAudio

Mid=Sequencer

rmi=Sequencer

avi=avivideo

+$#A Using the Command-Message Interface

Applications that require a C-language interface to MCI should use the command-message interface. The following topics provide background and detailed information about using this interface.

About Command Messages
Summary of MCI Command Messages
Sending MCI Command Messages
Opening an MCI Device
Closing an MCI Device
Using the Wait and Notify Flags
Using MCI_SYSINFO to Obtain MCI System Information
About the Command-String Interface
Sending MCI Command Strings
Additional Information About Command Strings

+$#A About Command Messages

MCI command messages consist of the following three elements:

• A constant message value

• A structure (parameter block) specifying additional parameters for the command

• A set of flags specifying options for the command and validating members in the structure

Commands are sent by using the mciSendCommand function, which has parameters for the message, flags, and a pointer to the structure. For more information about the mciSendCommand function, see Sending MCI Command Messages.

+$#A Command-Message Example

For an example of an MCI command message, consider MCI_PLAY. The MMSYSTEM.H header file defines the MCI_PLAY command message, the MCI_NOTIFY, MCI_WAIT, MCI_FROM, and MCI_TO flags, and the MCI_PLAY_PARMS structure. The MCI_PLAY_PARMS structure has the following form.

typedef struct {

DWORD dwCallback;

DWORD dwFrom;

DWORD dwTo;

} MCI_PLAY_PARMS;

The MCI_WAIT and MCI_NOTIFY flags are option flags. MCI_WAIT tells the driver to wait until the command is complete before returning control to the application. MCI_NOTIFY tells the driver to notify the application when the command is complete. For more information on using these flags, see Using the Wait and Notify Flags.

The MCI_FROM and MCI_TO flags validate the dwFrom and dwTo members in the MCI_PLAY_PARMS structure. These members specify beginning and ending positions for the play operation. Both flags are optional. If either flag is specified, the driver takes the value from the corresponding member in the structure as a beginning or ending position; otherwise, the members are ignored.

Note Members in a structure are ignored unless corresponding flags are specified to validate the members.

+$#A Items in Query Commands

Two command messages that query devices for information, MCI_GETDEVCAPS and MCI_STATUS, extend the command-message paradigm by adding a dwItem member to the structure and defining item constants for the member. Item constants specify which item of information is being requested. For example, MMSYSTEM.H defines the MCI_STATUS_PARMS structure for the MCI_STATUS command message as follows.

typedef struct {

DWORD dwCallback;

DWORD dwReturn;

DWORD dwItem;

DWORD dwTrack;

} MCI_STATUS_PARMS;

If you use the MCI_STATUS_ITEM flag to validate the dwItem member, make sure that dwItem contains a constant defined by MMSYSTEM.H to indicate the exact status information requested. The MCI_STATUS command message uses the following constants for dwItem:

MCI_STATUS_CURRENT_TRACK
MCI_STATUS_LENGTH
MCI_STATUS_MEDIA_PRESENT
MCI_STATUS_MODE
MCI_STATUS_NUMBER_OF_TRACKS
MCI_STATUS_POSITION
MCI_STATUS_READY
MCI_STATUS_TIME_FORMAT

+$#A Summary of MCI Command Messages

MCI defines four classifications of command messages. The command messages and options in the following two classifications are defined as the minimum command set for any MCI driver:

• System command messages are handled directly by MCI rather than by the driver.

• Required command messages are handled by the driver. All drivers should support the required command messages and options.

Regardless of the specific driver you use, you should be able to assume that the command messages and options in the two preceding groups are available.

The command messages in the following two classifications are not supported by all drivers:

• Basic (or optional) command messages are used by some devices. If a device supports a basic command message, it must support a defined set of options for the command message.

• Extended command messages are specific to certain device types or drivers. They include new command messages (for example, the MCI_PUT and MCI_WHERE command messages for the animation device type) and extensions to existing command messages (for example, the MCI_ANIM_GETDEVCAPS_CAN_STRETCH option added to the MCI_STATUS command message for the animation device type).

If you need to use a basic or extended command message or option, you should query the device driver before trying to use the command message or option (that is, unless you are certain that the MCI driver you have used during development is the same one that will be available on the delivery system). The following tables summarize the specific command messages in each category.

MCI directly processes the following system command messages, rather than passing them to MCI device drivers.

Command | Description
MCI_BREAK | Sets a break key for an MCI device.
MCI_SYSINFO | Returns information about MCI devices.

Required command messages must be supported by all MCI device drivers. The following table lists the required command messages.

Command | Description
MCI_CLOSE | Closes the device.
MCI_GETDEVCAPS | Obtains the capabilities of a device.
MCI_INFO | Obtains textual information from a device.
MCI_OPEN | Initializes the device.
MCI_STATUS | Obtains status information from the device.

Following are the basic command messages. The use of these command messages by a device is optional.

Command | Description
MCI_LOAD | Loads data from a file.
MCI_PAUSE | Stops playing.
MCI_PLAY | Starts transmitting output data.
MCI_RECORD | Starts recording data.
MCI_RESUME | Resumes playing or recording on a paused device.
MCI_SAVE | Saves data to a file.
MCI_SEEK | Seeks forward or backward.
MCI_SET | Sets the operating state of the device.
MCI_STATUS | Obtains status information about the device. (Supplements the required MCI_STATUS command message with options for devices using linear media with identifiable positions.)
MCI_STOP | Stops playing or recording.

Some MCI devices have additional command messages or add options to existing command messages. Although some extended command messages apply only to a specific device driver, most of them apply to all drivers of a particular device type. For example, the sequencer command set extends the MCI_SET command message to add time formats that are needed by MIDI sequencers.

Unless you are certain that the specific MCI driver you use during development will be available on the delivery system, do not assume that the device supports the extended command messages or options. You can use the MCI_GETDEVCAPS command message to determine whether a specific feature is supported. Applications should be ready to handle an MCIERR_UNSUPPORTED_FUNCTION return value.

The following extended command messages are available with certain device types.

Command Message | Device types | Description
MCI_CUE | waveaudio | Prepares for playing or recording.
MCI_DELETE | waveaudio | Deletes a data segment from the media element.
MCI_ESCAPE | videodisc | Sends custom information to a device.
MCI_FREEZE | overlay | Disables video acquisition to the frame buffer.
MCI_PUT | animation overlay | Defines the source, destination, and frame windows.
MCI_REALIZE | animation | Tells the device to select and realize its palette into a display context of the displayed window.
MCI_SPIN | videodisc | Starts the disc spinning or stops the disc from spinning.
MCI_STEP | animation, videodisc | Steps the play one or more frames forward or reverse.
MCI_UNFREEZE | overlay | Enables the frame buffer to acquire video data.
MCI_UPDATE | animation | Repaints the current frame into the display context.
MCI_WHERE | animation, overlay | Obtains the rectangle specifying the source, destination, or frame area.
MCI_WINDOW | animation, overlay | Controls display window options such as caption text.

+$#A Sending MCI Command Messages

Windows provides the following functions to send command messages to devices and to query devices for error information.

Function | Description
mciSendCommand | Sends a command message to an MCI device.
mciGetErrorString | Returns the error string corresponding to an error number.

+$#A The MCI Device Identifier

MCI returns a device identifier when you open a device by using the MCI_OPEN command message. Use this identifier to identify the opened device when sending subsequent commands.

MMSYSTEM.H defines a special constant, MCI_ALL_DEVICE_ID, to indicate that a command message is to be sent to all of the MCI devices that an application has opened. You can use MCI_ALL_DEVICE_ID with any command message that does not return information. For instance, the following example closes all of the MCI devices that have been opened by an application.

DWORD dwReturn;

/*

* Closes all MCI devices opened by this application.

* Waits until devices are closed before returning.

*/

if (dwReturn = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE,

MCI_WAIT, NULL))

/* Error, unable to close all devices. */

return FALSE;

Note Although using the MCI_ALL_DEVICE_ID identifier is a convenient way to broadcast a command to all your devices, don't rely on it to synchronize devices; the timing between messages can vary.

+$#A Opening an MCI Device

Before using a device, you must initialize it by using the MCI_OPEN command message. The variations of this command message make it one of the most complex of all MCI commands.

+$#A The MCI_OPEN_PARMS Structure

Like all MCI command messages, MCI_OPEN has an associated structure. The default structure for MCI_OPEN is the MCI_OPEN_PARMS structure. Certain devices, such as waveaudio, animation, and overlay devices, have extended structures to accommodate additional optional parameters. Unless you need to use these additional parameters, you can use the MCI_OPEN_PARMS structure with any MCI device.

MCI uses the wDeviceID member of this structure to return the device identifier to the application. You should check the return value of the mciSendCommand function before using the device identifier, to be sure that it is valid. A nonzero return value indicates that an error occurred during the opening of the device process.

Note You can also determine the device identifier by specifying the device name in a call to the mciGetDeviceID function.

Other members used in opening a device correspond to the following flags for the command message.

Flag | Meaning
MCI_OPEN_ALIAS | The lpstrAlias member of the structure contains a pointer to a device alias.
MCI_OPEN_ELEMENT | The lpstrElementName member of the structure contains a pointer to the element name.
MCI_OPEN_SHAREABLE | The device or element was opened as shareable.
MCI_OPEN_TYPE | The lpstrDeviceType member of the structure contains a pointer to the device-type identifier.
MCI_OPEN_TYPE_ID | The lpstrDeviceType member of the structure contains an integer device-type identifier.

+$#A Opening Simple Devices

To open a simple device such as a CD audio or videodisc device, use the lpstrDeviceType member in the MCI_OPEN_PARMS structure to identify the device to open. There are three ways to identify devices:

• Specify a pointer to a null-terminated string containing the device name.

• Specify a device-type constant.

• Specify the actual name of the device driver.

Regardless of which method you use to identify the device, you must specify the MCI_OPEN_TYPE flag to validate the lpstrDeviceType member. For instance, the following example opens a CD audio device by specifying the device name.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/* Open a compact disc device by specifying the device name. */

mciOpenParms.lpstrDeviceType = "cdaudio";

if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying a device-type constant. To be opened by means of a device-type constant, device drivers must be installed in the [mci] section of the registry by using the following device types.

Device type | Device-type constant
animation | MCI_DEVTYPE_ANIMATION
cdaudio | MCI_DEVTYPE_CD_AUDIO
dat | MCI_DEVTYPE_DAT
digitalvideo | MCI_DEVTYPE_DIGITAL_VIDEO
other | MCI_DEVTYPE_OTHER
overlay | MCI_DEVTYPE_OVERLAY
scanner | MCI_DEVTYPE_SCANNER
sequencer | MCI_DEVTYPE_SEQUENCER
videodisc | MCI_DEVTYPE_VIDEODISC
waveaudio | MCI_DEVTYPE_WAVEFORM_AUDIO

If you open a device by specifying a device-type constant, you must specify the MCI_OPEN_TYPE_ID flag in addition to the MCI_OPEN_TYPE flag. The following example opens a CD audio device by specifying a device-type constant.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/*

* Open a compact disc device by

* specifying a device-type constant.

*/

mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

When you open a device by specifying a device-type constant, MCI uses the high-order word of the lpstrDeviceType member to distinguish between multiple occurrences of the device type in the registry.

For example, if the registry lists entries for waveaudio1 and waveaudio2, the following statement sets the lpstrDeviceType member to specify the device identified by waveaudio2.

LPSTR lpstrDeviceType;

lpstrDeviceType = (LPSTR)MAKELONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 2);

Similarly, to specify the device identified by waveaudio1, set the high-order word of lpstrDeviceType to 0 or 1. (Setting the high-order word to 0 tells MCI to use an unnumbered device or the lowest-numbered device.)

You can also open a device by specifying the filename of the device driver in the lpstrDeviceType member of the MCI_OPEN_PARMS structure. You don't need to specify the complete path or the filename extension ⎯ MCI assumes that drivers are located in the Windows system directory and have a filename extension of .DRV.

Note Opening MCI devices by specifying the filename of the device driver may be useful during development and testing. However, this technique is not recommended for applications because it makes them device dependent. Applications that open MCI devices by filename may not work in all systems.

+$#A Opening Compound Devices

There are three ways to open a compound device:

• Specify only the device (by using the device name, a device-type constant, or the filename of the device driver).

• Specify both the device and the device element.

• Specify only the device element.

The first method ⎯ specifying only the device ⎯ is useful for determining the capabilities of a device by using the MCI_GETDEVCAPS command message. To actually control a media device, you must specify a device element when you open the device.

To specify a device element, fill the lpstrElementName member of the MCI_OPEN_PARMS structure with a pointer to a null-terminated string containing the filename of the device element. You must also validate the lpstrElementName member by specifying the MCI_OPEN_ELEMENT flag in the dwFlags parameter of the mciSendCommand function.

You can specify the device in the same manner used for simple devices: by device name, by device-type constant, or by the filename of the driver. The following example opens the waveaudio device with a WAVE file named TIMPANI.WAV.

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

/*

* Open a waveaudio device by specifying

* the device name and device element.

*/

mciOpenParms.lpstrDeviceType = "waveaudio";

mciOpenParms.lpstrElementName = "timpani.wav";

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

(DWORD)(LPVOID) &mciOpenParms))

/* Error, unable to open device. */

return FALSE;

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

You can also open a device by specifying just a device element. MCI uses the filename extension of the given device element along with the [mci extensions] section of the registry to select the device to open. For more information about opening devices by specifying only a device element, see Opening MCI Devices.

+$#A Closing an MCI Device

The MCI_CLOSE command message releases access to a device or device element. MCI frees a device when all processes using a device have closed it. To help MCI manage devices, applications should explicitly close each device or device element when they are finished using it.

Note It is very important that applications close all opened MCI devices before terminating. You can close all devices with a single command message by specifying the MCI_ALL_DEVICE_ID constant as the device identifier.

+$#A Using the Wait and Notify Flags

Typically, MCI returns control to the application immediately, even if it takes several seconds to complete the action initiated by a command. For example, after a CD audio device receives a seek command, control returns to the application before the seek operation is complete. You can use the following two flags to request that MCI wait until a command is complete before returning control to the application, or that MCI notify the application when the command is complete.

Flag | Description
MCI_NOTIFY | Directs the device to return control immediately and post the MM_MCINOTIFY message when the requested action is complete.
MCI_WAIT | Directs the device to wait until the requested action is complete before returning control to the application.

+$#A Using the Notify Flag

The MCI_NOTIFY flag directs MCI to post the MM_MCINOTIFY message when the device completes an action. The application specifies the handle of the destination window for the message in the dwCallBack member of the structure sent with the command message. Every structure associated with a command message contains this member.

Note MCI can have only one pending notification message for each device.

When MCI posts the MM_MCINOTIFY message, it sets the low-order word of the lParam parameter of the window procedure to the identifier of the device initiating the callback. The wParam parameter of the window procedure contains one of the following constants specifying the notification status of the callback.

Flag | Meaning
MCI_NOTIFY_SUCCESSFUL | The conditions required for initiating the callback are satisfied and the command finished without interruption.
MCI_NOTIFY_SUPERSEDED | MCI had a notification pending and received another notify request. MCI resets the callback conditions to correspond to the notify request of the new command.
MCI_NOTIFY_ABORTED | The application sent a new command that prevented the callback conditions set by a previous command from being satisfied. If an application interrupts a command that requested notification, MCI does not inform the window procedure of the notify command being aborted.
MCI_NOTIFY_FAILURE | A device error occurred while a device was executing the command. For example, MCI posts this message when a hardware error occurs during a play command.

+$#A Using the Wait Flag and Break Command Message

Specifying the MCI_WAIT flag with a command message causes MCI to wait until the command has finished before returning control to the application. Using the wait flag with commands that take a long time essentially disables a system from receiving any user input until the command is complete.

MCI supports a break key to interrupt the wait operation and return control to the application. The break key does not interrupt the command, but by returning control to the application, it allows the application to monitor user input.

By default, MCI defines the break key as CTRL+BREAK. Applications can redefine this key by using the MCI_BREAK command message. MCI_BREAK uses the MCI_BREAK_PARMS structure.

The following flags validate members in the MCI_BREAK_PARMS structure:

Flag | Description
MCI_BREAK_HWND | Validates the hwndBreak member, which specifies the window that must have the focus to enable break detection.
MCI_BREAK_KEY | Validates the nVirtKey member, which specifies the virtual-key code to be used for the break key.
MCI_BREAK_OFF | Disables any existing break key.

+$#A Using MCI_SYSINFO to Obtain MCI System Information

The MCI_SYSINFO command message obtains system information about MCI devices. MCI handles this message without relaying it to any MCI device. MCI returns the system information in the MCI_SYSINFO_PARMS structure.

The MCI_SYSINFO command message has the following flags.

Flag | Meaning
MCI_SYSINFO_QUANTITY | MCI returns the number of devices of a given type listed in the [mci] section of the registry. When this flag is used with the MCI_SYSINFO_OPEN flag, the number of open devices is returned.
MCI_SYSINFO_NAME | MCI returns the name of a device that satisfies the query. When this flag is used with the MCI_SYSINFO_OPEN flag, only the names of open devices are returned.
MCI_SYSINFO_OPEN | MCI returns the number or names of open devices.
MCI_SYSINFO_INSTALLNAME | MCI returns the name listed in the registry entry used to install the device.

You use the MCI_SYSINFO_QUANTITY flag to determine the number of devices for a particular type listed in the [mci] section of the registry. For this flag, set the wDeviceType member of the structure to the device type. When requesting the number of devices, set the wDeviceID parameter to NULL. MCI returns the number of devices as a doubleword in the lpstrReturn member of the structure. If you combine the MCI_SYSINFO_QUANTITY flag with the MCI_SYSINFO_OPEN flag, MCI returns the number of devices of that type opened by the process.

Once you have the number of devices, you can enumerate the names of the devices by using the MCI_SYSINFO_NAME flag. To get information by using this flag, you must create a buffer for the return name. In the structure, specify a pointer to the buffer, the size of the buffer, the index number corresponding to the device (1 is the first device), and the device type. This information is entered in the lpstrReturn, dwRetSize, dwNumber, and wDeviceType members. MCI returns the device name in the buffer. When requesting the name of a particular device, set the wDeviceID parameter to NULL. To restrict the names to open devices, use the MCI_SYSINFO_OPEN flag with MCI_SYSINFO_NAME.

To retrieve information on all devices in the system, you can assign the MCI_ALL_DEVICE_ID constant to the wDeviceID parameter. When you use this identifier, MCI ignores the contents of the wDeviceType member and returns information on all MCI devices listed in the registry. When you use the MCI_SYSINFO_OPEN flag with the MCI_ALL_DEVICE_ID identifier, MCI returns information about the devices opened by the task.

You can retrieve the installation name of an open device by using the MCI_SYSINFO_INSTALLNAME flag. To get the installation name, you must create a buffer for the return name. In the structure, specify a pointer to the buffer and the size of the buffer in the lpstrReturn and dwRetSize members. MCI places the device that corresponds to the wDeviceID parameter name in the buffer.

+$#A About the Command-String Interface

This section presents an introduction to the MCI command-string interface. MCI provides the following functions for working with command strings.

Function | Description
mciSendString | Sends a command string to an MCI device driver. This function also has parameters for callback functions and return strings.
mciGetErrorString | Returns the error string corresponding to an error number.

+$#A Sending MCI Command Strings

Use the mciSendString function to send command strings to a device. The following statement passes the "play to 500" command string to a waveaudio device driver.

DWORD dwErrorCode;

dwErrorCode = mciSendString("play waveaudio to 500", NULL, 0, 0L);

If the mciSendString function succeeds, it returns zero. If the function fails, it returns an error code. To get extended error information, use the mciGetErrorString function.

+$#A Additional Information About Command Strings

The MCITEST application contains examples of using the mciSendString and mciGetErrorString functions. You can use this application to experiment with MCI devices and command strings.

+$#A Multimedia File Input/Output

Most multimedia applications require file input/output (I/O) ⎯ the ability to create, read, and write disk files. The multimedia file I/O services provided by the Microsoft® Win32® application programming interface (API) offer buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible with custom I/O procedures.

+$#A About the Multimedia File Input/Output Services

Most applications need only the basic file I/O services and the RIFF file I/O services. Applications sensitive to file I/O performance, such as applications that stream data from a CD-ROM in real time, can optimize performance by using services to directly access the file I/O buffer. Applications that access custom storage systems can provide their own I/O procedure that reads and writes elements of the storage system. A storage system is a method of physically storing data in a file, such as a file-archival system or a database storage system.

This section covers the following topics:

• Using basic file I/O services

• Performing buffered file I/O

• Working with RIFF files

• Directly accessing a file I/O buffer

• Using memory files

• Writing a custom I/O procedure

The multimedia file I/O services provide support for the following file I/O operations:

• Basic unbuffered and buffered file I/O

• RIFF file I/O

• Direct access to the file I/O buffer

• Memory files

• Custom storage system I/O using application-supplied I/O procedures

+$#A Comparison With Other File I/O

You might ask why you need another set of file I/O services, when you already have the services of the C run-time and Win32 API libraries. The multimedia file I/O services offer the following advantages over other file I/O services:

• They are a part of the system software, so they don't increase the size of your application, like linking to the C run-time libraries

• They provide more functionality than the Win32 services, such as support for buffered I/O, RIFF files, memory files, and custom storage systems

In addition, the multimedia file I/O services are optimized for performance-intensive applications.

+$#A Function Prefixes

All multimedia file I/O function names begin with the mmio prefix. Similarly, all multimedia file I/O message names begin with the MMIOM_ prefix.

+$#A Data Types

The MMSYSTEM.H header file defines data types and function prototypes for all multimedia file I/O functions. You must include this header file in any source-code module that uses these functions. MMSYSTEM.H depends on declarations made in WINDOWS.H, so you must first include the WINDOWS.H header file. MMSYSTEM.H defines the following data types for the multimedia file I/O functions.

Type | Description
FOURCC | A four-character code identifying an element of a RIFF file.
HMMIO | A handle of an open file.
MMCKINFO | A structure containing information about a chunk in a RIFF file.
MMIOINFO | A structure used to maintain the current state of a file accessed using the multimedia file I/O services.
MMIOPROC | A custom multimedia file I/O procedure.

+$#A Performing Basic File Input/Output

Using the basic I/O services is similar to using the C run-time file I/O services. Files must be opened before they can be read or written. After reading or writing, the file must be closed. You can change the current read/write location by seeking to a specified position in an open file. The following table lists the basic file I/O functions.

Function | Description
mmioClose | Closes an open file.
mmioOpen | Opens a file for reading or writing or both and returns a handle of the open file.
mmioRead | Reads a specified number of bytes from an open file.
mmioSeek | Changes the current position for reading or writing in an open file.
mmioWrite | Writes a specified number of bytes to an open file.

These file I/O functions provide the core of the multimedia file I/O services ⎯ you can use them for buffered and unbuffered I/O, as well as for I/O to RIFF files, memory files, and custom storage systems.

+$#A Opening a Multimedia File

Before doing any I/O operations to a file, you must first open the file by using the mmioOpen function. The mmioOpen function returns a file handle that you use to identify the open file when calling other file I/O functions.

Note Do not use HMMIO file handles with Windows or C run-time file I/O functions.

There are options you can use with the mmioOpen function for operations beyond basic file I/O. By specifying an MMIOINFO structure with the lpmmioinfo parameter you can open memory files, specify a custom I/O procedure, or supply a buffer for buffered I/O. These topics are discussed later in this section. First, this section discusses the most basic use of mmioOpen ⎯ opening files for basic unbuffered file I/O.

To open a file for basic I/O operations, set the lpmmioinfo parameter of mmioOpen to NULL. For example, the following example opens a file named C:\SAMPLES\SAMPLE1.TXT for reading, and checks the return value for errors.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

if ((hFile = mmioOpen("C:\\SAMPLES\\SAMPLE1.TXT",

NULL, MMIO_READ)) != NULL) /* File opened successfully */

return TRUE;

else

/* File cannot be opened. */

return FALSE;

+$#A Options for Opening a File

When you open a file, you must specify whether you are opening the file for reading, writing, or both reading and writing. In addition, you can specify other options, such as to create or delete a new file. Use the dwFlags parameter of mmioOpen to specify options for opening a file.

Following are the basic options for opening a file using mmioOpen:

Flag | Description
MMIO_READ | Opens a file for reading only.
MMIO_WRITE | Opens a file for writing only.
MMIO_READWRITE | Opens a file for reading and writing.
MMIO_CREATE | Creates a new file (if the file already exists, it truncates it to zero length).
MMIO_DELETE | Deletes a file.
MMIO_ALLOCBUF | Opens a file for buffered I/O.

The MMIO_READ, MMIO_WRITE, and MMIO_READWRITE flags are read/write privilege flags. These flags are mutually exclusive ⎯ specify only one when opening a file. If you don't specify one of these flags, mmioOpen opens the file for reading only.

The MMIO_CREATE and MMIO_DELETE flags are also mutually exclusive. You can specify one of the read/write privilege flags with the MMIO_CREATE flag. You can't specify any additional flags with the MMIO_DELETE flag.

For information on using the MMIO_ALLOCBUF flag, see Performing Buffered File Input/Output.

In addition to the basic options for opening a file, there are sharing options you can use for opening files so they can be opened and accessed by more than one process. Following are the sharing options for opening a file using mmioOpen.

Flag | Description
MMIO_COMPAT | Opens a file in compatibility mode.
MMIO_EXCLUSIVE | Opens a file in exclusive mode.
MMIO_DENYWRITE | Opens a file and denies other processes write access to the file.
MMIO_DENYREAD | Opens a file and denies other processes read access to the file.
MMIO_DENYNONE | Opens a file without denying other processes read or write access to the file.

+$#A Crating and Deleting Files

To create a new file, specify the MMIO_CREATE option with the mmioOpen function. For example, the following example creates a new file and opens it for both reading and writing.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

hFile = mmioOpen("NEWFILE.TXT",

NULL, MMIO_CREATE | MMIO_READWRITE);

if (hFile != NULL)

/* File created successfully. */

return TRUE;

else

/* File could not be created. */

return FALSE;

Note If the file you are creating already exists, it will be truncated to zero length.

To delete a file, specify the MMIO_DELETE flag with the mmioOpen function. Once you delete a file, it can't be recovered. If the file deletion is the result of a request from a user, always query the user to be sure the user wants to delete the specified file before you actually delete it.

+$#A Reading and Writing a File

To read and write to open files, use the mmioRead and mmioWrite functions. Each of these functions takes an HMMIO file handle, a pointer to a buffer, and a parameter specifying the number of bytes to read or write.

For an example using mmioRead to read from a file, see Example of RIFF File I/O.

+$#A Seeking to a New Position in a Multimedia File

The current position or file pointer in a file is the location where the next read or write operation will occur. To change the current position in an open file, use the mmioSeek function.

The return value is the new position, specified in bytes from the beginning of the file. If an error occurs, the return value is −1. If you seek to an invalid location in a file, such as past the end of the file, mmioSeek may not return an error, but subsequent I/O operations can fail.

+$#A Performing Buffered File Input/Output

Most of the overhead in file I/O involves accessing the media (the physical device). If you are reading or writing many small blocks of information, the media device can spend a lot of time seeking the physical location on the media for each read or write operation. In this case, better performance is achieved by using buffered file I/O. With buffered I/O, the file I/O manager maintains an intermediate buffer larger than the blocks of information you are reading or writing. It only accesses the media when the buffer must be filled from or written to the disk.

+$#A Deciding When to Use Buffered File I/O

It's difficult to provide exact metrics telling you when you need to use buffered I/O. It depends on how many read and write operations you perform on a file, and on the size of these read and write operations. A general guideline is to use buffered I/O if you are doing many small I/O operations (less than 2 kilobytes each). But this rule isn't absolute ⎯ it's best to understand exactly how your program uses file I/O and experiment to optimize file I/O for your program's requirements.

+$#A Opening a File for Buffered File I/O

The multimedia file I/O manager provides several ways to set up and use buffered file I/O. The main distinction between these different approaches is whether the file I/O manager or the application allocates the buffer ⎯ either the file I/O manager or the application can allocate the I/O buffer. Unless you want to access the I/O buffer directly or open a memory file, let the file I/O manager allocate the buffer. For more information about accessing an I/O buffer directly and using memory files, see Directly Accessing a File Input/Output Buffer and Performing File Input/Output on Memory Files.

A buffer allocated by the file I/O manager is called an internal buffer. To open a file for buffered I/O using an internal buffer, specify the MMIO_ALLOCBUF flag with the mmioOpen function when you open the file. Once a file is opened for buffered I/O, the buffer is essentially transparent to the application. You can read, write, and seek the same way as with unbuffered I/O.

+$#A I/O Buffer Control Functions

The multimedia file I/O services also include some functions that give you more control over the file I/O buffer. Using the following functions, you can force the contents of an I/O buffer to be written to disk, enable buffered I/O on a file opened for unbuffered I/O, change the size of an I/O buffer, and supply your own I/O buffer.

Function | Description
mmioFlush | Writes the contents of the I/O buffer to disk.
mmioSetBuffer | Changes the size of the I/O buffer, and allows applications to supply their own buffer.

+$#A Flushing an I/O Buffer

Flushing an I/O buffer means writing the contents of the buffer to disk. You don't have to call the mmioFlush function to flush an I/O buffer ⎯ the buffer is automatically flushed when you close a file using the mmioClose function. If you don't close a file immediately after writing to it, you flush the buffer to ensure the information is written to disk.

Note If you run out of disk space, mmioFlush may fail, even if the preceding mmioWrite calls were successful. Similarly, mmioClose may fail when it is flushing its I/O buffer.

+$#A Changing the Size of the Internal I/O Buffer

The default size of the internal I/O buffer is 8K. If this size is not adequate, you can use the mmioSetBuffer function to change the size of the buffer. You can also use mmioSetBuffer to enable buffering on a file opened for unbuffered I/O.

The following example opens a file named SAMPLE.TXT for unbuffered I/O, and then enables buffered I/O with an internal 16K buffer.

HMMIO hFile;

.ifdef REF

.

.

.

.endif

if ((hFile = mmioOpen("SAMPLE.TXT", NULL, MMIO_READ)) != NULL) {

/* File opened successfully; request an I/O buffer */

if (mmioSetBuffer(hFile, NULL, 16384L, 0))

/* Buffer cannot be allocated. */

return FALSE;

else

/* Buffer allocated successfully. */

return TRUE;

}

else

/* File cannot be opened. */

return FALSE;

+$#A Supplying Your Own I/O Buffer

You can also use mmioSetBuffer to supply your own buffer for use as a memory file. For details on using memory files, see Performing File Input/Output on Memory Files.

+$#A Working With RIFF Files

The preferred format for multimedia files is the Resource Interchange File Format (RIFF). The RIFF format is a tagged-file structure.

The multimedia file I/O services provide the following functions to support file I/O to RIFF files.

Function | Description
mmioAscend | Ascends out of a RIFF file chunk to the next chunk in the file.
mmioCreateChunk | Creates a new chunk in a RIFF file.
mmioDescend | Descends into a RIFF file chunk beginning at the current file position, or searches for a specified chunk.
mmioFOURCC | Converts four individual characters into a four-character code.
mmioStringToFOURCC | Converts a null-terminated string into a four-character code.

These functions work with the basic buffered and unbuffered file I/O services ⎯ you can open, read, and write RIFF files the same as other file types.

+$#A About RIFF Files

The basic building block of a RIFF file is called a chunk. Each chunk consists of the following fields:

• A four-character code specifying the chunk identifier

• A doubleword specifying the size of the data field in the chunk

• A data field

The only chunks allowed to contain other chunks (subchunks) are those with a chunk identifier of "RIFF" or "LIST". The first chunk in a RIFF file must be a "RIFF" chunk. All other chunks in the file are subchunks of the "RIFF" chunk.

+$#A "RIFF" Chunks

"RIFF" chunks include an additional field in the first four bytes of the data field. This additional field provides the form type of the field. The form type is a four-character code identifying the format of the data stored in the file. For example, Microsoft waveform audio files (WAVE files) have a form type of "WAVE". The following illustration shows a "RIFF" chunk containing two subchunks.

+$#A "LIST" Chunks

"LIST" chunks also include an additional field in the first four bytes of the data field. This additional field contains the list type of the field. The list type is a four-character code identifying the contents of the list. For example, a "LIST" chunk with a list type of "INFO" can contain "ICOP" and "ICRD" chunks providing copyright and creation date information. The following illustration shows a "RIFF" chunk containing a "LIST" chunk and one other subchunk (the "LIST" chunk contains two subchunks).

+$#A Generating Four-Character Codes

A four-character code is a 32-bit quantity representing a sequence of one to four ASCII alphanumeric characters, padded on the right with blank characters. The data type for a four-character code is FOURCC. Use the mmioFOURCC function to convert four characters to a four-character code, as shown in the following example, which generates a four-character code for "WAVE":

FOURCC fourccID;

.ifdef REF

.

.

.

.endif

fourccID = mmioFOURCC('W', 'A', 'V', 'E');

To convert a null-terminated string into a four-character code, use the mmioStringToFOURCC function, as shown in the following example, which also generates a four-character code for "WAVE":

FOURCC fourccID;

.ifdef REF

.

.

.

.endif

fourccID = mmioStringToFOURCC("WAVE", 0);

The second parameter in mmioStringToFOURCC specifies options for converting the string to a four-character code. If you specify the MMIO_TOUPPER flag, mmioStringToFOURCC converts all alphabetic characters in the string to uppercase. This is useful when you need to specify a four-character code to identify a custom I/O procedure (four-character codes identifying file-extension names must be all uppercase).

+$#A Creating RIFF Chunks

To create a new chunk, use the mmioCreateChunk function to write a chunk header at the current position in an open file.

The following example creates a new chunk with a chunk ID of "RIFF" and a form type of "RDIB".

HMMIO hmmio;

MMCKINFO mmckinfo;

.ifdef REF

.

.

.

.endif

mmckinfo.fccType = mmioFOURCC('R', 'D', 'I', 'B');

mmioCreateChunk(hmmio, &mmckinfo, MMIO_CREATERIFF);

If you're creating a "RIFF" or "LIST" chunk, you must specify the form type in the fccType field of the MMCKINFO structure. In the previous example, the form type is "RDIB".

If you know the size of the data field in a new chunk, you can set the cksize member in the MMCKINFO structure when you create the chunk. This value will be written to the data size field in the new chunk. If this value is not correct when you call the mmioAscend function to mark the end of the chunk, it will be rewritten automatically to reflect the correct size of the data field.

After you create a new chunk using mmioCreateChunk, the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). If the chunk is a "RIFF" or "LIST" chunk, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk).

+$#A Navigating RIFF Files

RIFF files consist of nested chunks of data. Multimedia file I/O services include two functions you can use to navigate between chunks in a RIFF file: mmioAscend and mmioDescend. You might think of these functions as high-level seek functions. When you descend into a chunk, the file position is set to the data field of the chunk (8 bytes from the beginning of the chunk). For "RIFF" and "LIST" chunks, the file position is set to the location following the form type or list type (12 bytes from the beginning of the chunk). When you ascend out of a chunk, the file position is set to the location following the end of the chunk.

+$#A Descending Into a Chunk

The mmioDescend function descends into a chunk or searches for a chunk, beginning at the current file position.

The mmioDescend function fills an MMCKINFO structure with information on the chunk. This information includes the chunk identifier, the size of the data field, and the form type, or list type if the chunk is a "RIFF" or "LIST" chunk.

+$#A Searching for a Chunk

To search for a chunk in an open RIFF file, specify the MMIO_FINDCHUNK flag in the wFlags parameter of mmioDescend. Set the ckid member of the MMCKINFO structure referenced by lpck to the four-character code of the chunk you want to search for.

If you are searching for a "RIFF" or "LIST" chunk, you don't need to set the ckid member of the MMCKINFO structure ⎯ mmioDescend sets this member for you. Set the fccType member to the four-character code of the form type or list type of the chunk.

The following example searches for a "RIFF" chunk with a form type of "WAVE" to verify that the file that has just been opened is a WAVE waveform audio file.

HMMIO hmmio;

MMCKINFO mmckinfoParent;

MMCKINFO mmckinfoSubchunk;

.ifdef REF

.

.

.

.endif

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure the file is a WAVE file.

*/

mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');

if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent,

NULL, MMIO_FINDRIFF))

/* The file is not a WAVE file. */

return FALSE;

else

/* The file is a WAVE file. */

return TRUE;

If the chunk you are searching for is a subchunk enclosed by a parent chunk (as are all chunks other than "RIFF" chunks), you should identify the parent chunk with the lpckParent parameter. In this case, mmioDescend will only search within the specified parent chunk.

The following example searches for the "fmt " chunk in the "RIFF" chunk descended into by the previous example.

/*

* Find the format chunk (form type "fmt "); it should be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

/* Error, cannot find the "fmt " chunk. */

return FALSE;

}

else {

/* "fmt " chunk found. */

return TRUE;

}

If you do not specify a parent chunk, the current file position should be at the beginning of a chunk before you call mmioDescend to search for a chunk. If you do specify a parent chunk, the current file position can be anywhere in the parent chunk.

If the search for a subchunk fails, the current file position is undefined. You can use the mmioSeek function and the dwDataOffset field of the MMCKINFO structure for the enclosing parent chunk to seek back to the beginning of the parent chunk, as in the following example:

mmioSeek(hmmio, mmckinfoParent.dwDataOffset + 4, SEEK_SET);

Since the dwDataOffset member specifies the offset to the beginning of the data portion of the chunk, you must seek four bytes past dwDataOffset to set the file position to be after the form type.

+$#A Ascending Out of a Chunk

After you descend into a chunk and read the data in the chunk, you can move the file position to the beginning of the next chunk by ascending out of the chunk by using the mmioAscend function.

For example, the following statement ascends out of the "fmt " subchunk descended into by the previous example, illustrating searching for a subchunk:

/* Ascend out of the "fmt " subchunk.

*/

mmioAscend(hmmio, &mmckinfoSubchunk, 0);

+$#A Example of RIFF File I/O

The following example shows how to open a RIFF file for buffered I/O, as well as how to descend, ascend, and read RIFF chunks.

/* ReversePlay--Plays a WAVE waveform audio file backwards. */

void ReversePlay(void)

{

char szFileName[128]; /* filename of file to open */

HMMIO hmmio; /* file handle for open file */

MMCKINFO mmckinfoParent; /* parent chunk information structure */

MMCKINFO mmckinfoSubchunk; /* subchunk information structure */

DWORD dwFmtSize; /* size of "fmt" chunk */

DWORD dwDataSize; /* size of "data" chunk */

WAVEFORMAT *pFormat; /* pointer to memory for "fmt" chunk */

.ifdef REF

...

.endif

/* Get the filename from the edit control. */

.ifdef REF

...

.endif

/*

* Open the given file for reading with buffered I/O

* using the default internal buffer.

*/

hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);

if(hmmio == NULL){

Error("Failed to open file.");

return;

}

/*

* Locate a "RIFF" chunk with a "WAVE" form type

* to make sure the file is a WAVE file.

*/

mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');

if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL,

MMIO_FINDRIFF)) {

Error("This is not a WAVE file.");

mmioClose(hmmio, 0);

return;

}

/*

* Find the "fmt " chunk (form type "fmt "); it must be

* a subchunk of the "RIFF" parent chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

Error("WAVE file has no \"fmt\" chunk");

mmioClose(hmmio, 0);

return;

}

/*

* Get the size of the "fmt " chunk--allocate and lock memory for it.

*/

dwFmtSize = mmckinfoSubchunk.cksize;

.ifdef REF

...

.endif

/* Read the "fmt " chunk. */

if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LRESULT)dwFmtSize){

Error("Failed to read format chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Ascend out of the "fmt " subchunk. */

mmioAscend(hmmio, &mmckinfoSubchunk, 0);

/*

* Find the data subchunk. The current file position

* should be at the beginning of the data chunk.

*/

mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');

if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,

MMIO_FINDCHUNK)) {

Error("WAVE file has no data chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Get the size of the data subchunk. */

dwDataSize = mmckinfoSubchunk.cksize;

if (dwDataSize == 0L) {

Error("The data chunk contains no data.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Open a waveform output device. */

.ifdef REF

...

.endif

/* Allocate and lock memory for the waveform data. */

.ifdef REF

...

.endif

/* Read the waveform data subchunk. */

if(mmioRead(hmmio, (HPSTR) lpData,

dwDataSize) != (LRESULT)dwDataSize) {

Error("Failed to read data chunk.");

.ifdef REF

...

.endif

mmioClose(hmmio, 0);

return;

}

/* Close the file. */

mmioClose(hmmio, 0);

/* Reverse the sound and play it. */

.ifdef REF

...

.endif

}

+$#A Directly Accessing a File Input/Output Buffer

Applications that are performance sensitive, such as applications that must stream data in real time from a CD-ROM, can optimize file I/O performance by accessing the file I/O buffer directly. Exercise care if you choose to do this ⎯ by accessing the file I/O buffer directly, you bypass some of the safeguards and error checking provided by the file I/O manager.

The multimedia file I/O services provide the following functions to support direct I/O buffer access.

Function | Description
mmioAdvance | Fills and/or flushes the I/O buffer of a file set up for direct I/O buffer access.
mmioGetInfo | Retrieves information on the file I/O buffer of a file opened for buffered I/O.
mmioSetInfo | Changes information on the file I/O buffer of a file opened for buffered I/O.

To access a file I/O buffer directly, open the file for buffered I/O as described in "Performing Buffered File I/O." You can use the internal file I/O buffer or supply your own buffer by using the mmioSetBuffer function.

+$#A Getting Information on the File I/O Buffer

Use the mmioGetInfo function to get information on a file I/O buffer, such as the buffer size and address.

+$#A Reading and Writing the File I/O Buffer

There are three members in the MMIOINFO structure used for reading and writing the file I/O buffer: pchNext, pchEndRead, and pchEndWrite. The pchNext field points to the next location in the buffer to read or write. You must increment pchNext as you read and write the buffer. The pchEndRead field identifies the last valid character you can read from the buffer. Likewise, pchEndWrite identifies the last location in the buffer you can write. To be precise, both pchEndRead and pchEndWrite point to the memory location following the last valid data in the buffer.

+$#A Advancing the File I/O Buffer

When you reach the end of the file I/O buffer, you must advance the buffer to fill it from the disk (if you are reading), and flush it to the disk (if you are writing). Use the mmioAdvance function to advance a file I/O buffer.

The mmioAdvance function advances the buffer by (pchNext − cchBuffer) bytes. All data in the buffer before pchNext is assumed to have been read already, while all data from pchNext to pchEndRead will be considered unread. So, when mmioAdvance is called, all data before pchNext will be overwritten. The buffer must not be treated as an array and pchNext must be incremented until pchNext equals pchEndRead.

The following illustrations show how the file I/O buffer is advanced as a file is read or written.

+$#A Using the mmioAdvance Function

To fill a file I/O buffer from disk, call mmioAdvance with the MMIO_READ flag. If there is not enough data remaining in the file to fill the buffer, the pchEndRead member in the MMIOINFO structure points to the location following the last valid byte in the buffer.

To flush a buffer to disk, first set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure. Then call mmioAdvance with the MMIO_WRITE flag.

+$#A Example of Accessing a File I/O Buffer

The following example is based on the ReversePlay example function discussed earlier in this section. In this example, direct-buffer access is used to read waveform data from a file.

HMMIO hmmio;

MMIOINFO mmioinfo;

DWORD dwDataSize;

DWORD dwCount;

HPSTR hptr;

.ifdef REF

.

.

.

.endif

/* Get information on the file I/O buffer. */

if (mmioGetInfo(hmmio, &mmioinfo, 0)) {

Error("Failed to get I/O buffer info");

mmioClose(hmmio, 0);

return FALSE;

}

/*

* Read the entire file by directly reading the file I/O buffer.

* When the end of the I/O buffer is reached, advance the buffer.

*/

for (dwCount = dwDataSize, hptr = lpData; dwCount > 0; dwCount--) {

/* Check to see if the I/O buffer must be advanced. */

if (mmioinfo.pchNext == mmioinfo.pchEndRead) {

if(mmioAdvance(hmmio, &mmioinfo, MMIO_READ)) {

Error("Failed to advance buffer.");

mmioClose(hmmio, 0);

return FALSE;

}

}

/* Get a character from the buffer. */

*hptr++ = *mmioinfo.pchNext++;

}

/* End direct buffer access and close the file. */

mmioSetInfo(hmmio, &mmioinfo, 0);

mmioClose(hmmio, 0);

+$#A Ending Direct Access of a File I/O Buffer

When you finish accessing a file I/O buffer, pass the MMIOINFO structure filled by the mmioGetInfo function to the mmioSetInfo function to stop direct-buffer access. If you wrote to the buffer, set the MMIO_DIRTY flag in the dwFlags member of the MMIOINFO structure before calling mmioSetInfo. Otherwise, the buffer will not be flushed to disk.

+$#A Performing File Input/Output on Memory Files

The multimedia file I/O services let you to treat a block of memory as a file. This can be useful if you already have a file image in memory. Memory files let you reduce the number of special-case conditions in your code because, for I/O purposes, you can treat file memory images as if they were disk-based files. You can also use memory files with the Clipboard.

+$#A Opening Memory Files

Like I/O buffers, memory files can use memory allocated by the application or by the file I/O manager. In addition, memory files can be either expandable or non-expandable. When the file I/O manager reaches the end of an expandable memory file, it expands the memory file by a predefined increment.

To open a memory file, use mmioOpen with the szFileName parameter set to NULL and the MMIO_READWRITE flag set in the dwOpenFlags parameter. Set the lpmmioinfo parameter to point to an MMIOINFO structure as follows:

• Set the pIOProc member to NULL.

• Set the fccIOProc member to FOURCC_MEM.

• Set the pchBuffer member to point to the memory block. To request that the file I/O manager allocate the memory block, set pchBuffer to NULL.

• Set the cchBuffer member to the initial size of the memory block.

• Set the adwInfo[0] member to the minimum expansion size of the memory block. For a non-expandable memory file, set adwInfo[0] to NULL.

• Set all other members to zero.

+$#A Allocating Memory for Memory Files

There are no restrictions on allocating memory for use as a non-expandable memory file. You can use static memory or stack memory, or you can use locally allocated or globally allocated memory. For expandable memory files, you must use memory allocated using the GlobalAlloc function and locked using the GlobalLock function.

+$#A Using Custom Input/Output Procedures

Multimedia file I/O services use I/O procedures to handle the physical input and output associated with reading and writing to different types of storage systems, such as file-archival systems and database-storage systems. There are predefined I/O procedures for standard Microsoft® Windows® files and for memory files. In the future, there will be a predefined I/O procedure for accessing elements of compound files. Compound files consist of a number of individual files, called file elements, bound together in one physical file.

You can supply a custom I/O procedure for accessing a unique storage system such as a database or file archive. This I/O procedure is private to your application.

The multimedia file I/O services provide the following functions to support custom I/O procedures:

Function | Description
mmioInstallIOProc | Installs, removes, or locates an I/O procedure.
mmioSendMessage | Sends a custom message to the I/O procedure associated with a file.

• Opening a file using a custom I/O procedure

• Writing an I/O procedure

• Installing an I/O procedure

+$#A Opening a File Using a Custom I/O Procedure

Before learning about how to write an I/O procedure, it's helpful to understand how to use one. To open a file using a custom I/O procedure, use the mmioOpen function as you would to open any other file. Use a plus sign (+) in the filename to separate the name of the physical file from the name of the element of the file you want to open. For example, the following statement opens a file element named "element" from a file named FILENAME.ARC:

mmioOpen("filename.arc+element", NULL, MMIO_READ);

When the file I/O manager encounters a plus sign in a filename, it looks at the preceding filename extension to determine which I/O procedure to associate with the file. In the previous example, the file I/O manager will attempt to use the I/O procedure associated with the .ARC filename extension. If no I/O procedure is installed, the mmioOpen function returns an error.

+$#A Writing an I/O Procedure

An I/O procedure is a message-processing function supplied by an application. An I/O procedure function has the following syntax:

LRESULT IOProc(lpmmioinfo, wMsg, lParam1, lParam2)

The lpmmioinfo parameter specifies a pointer to an MMIOINFO structure associated with the file being accessed.

The wMsg parameter is of type UINT and specifies the message being sent by the file I/O manager to the I/O procedure.

The lParam1 parameter is of type LPARAM and specifies 32 bits of message-specific information.

The lParam2 parameter is of type LPARAM and specifies 32 bits of message-specific information.

The return value is message-specific. If the I/O procedure does not recognize a message, it should return zero.

+$#A I/O Procedure Messages

I/O procedures must respond to the MMIOM_CLOSE, MMIOM_OPEN, MMIOM_READ, MMIOM_WRITE, MMIOM_SEEK, and MMIOM_WRITEFLUSH messages. Each of these messages has two parameters.

You can also create custom messages and send them to your I/O procedure using the mmioSendMessage function. If you define your own messages, be sure they are defined at or above the MMIOM_USER message. For example, the following example defines a message named MMIOM_MYMESSAGE:

#define MMIOM_MYMESSAGE MMIOM_USER + 0

+$#A Using the MMIOINFO Structure

In addition to processing messages, an I/O procedure must maintain the lDiskOffset member in the MMIOINFO structure referenced by the lpmmioinfo parameter. The lDiskOffset member must always contain the file offset to the location that the next MMIOM_READ or MMIOM_WRITE message will access. The offset is specified in bytes and is relative to the beginning of the file. The I/O procedure can use the adwInfo[] member to maintain any required state information. The I/O procedure should not modify any other members in the MMIOINFO structure.

+$#A Installing an I/O Procedure

Use mmioInstallIOProc to install, remove, or locate an I/O procedure.

For example, to install an I/O procedure associated with the filename extension ARC, use the following statement:

mmioInstallIOProc (mmioFOURCC('A', 'R', 'C', ' '),

(LPMMIOPROC)lpmmioproc, MMIO_INSTALLPROC);

Note Be sure to remove any I/O procedures you've installed before closing your application.

+$#A Installing an I/O Procedure Using MmioOpen

When you install an I/O procedure by using the mmioInstallIOProc function, the procedure remains installed until you remove it. The I/O procedure will be used for any file you open if the file has the appropriate filename extension. You can also temporarily install an I/O procedure by using the mmioOpen function. In this case, the I/O procedure is only used with a file opened by mmioOpen and is removed when the file is closed by using the mmioClose function.

To specify an I/O procedure when you open a file using mmioOpen, use the lpmmioinfo parameter to reference an MMIOINFO structure as follows:

• Set the fccIOProc member to NULL.

• Set the pIOProc member to the procedure-instance address of the I/O procedure.

• Set all other members to zero (unless you are opening a memory file, or directly reading or writing to the file I/O buffer).

+$#A File Input/Output Functions

Following are the functions used with file input/output.

mmioAdvance
mmioAscend
mmioClose
mmioCreateChunk
mmioDescend
mmioFlush
mmioFOURCC
mmioGetInfo
mmioInstallIOProc
mmioOpen
MMIOProc
mmioRead
mmioRename
mmioSeek
mmioSendMessage
mmioSetBuffer
mmioSetInfo
mmioStringToFOURCC
mmioWrite
mmsystemGetVersion

+$#A Introduction to Multimedia Services

Microsoft® Windows® provides developers with high-level and low-level services for developing multimedia applications that use the extended capabilities of a multimedia personal computer.

+$#A About Windows Multimedia Services

Windows provides the following multimedia services:

• A Media Control Interface (MCI) for controlling media devices.

• Extensible string-based and message-based interfaces for communicating with MCI device drivers.

• MCI device drivers for playing and recording waveform audio, playing Musical Instrument Digital Interface (MIDI) files, and playing compact disc audio from a CD-ROM drive.

• Low-level application programming interface (API) support for multimedia-related services.

• Low-level support for playing and recording audio with waveform and MIDI audio devices.

• Low-level support for precision timer services.

• Multimedia file input and output (I/O) services that provide buffered and unbuffered file I/O and support for standard Resource Interchange File Format (RIFF) files. The services are extensible by means of custom I/O procedures.

• A MIDI Mapper supporting standard MIDI patch services. This allows MIDI files to be authored independently of users' MIDI synthesizer setups.

• Control Panel options that let users change display drivers, set up a screen saver, install multimedia device drivers, assign waveform sounds to system alerts, and configure the MIDI Mapper.

This overview covers the following topics:

• The software architecture of the multimedia services

• The design philosophy and implementation of the multimedia services

• Basic information about developing applications that use the multimedia services

+$#A Architecture of Windows Multimedia Services

Although the multimedia services are provided by a number of files, the overall architecture can be viewed as consisting of just three software modules:

• The WINMM library, which provides the MCI services and low-level multimedia support functions

• Multimedia device drivers, which provide communication between the low-level WINMM functions and multimedia devices, such as waveform, MIDI, and timer hardware

• Drivers for MCI, which provide high-level control of media devices

+$#A Windows Multimedia Design Philosophy

The architecture of the multimedia services is designed around the concepts of extensibility and device independence. Extensibility allows the software architecture to accommodate advances in technology easily, without changes to the architecture itself. Device independence makes it easy to develop multimedia applications that will run on a wide range of hardware providing different levels of multimedia support. Three design elements of the system software provide extensibility and device independence:

• A translation layer (WINMM) that isolates applications from device drivers and centralizes device-independent code

• Run-time linking that allows the translation layer to link to the drivers it needs

• A well-defined and consistent driver interface that minimizes special-case code and makes the installation and upgrade process easier

+$#A Building a Multimedia Application

Before writing a multimedia application, you should be familiar with programming in the Windows environment.

When using the multimedia services, you must include the MMSYSTEM.H header file in all source-code files that call multimedia functions. This header file depends on declarations made in the WINDOWS.H header file, so you must first include WINDOWS.H. The MMSYSTEM.H header file provides function prototypes as well as definitions of multimedia data types and constants.

In addition to linking the normal Windows libraries, you must also link to the WINMM.LIB import library when linking an application that uses multimedia functions.

+$#A Multimedia Timer and Joystick Services

This overview shows you how to add joystick input capabilities to your application and how to control event timing.

+$#A About Multimedia Timers

The joystick services can receive control signals from up to two joysticks. Timer services provide improved timer resolution and accuracy. Intel®-based systems have an accuracy of up to 16 milliseconds and MIPS®-based systems have an accuracy of up to 10 milliseconds.

The functions and data structures associated with the joystick and timer-interrupt services are defined in MMSYSTEM.H and WINMM.LIB.

+$#A Timer Services

The multimedia timer services provided with the Microsoft® Win32® application programming interface (API) let applications schedule timed periodic or one-time asynchronous events. Unlike the timer services that post WM_TIMER messages to a message queue, the multimedia timer services call a callback function.

+$#A Timer Data Types

The MMSYSTEM.H header file defines data types and function prototypes for timer functions. You must include this header file in any source-code module that uses timer services. MMSYSTEM.H defines the following new data types.

Type | Description
MMTIME | A structure for representing time in one of several formats.
TIMECAPS | A structure for querying timer capabilities.

+$#A Using Timer Services

Timer services let an application request and receive timer messages at application-specified intervals. Real-time multimedia applications can use the following functions to control the pace of data and synchronized presentations.

Function | Description
timeBeginPeriod | Establishes the minimum timer resolution an application will use.
timeEndPeriod | Clears a minimum timer resolution previously set using timeBeginPeriod.
timeGetDevCaps | Returns information about the capabilities of the timer services.
timeGetTime | Returns the system time in milliseconds.
timeGetSystemTime | Fills an MMTIME structure with the system time in milliseconds.
timeSetEvent | Creates a timer event that executes a specific action at a specific time or at periodic intervals.
timeKillEvent | Cancels a timer event previously created using timeSetEvent.

+$#A Getting the System Time

An application can get the current system time using the timeGetTime or timeGetSystemTime function. The system time is the count of milliseconds since the Microsoft® Windows® operating system was started. The timeGetTime function returns the system time, and the timeGetSystemTime function fills an MMTIME structure with the system time.

+$#A Determining Maximum and Minimum Event Periods

You can use the timeGetDevCaps function to determine the minimum and maximum timer-event periods provided by the timer services. These values vary across computers and can vary depending on the mode in which Windows is running.

+$#A Establishing Minimum Timer Resolution

Before starting timer events, your application must establish the minimum timer resolution that it intends to use. It must clear this value after finishing with the timer-event services.

Use the timeBeginPeriod and timeEndPeriod functions to set and clear the minimum timer-event resolution for your application. You must match a call to timeBeginPeriod with a corresponding call to timeEndPeriod, specifying the same minimum resolution in both calls. An application can make multiple timeBeginPeriod calls, as long as each call is matched with a call to timeEndPeriod.

+$#A Starting Timer Events

To initialize and start timer events, use the timeSetEvent function. This function returns a timer identifier that can be used to stop or identify timer events.

The timeSetEvent function returns a timer identifier if successful or NULL if unsuccessful. Interrupt timers are a scarce resource, and periodic timers with resolution less than 100 milliseconds consume a significant portion of CPU time. For periodic timers, you must pair calls to timeSetEvent with calls to timeKillEvent. For more information, see Canceling a Timer Event.

The lpFunction parameter contains the address of the function to be called when the timer event takes place.

Since the callback function is accessed at interrupt time, it must adhere to strict programming guidelines. Timer-callback functions follow the same programming guidelines as callback functions for the low-level audio services. For information about writing low-level audio callback functions, see Low-Level Audio. For more information about timer callback functions, see TimeProc.

+$#A Canceling a Multimedia Timer Event

You can cancel an active timer event at any time. Be sure to cancel any outstanding timers before freeing the module containing the callback function. To cancel a timer event, use the timeKillEvent function.

+$#A Using Timer Callbacks

This topic describes how an application can use the timer services. First, the application calls the timeGetDevCaps function to determine the minimum and maximum resolution supported by the timer services. Before setting up any timer events, the application uses the timeBeginPeriod function to establish the minimum timer resolution it will use, as shown in the following example:

#define TARGET_RESOLUTION 1 /* Try for 1-millisecond accuracy. */

TIMECAPS tc;

UINT wTimerRes;

if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {

/* Error; application can't continue. */

}

wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION),

tc.wPeriodMax);

timeBeginPeriod(wTimerRes);

To start the timer event, the application specifies the amount of time before the callback occurs, the required resolution, the address of the callback function, and user data to supply with the callback. The application can use a function like the following to start a one-time timer event.

UINT SetTimerCallback(NPSEQ npSeq, /* Sequencer data */

UINT msInterval) /* Event interval */

{

npSeq->wTimerID = timeSetEvent(

msInterval, /* Delay */

wTimerRes, /* Resolution (global variable) */

(LPTIMECALLBACK) OneShotTimer, /* Callback function */

(DWORD) npSeq, /* User data */

TIME_ONESHOT); /* Event type (one-time) */

if (npSeq->wTimerID != 0)

return TIMERR_NOCANDO;

else

return TIMERR_NOERROR;

}

The following callback function is used for the one-shot timer.

void PASCAL OneShotTimer(UINT wTimerID, UINT msg,

DWORD dwUser, DWORD dw1, DWORD dw2)

{

NPSEQ npSeq; /* Pointer to sequencer data */

npSeq = (NPSEQ) dwUser;

npSeq->wTimerID = 0; /* Invalidate unused timer id */

TimerIntRoutine(npSeq); /* Handle interrupt-time tasks */

UNREFERENCED_PARAMETER(dw1);

UNREFERENCED_PARAMETER(dw2);

UNREFERENCED_PARAMETER(msg);

UNREFERENCED_PARAMETER(wTimerID);

}

Before freeing the dynamic-link library (DLL) that contains the callback function, the application cancels any outstanding timers. To cancel one timer event, it can call the following function.

void DestroyTimer(NPSEQ npSeq)

{

if (npSeq->wTimerID) { /* If timer event is pending */

timeKillEvent(npSeq->wTimerID); /* Cancel the event */

npSeq->wTimerID = 0;

}

}

Finally, to cancel the minimum timer resolution it established, the application calls timeEndPeriod as follows.

timeEndPeriod(wTimerRes);

+$#A Joystick Services

The joystick is an input device that provides position information. It is an additional supported input device, not a replacement for the mouse. All absolute-position devices, including touch screens, digitizing tablets, and light pens, can use the joystick services to provide position and button information to applications.

The joystick services are loaded when Windows is started. The joystick services can monitor two joysticks, each with two- or three-axis movement, and up to four buttons. Applications access the joystick services through the set of functions described in this section.

Note The driver for the IBM® Game Adapter supports two 2-axis joysticks or one 3-axis joystick.

+$#A Joystick Data Types

The MMSYSTEM.H header file defines new data types and function prototypes for joystick functions. MMSYSTEM.H defines the following new data types.

Type | Description
JOYCAPS | A structure that contains joystick capability information.
JOYINFO | A structure that contains joystick position and button information.

+$#A Using Joystick Services

Joystick services include functions to query each joystick for its capabilities, to poll each joystick for position and button information, and to receive messages in response to joystick events. Your application can use the following joystick functions to accept input from one or two joysticks.

Function | Description
joyGetNumDevs | Returns the number of joysticks supported by the joystick services.
joyGetDevCaps | Returns joystick capabilities.
joyGetPos | Returns joystick position and button information.
joySetCapture | Causes joystick input to be sent to a specified window at regular intervals or when the joystick state changes.
joyReleaseCapture | Releases the joystick captured using joySetCapture.
joyGetThreshold | Returns the movement threshold of a joystick.
joySetThreshold | Sets the movement threshold of a joystick.

+$#A Determining Joystick Capabilities

The various joysticks in use today can support two or three axes and a variety of button configurations. Also, joysticks support different ranges of motion and polling frequencies. Joystick drivers can support either one or two joysticks. Two functions allow you to determine the capabilities of the joystick services and joystick devices installed on a system.

+$#A Getting the Joystick Driver Capabilities

You can use the joyGetNumDevs function to determine the number of joystick devices supported by the joystick services. This function returns the number of joysticks, or zero if there is no joystick support. The value returned is not necessarily the number of joysticks attached to the system. To determine whether a joysticf supportek is attached, call the joyGetPos function for the device. The function returns JOYERR_UNPLUGGED if the specified device is disconnected.

The following example determines whether the joystick services are available and then determines if a joystick is attached to one of the ports:

JOYINFO joyinfo;

UINT wNumDevs, wDeviceID;

BOOL bDev1Attached, bDev2Attached;

if((wNumDevs = joyGetNumDevs()) == 0)

return ERR_NODRIVER;

bDev1Attached =

joyGetPos(JOYSTICKID1,&joyinfo) != JOYERR_UNPLUGGED;

bDev2Attached =

wNumDevs == 2 &&

joyGetPos(JOYSTICKID2,&joyinfo) != JOYERR_UNPLUGGED;

/* Decide which joystick to use */

if(bDev1Attached || bDev2Attached)

wDeviceID = bDev1Attached ? JOYSTICKID1 : JOYSTICKID2;

else

return ERR_NODEVICE;

+$#A Getting the Joystick Capabilities

You can use the joyGetDevCaps function to obtain the specific capabilities of each joystick attached to a given system. This function fills a JOYCAPS structure with the range of each axis on the joystick, the number of buttons, and the maximum and minimum polling frequency.

+$#A Methods for Checking Joystick Status

An application can receive information from the joystick in one of two ways:

• By processing joystick messages from a captured joystick

• By polling the joystick directly

The message-processing method can be simpler to use; your application is sent messages that indicate the position of the stick and the state of the buttons (pressed or released).

+$#A Capturing Joystick Messages to a Window Function

You can capture joystick input to a window function; your application then receives joystick messages at specified intervals or when the user manipulates the joystick. The messages are described in the following topic.

Only one application can capture joystick messages from a given joystick. Capturing joystick messages does not, however, prevent your application (or other applications) from polling the joystick using joyGetPos. If joyGetPos is called while joystick input is captured, joystick events occurring close to the time of the joyGetPos call may not be accurately reported to the capture window.

The joySetCapture function captures joystick input to a window function you specify. To release the joystick, call the joyReleaseCapture function.

To capture messages from two joysticks attached to the system, you must call joySetCapture twice, once for each joystick. Your window then receives separate messages for each device.

You cannot capture an unplugged joystick. The joySetCapture function returns zero if successful; it returns JOYERR_UNPLUGGED if the specified device is unplugged.

Note The joystick services set up a Windows timer event with each call to joySetCapture.

Set the period parameter for joySetCapture to a value that falls within the minimum and maximum resolution range for the joystick. To determine the minimum and maximum resolution of the joystick, call the joyGetDevCaps function, which fills the wPeriodMin and wPeriodMax members in the JOYCAPS structure.

If the specified period value is outside the range of valid resolution values for the joystick, the joystick services use the minimum or maximum resolution value, whichever is closer to the specified period.

+$#A Processing Multimedia Timer Joystick Messages

The following joystick messages can be sent to a window function. Numerals 1 and 2 in these messages correspond to the joystick initiating the message. MM_JOY1 messages are sent to the window function if your application requests input from the first joystick, and MM_JOY2 messages are sent if your application requests input from the second joystick. All messages report nonexistent buttons as released.

Messages | Description
MM_JOY1MOVE, MM_JOY2MOVE | Report a change in the x-axis and/or y-axis position of the joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1ZMOVE, MM_JOY2ZMOVE | Report a change in the z-axis position of a 3-axis joystick. The wParam parameter contains a combination of JOY_BUTTON bit flags specifying which buttons were pressed. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.
MM_JOY1BUTTONUP, MM_JOY2BUTTONUP, MM_JOY1BUTTONDOWN, MM_JOY2BUTTONDOWN | Report that a joystick button has been pressed or released. The wParam parameter contains one JOY_BUTTONCHG bit flag specifying which button changed state and a combination of JOY_BUTTON bit flags specifying the current button states. The low-order word of lParam contains the x-position of the joystick, and the high-order word contains the y-position.

The joystick services use the following bit flags, passed in the wParam parameter of the window function, to report the state of the joystick buttons.

Flag | Description
JOY_BUTTON1 | Set when button 1 is pressed.
JOY_BUTTON1CHG | Set when button 1 has changed state.
JOY_BUTTON2 | Set when button 2 is pressed.
JOY_BUTTON2CHG | Set when button 2 has changed state.
JOY_BUTTON3 | Set when button 3 is pressed.
JOY_BUTTON3CHG | Set when button 3 has changed state.
JOY_BUTTON4 | Set when button 4 is pressed.
JOY_BUTTON4CHG | Set when button 4 has changed state.

The MM_JOY*MOVE messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick.

The MM_JOY*BUTTONUP and MM_JOY*BUTTONDOWN messages use the JOY_BUTTON flags to report the state (pressed or released) of all buttons on the specified joystick. Also, they use JOY_BUTTONCHG flags to indicate which button changed state, thereby generating the message.

For example, if the user presses and holds buttons 1 and 2 and then moves the stick, a window function may receive the following messages.

Message | wParam Flags
MM_JOY1BUTTONDOWN | JOY_BUTTON1 | JOY_BUTTON1CHG
MM_JOY1BUTTONDOWN | JOY_BUTTON1 | JOY_BUTTON2 | JOY_BUTTON2CHG
MM_JOY1MOVE | JOY_BUTTON1 | JOY_BUTTON2

A window function may receive the following messages when the user presses and releases button 3 without moving the stick.

Message | wParam Flags
MM_JOY1BUTTONDOWN | JOY_BUTTON3 | JOY_BUTTON3CHG
MM_JOY1BUTTONUP | JOY_BUTTON3CHG

+$#A Releasing the Joystick

When your application no longer needs to receive periodic joystick messages, it should release the joystick by using the joyReleaseCapture function. If your application does not release the joystick before ending, the joystick is released shortly after the capture window is destroyed.

+$#A Setting the Movement Threshold

You can change the movement threshold of the joystick by calling the joySetThreshold function. The movement threshold is the number of device units that the stick must be moved before an MM_JOY*MOVE message is sent to the window that has captured the device.

+$#A Polling the Joystick

You can poll the joystick for position and button information. For example, an application may poll the joystick to get baseline position values; the Joystick Control Panel application uses this technique when calibrating the joystick. The joyGetPos function allows you to poll the joystick for position and button information.

Note Calling joyGetPos while joystick input is captured can prevent the joystick services from accurately reporting joystick events to the capture window.

+$#A Using Joystick Messages

The remainder of this topic presents examples from a simple joystick game that performs the useful function of shooting holes in the desktop: it gets position and button-state information from the joystick services and, when a user presses the joystick buttons, plays waveform resources and paints bullet holes on the screen.

Most of the joystick-control code is in the main window function. In the following WM_CREATE case of the message handler, the application captures input from joystick 1.

case WM_CREATE:

if(joySetCapture(hWnd, JOYSTICKID1, NULL, FALSE)) {

MessageBeep(MB_ICONEXCLAMATION);

MessageBox(hWnd, "Couldn't capture the joystick.", NULL,

MB_OK | MB_ICONEXCLAMATION);

PostMessage(hWnd,WM_CLOSE,0,0L);

}

break;

In response to the MM_JOY1MOVE messages, the application changes the position of the cursor and, if either button is pressed, draws a hole in the desktop.

case MM_JOY1MOVE :

if((UINT) wParam & (JOY_BUTTON1 | JOY_BUTTON2))

DrawFire(hWnd);

DrawSight(lParam); /* Calculate new cursor position */

break;

In response to the MM_JOY1BUTTONDOWN messages, the application uses sndPlaySound to play a waveform audio file.

case MM_JOY1BUTTONDOWN :

if((UINT) wParam & JOY_BUTTON1) {

sndPlaySound(lpButton1,

SND_LOOP | SND_ASYNC | SND_MEMORY);

DrawFire(hWnd);

}

else if(wParam & JOY_BUTTON2) {

sndPlaySound(lpButton2,

SND_ASYNC | SND_MEMORY | SND_LOOP);

DrawFire(hWnd);

}

break;

By specifying the SND_LOOP and SND_ASYNC flags with sndPlaySound, the JOYTOY application repeats the waveform playback until the button is released.

When a button is released, the window function receives a MM_JOY1BUTTONUP message, and handles it with the following statement.

case MM_JOY1BUTTONUP:

sndPlaySound(NULL, 0);

break;

This sequence stops the waveform-audio playback.

+$#A Multimedia Timer and Joystick Functions

Following are the functions used with joystick services.

joyGetDevCaps
joyGetNumDevs
joyGetPos
joyGetThreshold
joyReleaseCapture
joySetCapture
joySetThreshold

Following are the functions used with timer services.

timeBeginPeriod
timeEndPeriod
timeGetDevCaps
timeGetSystemTime
timeGetTime
timeKillEvent
TimeProc
timeSetEvent


National Language Support.rtf

+$#AK National Language Support

National language support functions help applications developed for the Win32 application programming interface (API) adapt to the differing language- and location-specific needs of users around the world.

This overview describes the national language support functions and explains how to use them in your Win32-based applications.

+$#A About National Language Support

The national language support functions let applications set the locale for the user, identifying the language in which the user carries out work, and retrieve strings, representing times, dates and other information, that are correctly formatted for the given language and location of the world.

National language support also includes support for keyboard layouts and language-specific fonts. For more information about these topics, see Keyboard Input and Fonts and Text.

+$#A Locales

A locale is a collection of language-related, user preference information represented as a list of values. Each system has at least one installed locale and usually has many locales from which the user may choose. Each locale has a unique locale identifier (LCID), a 32-bit value that consists in part of a language identifier. The language identifier is a standard international numeric abbreviation for a country or geographical region.

The system assigns a locale to each thread. Initially, the system assigns the system default locale to the thread. This default locale is set by the user when the system is installed or through the International applet of the Control Panel. If a thread is run in a process belonging to a user, the system assigns the user default locale to the thread. An application can override either default by using the the SetThreadLocale function to explicitly set the locale for a thread.

There are two predefined locale identifiers: LOCALE_SYSTEM_DEFAULT, which identifies the system default locale, and LOCALE_USER_DEFAULT, which identifies the locale of the current user. An application can retrieve the current locale identifiers by using the GetSystemDefaultLCID and GetUserDefaultLCID functions. Similarly, an application can retrieve the current language identifiers by using the GetSystemDefaultLangID and GetUserDefaultLangID functions.

An application can retrieve specific information about any available language or locale. This can be important for handling language- and locale-specific strings. An application can retrieve information about a locale by using the GetLocaleInfo function. Each element of locale information has a unique LCTYPE value that identifies a specific information type. Applications use these values to retrieve the information they need.

All LCTYPE values are mutually-exclusive, so only one type of information can be retrieved at a time. An exception is the LOCALE_NOUSEROVERRIDE which can be used to bypasses user overrides and retrieve the system default values for the requested locale.

Locale information is always stored and manipulated as a null-terminated string. No binary data is allowed; any numeric values must be specified as text. Each type of information has a particular format. Also, several of types are linked together, such that changing one changes the value of the other as well.

Although a given locale identifier may be supported, it is not available for use by an application unless it is also installed.

+$#A Time and Date

You can retrieve strings for any given time or date in a format that is appropriate for the current locale by using the GetTimeFormat and GetDateFormat functions.

For GetTimeFormat, the time values in the SYSTEMTIME structure pointed to by lpTime must be valid. The function checks each of the time values to determine that it is within the appropriate range of values. If any of the time values are outside the correct range, the function fails, and sets the last-error to ERROR_INVALID_PARAMETER.

The function ignores the date portions of the SYSTEMTIME structure pointed to by lpTime: wYear, wMonth, wDayOfWeek, and wDay.

If a time marker exists and the TIME_NOTIMEMARKER flag is not set, the function localizes the time marker based on the specified locale identifier. Examples of time markers are "AM" and "PM" for US English, "de." and "du." for Mexican Spanish.

If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function removes the separator(s) preceding the minutes and/or seconds element(s).

If TIME_NOTIMEMARKER is specified, the function removes the separator(s) preceding and following the time marker.

If TIME_FORCE24HOURFORMAT is specified, the function displays any existing time marker, unless the TIME_NOTIMEMARKER flag is also set.

The function does not include milliseconds as part of the formatted time string.

To use the LOCALE_NOUSEROVERRIDE flag, lpFormat must be NULL.

No errors are returned for a bad format string. The function simply forms the best time string that it can. If more than two hour, minute, second, or time marker format pictures are passed in, then the function defaults to two. For example, the only time marker pictures that are valid are L"t" and L"tt" (the 'L' indicates a Unicode (16-bit characters) string). If L"ttt" is passed in, the function assumes L"tt".

For GetDateFormat, the date values in the SYSTEMTIME structure pointed to by lpDate must be valid. The function checks each of the date values: year, month, day, and day of week. If the day of the week is *-+98/incorrect, the function uses the correct value, and returns no error. If any of the other date values are outside the correct range, the function fails, and sets the last-error to ERROR_INVALID_PARAMETER.

The day name, abbreviated day name, month name, and abbreviated month name are all localized based on the given locale identifier. The function ignores the time portions of the SYSTEMTIME structure pointed to by lpDate: wHour, wMinute, wSecond, and wMilliseconds.

The flag DATE_USE_ALT_CALENDAR will always use the format defined as the default format for that alternate calendar, rather than using any user overrides. The user overrides will only be used in the event that there is no default format for the alternate calendar specified.

The DATE_SHORTDATE and DATE_LONGDATE flag options are mutually exclusive. If neither one is specified and lpFormat is NULL, then DATE_SHORTDATE is the default.

If the lpFormat parameter is not NULL, the dwFlags parameter must be zero. None of the flags make sense when the format string is specified.

No errors are returned for a bad format string. The function simply forms the best date string that it can. For example, the only year pictures that are valid are L"yyyy" and L"yy" (the 'L' indicates a Unicode (16-bit characters) string). If L"y" is passed in, the function assumes L"yy". If L"yyy" is passed in, the function assumes L"yyyy". If more than 4 date (L"dddd") or 4 month (L"MMMM") pictures are passed in, then the function defaults to L"dddd" or L"MMMM".

Any text that should remain in its exact form in the date string should be enclosed within single quotes in the date format picture. The single quote may also be used as an escape character to allow the single quote itself to be displayed in the date string. However, the escape sequence must be enclosed within two single quotes. For example, to display the date as "May '93", the format string would be: L"MMMM ''''yy " The first and last single quotes are the enclosing quotes. The second and third single quotes are the escape sequence to allow the single quote to be displayed before the century.

+$#A Date and Calendar

Most locales use the standard Gregorian calendar and a set number of date formats. These "default choices" for date formats are available for display by using the EnumDateFormats function. There are other locales which require special considerations when creating a complete list of format choices. Some of these require text strings to be inserted within the date format string, others require a completely different method of computation of the values. These special requirements are addressed by the addition of new LCTYPE and CALTYPE values.

Each locale identifier has a default calendar type associated with it. A locale identifier can also have an alternate calendar type, which can be set using the LOCALE_IOPTIONALCALENDAR type for this locale.

+$#A Localization and the Shell Font

Windows NT is available in localized editions of many different languages, but the U.S. English edition can also be used to run applications written in other languages. This is true even when the base character sets are different, such as when applications are written in the Central and Eastern European languages, or in Greek. These applications require a user interface (UI) with dialog boxes, icons, and applets that provide information in the application's language, which may be different than the language being used in the current Windows NT UI.

To enable the U.S. English Windows NT user interface, as well as characters outside the Windows character set for the US and Western Europe (code page 1252), to display correctly within applications and in system edit controls, Windows NT uses intelligent font switching based on the currently selected locale. This allows currently shipping localized applications to run on the U.S. English version of Windows NT without modifying the application in any way.

An example of the problem being addressed here is that the U.S. English Windows NT shell font is MS Sans Serif but the shell font for Greek Windows 3.1 is MS Sans Serif Greek. These character sets cannot be directly mapped to each other so simply replacing the MS Sans Serif font with the MS Sans Serif Greek font when the locale is set to Greek does not allow existing applications with this kind of difference to run adequately or to display Greek characters in system menus, dialogs and edit controls. To resolve this problem at run time, Microsoft has defined a new font face name, MS Shell Dlg. It is mapped by an entry in the registry to the appropriate shell font for the currently active locale.

+$#A MS Shell Dlg

MS Shell Dlg is a mapping mechanism that enables U.S. English Windows NT to support non-code page 1252 locales. It is not a font but only a face name for a nonexistent font. It can be specified in either the Windows NT Setup file during the installation process or when customizing a local system by double-clicking on the Control Panel's International icon. When you double-click on the icon, select the appropriate Country and Language in the International dialog box, and then reboot the system, the appropriate change occurs in the Registry under the following key:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\Current Version\FontSubstitutes

An added benefit of this new means of font mapping in the registry is that you can change the font for the entire shell by simply changing one .inf file entry. For example, Far East localization is much easier because you can change the .inf file entry, instead of changing every instance of MS Sans Serif in every .rc and .dlg file, and the change is complete without rebuilding any binaries.

Windows NT applications can also take advantage of this feature. It is very helpful when your application contains multiple language resources that cross character-set boundaries. It is also useful when you have an English application in which supporting multiple languages and character sets is important.

+$#A Windows 95 and MS Shell Dlg

Windows 95 does not support the concept of switching Windows character sets so the role of MS Shell Dlg is more limited. It does, however, facilitate localization in that Windows 95 is equipped with a static entry to replace the standard system font. For example, the system font, MS San Serif, is easily replaced with MS San Serif Greek or another language. This also ensures compatibility with the applets provided with Windows NT.

+$#A When MS Shell Dlg Should Not Be Used

At this time, MS Shell Dlg is not intended for use when you are working with a static, non-English UI application that you want to run when the user has chosen a locale with a different Windows base character set. This is because the application's UI language may not be supported with the font that is substituted for MS Shell Dlg. For example, if you are using a German localized version of Windows NT and want to install a non-Unicode, Greek application, you can use the Control Panel's International applet to change the locale to Greek. This will reset MS Shell Dlg to be a Greek font but this font does not contain all of the glyphs necessary to display German. Therefore, any non-ASCII characters in your German UI will not be displayed properly. To support this scenario, you would have to set MS Shell Dlg to a font that contains both the Western European and the Greek glyphs.

+$#A National Language Support Reference

This section provides detailed reference information for the national language support functions, structures, and messages. The information is separated into the following functional groups.

+$#A National Language Support Functions

This section describes the national language support functions. Many national language support functions generate extended error information on errors. Use the GetLastError function to get this information.

SetLocaleInfo

+$#A National Language Support Macros

This section describes the macros used with the national language support functions.

LANGIDFROMLCID
MAxKELANGID
MAKELCID

+$#A National Language Support Constants

This section describes the constants used with the national language support functions.

+$#A Locale Information

LOCALE_ILANGUAGE

Language identifier indicating the language. The maximum number of characters allowed for this string is 5.

LOCALE_SLANGUAGE

Full localized name of the language.

LOCALE_SENGLANGUAGE

Full English name of the language from the International Organization for Standardization (ISO) Standard 639. This is always restricted to characters mappable into the ASCII 127-character subset.

LOCALE_SABBREVLANGNAME

Abbreviated name of the language, created by taking the 2-letter language abbreviation from the ISO Standard 639 and adding a third letter, as appropriate, to indicate the sublanguage.

LOCALE_SNATIVELANGNAME

Native name of the language.

LOCALE_ICOUNTRY

Country code, based on international phone codes, also referred to as IBM country codes. The maximum number of characters allowed for this string is 6.

LOCALE_SCOUNTRY

Full localized name of the country.

LOCALE_SENGCOUNTRY

Full English name of the country. This is always restricted to characters mappable into the ASCII 127-character subset.

LOCALE_SABBREVCTRYNAME

Abbreviated name of the country from the ISO Standard 3166.

LOCALE_SNATIVECTRYNAME

Native name of the country.

LOCALE_IDEFAULTLANGUAGE

Language identifier for the principal language spoken in this locale. This is provided so that partially specified locales can be completed with default values. The maximum number of characters allowed for this string is 5.

LOCALE_IDEFAULTCOUNTRY

Country code for the principal country in this locale. This is provided so that partially specified locales can be completed with default values. The maximum number of characters allowed for this string is 6.

LOCALE_IDEFAULTANSICODEPAGE

American National Standards Institute (ANSI) code page associated with this locale. The maximum number of characters allowed for this string is 6.

LOCALE_IDEFAULTOEMCODEPAGE

Original equipment manufacturer (OEM) code page associated with the locale. The maximum number of characters allowed for this string is 6.

LOCALE_IDEFAULTCODEPAGE

Original equipment manufacturer (OEM) code page associated with the country. The maximum number of characters allowed for this string is 6.

LOCALE_SLIST

Character(s) used to separate list items. For example, a comma is used in many locales.

LOCALE_IMEASURE

System of measurement. This value is 0 if the metric system (Systéme International d'Unités, or S.I.) is used and 1 if the U.S. system is used. The maximum number of characters allowed for this string is 2.

LOCALE_SDECIMAL

Character(s) used as the decimal separator.

LOCALE_STHOUSAND

Character(s) used to separate groups of digits to the left of the decimal.

LOCALE_SGROUPING

Sizes for each group of digits to the left of the decimal. An explicit size is needed for each group; sizes are separated by semicolons. If the last value is zero, the preceding value is repeated. To group thousands, specify 3;0, for example.

LOCALE_IDIGITS

Number of fractional digits. The maximum number of characters allowed for this string is 3.

LOCALE_ILZERO

Specifier for leading zeros in decimal fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | No leading zeros
1 | Leading zeros

LOCALE_INEGNUMBER

Negative number mode. The mode can be one of these values:

0 | (1.1)
1 | -1.1
2 | - 1.1
3 | 1.1-
4 | 1.1 -

LOCALE_SNATIVEDIGITS

Native equivalents to ASCII 0 through 9.

LOCALE_SCURRENCY

String used as the local monetary symbol.

LOCALE_SINTLSYMBOL

Three characters of the international monetary symbol specified in ISO 4217, "Codes for the Representation of Currencies and Funds," followed by the character separating this string from the amount.

LOCALE_SMONDECIMALSEP

Character(s) used as the monetary decimal separator.

LOCALE_SMONTHOUSANDSEP

Character(s) used as the monetary separator between groups of digits to the left of the decimal.

LOCALE_SMONGROUPING

Sizes for each group of monetary digits to the left of the decimal. An explicit size is needed for each group; sizes are separated by semicolons. If the last value is zero, the preceding value is repeated. To group thousands, specify 3;0, for example.

LOCALE_ICURRDIGITS

Number of fractional digits for the local monetary format. The maximum number of characters allowed for this string is 3.

LOCALE_IINTLCURRDIGITS

Number of fractional digits for the international monetary format. The maximum number of characters allowed for this string is 3.

LOCALE_ICURRENCY

Positive currency mode. The maximum number of characters allowed for this string is 2. The mode can be one of the following values:

0 | Prefix, no separation
1 | Suffix, no separation
2 | Prefix, 1-char. separation
3 | Suffix, 1-char. separation

LOCALE_INEGCURR

Negative currency mode. The maximum number of characters allowed for this string is 3. The mode can be one of the following values:

0 | ($1.1)
1 | −$1.1
2 | $−1.1
3 | $1.1−
4 | (1.1$)
5 | −1.1$
6 | 1.1−$
7 | 1.1$−
8 | −1.1 $ (space before $)
9 | −$ 1.1 (space after $)
10 | 1.1 $− (space before $)
11 | $ 1.1− (space after $)
12 | $ −1.1 (space after $)
13 | 1.1− $ (space before $)
14 | ($ 1.1) (space after $)
15 | (1.1 $) (space before $)

LOCALE_SDATE

Character(s) for the date separator.

LOCALE_STIME

Character(s) for the time separator.

LOCALE_STIMEFORMAT

Time formatting strings for this locale. The string can consist of a combination of the hour, minute, and second format pictures defined in in the Hour, Minute, and Second Format Pictures table in National Language Support Constants.

LOCALE_SSHORTDATE

Short date formatting string for this locale. The string can consist of a combination of day, month, and year format pictures defined in in Day, Month, Year, and Era Format Pictures table in National Language Support Constants.

LOCALE_SLONGDATE

Long date formatting string for this locale. The string can consist of a combination of day, month, and year format pictures defined in in the Day, Month, Year, and Era Format Pictures table in National Language Support Constantsand any string of characters enclosed in single quotes. Characters in single quotes remain as given.

LOCALE_IDATE

Short date format-ordering specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | Month-Day-Year
1 | Day-Month-Year
2 | Year-Month-Day

LOCALE_ILDATE

Long date format-ordering specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | Month-Day-Year
1 | Day-Month-Year
2 | Year-Month-Day

LOCALE_ITIME

Time format specifier. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | AM / PM 12-hour format
1 | 24-hour format

LOCALE_ICENTURY

Specifier for full 4-digit century. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | Abbreviated 2-digit century
1 | Full 4-digit century

LOCALE_ITLZERO

Specifier for leading zeros in time fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | No leading zeros for hours
1 | Leading zeros for hours

LOCALE_IDAYLZERO

Specifier for leading zeros in day fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | No leading zeros for days
1 | Leading zeros for days

LOCALE_IMONLZERO

Specifier for leading zeros in month fields. The maximum number of characters allowed for this string is 2. The specifier can be one of the following values:

0 | No leading zeros for months
1 | Leading zeros for months

LOCALE_S1159

String for the AM designator.

LOCALE_S2359

String for the PM designator.

LOCALE_ICALENDARTYPE

Current calendar type. This type can be one of these values:

1 | Gregorian (as in United States)
2 | Gregorian (English strings always)
3 | Era: Year of the Emperor (Japan)
4 | Era: Year of the Republic of China
5 | Tangun Era (Korea)

LOCALE_IOPTIONALCALENDAR

Additional calendar types. This can be a zero-separated list of one or more of these calendars type values:

0 | No additional types valid
1 | Gregorian (as in United States)
2 | Gregorian (English strings always)
3 | Era: Year of the Emperor (Japan)
4 | Era: Year of the Republic of China
5 | Tangun Era (Korea)

LOCALE_IFIRSTDAYOFWEEK

Specifier for the first day in a week. The specifier can be one of these values:

0 | LOCALE_SDAYNAME1
1 | LOCALE_SDAYNAME2
2 | LOCALE_SDAYNAME3
3 | LOCALE_SDAYNAME4
4 | LOCALE_SDAYNAME5
5 | LOCALE_SDAYNAME6
6 | LOCALE_SDAYNAME7

LOCALE_IFIRSTWEEKOFYEAR

Specifier for the first week of the year. The specifier can be one of these values:

0 | Week containing 1/1 is the first week of that year.
1 | First full week following 1/1 is the first week of that year.
2 | First week containing at least 4 days is the first week of that year.

LOCALE_SDAYNAME1

Native long name for Monday.

LOCALE_SDAYNAME2

Native long name for Tuesday.

LOCALE_SDAYNAME3

Native long name for Wednesday.

LOCALE_SDAYNAME4

Native long name for Thursday.

LOCALE_SDAYNAME5

Native long name for Friday.

LOCALE_SDAYNAME6

Native long name for Saturday.

LOCALE_SDAYNAME7

Native long name for Sunday.

LOCALE_SABBREVDAYNAME1

Native abbreviated name for Monday.

LOCALE_SABBREVDAYNAME2

Native abbreviated name for Tuesday.

LOCALE_SABBREVDAYNAME3

Native abbreviated name for Wednesday.

LOCALE_SABBREVDAYNAME4

Native abbreviated name for Thursday.

LOCALE_SABBREVDAYNAME5

Native abbreviated name for Friday.

LOCALE_SABBREVDAYNAME6

Native abbreviated name for Saturday.

LOCALE_SABBREVDAYNAME7

Native abbreviated name for Sunday.

LOCALE_SMONTHNAME1

Native long name for January.

LOCALE_SMONTHNAME2

Native long name for February.

LOCALE_SMONTHNAME3

Native long name for March.

LOCALE_SMONTHNAME4

Native long name for April.

LOCALE_SMONTHNAME5

Native long name for May.

LOCALE_SMONTHNAME6

Native long name for June.

LOCALE_SMONTHNAME7

Native long name for July.

LOCALE_SMONTHNAME8

Native long name for August.

LOCALE_SMONTHNAME9

Native long name for September.

LOCALE_SMONTHNAME10

Native long name for October.

LOCALE_SMONTHNAME11

Native long name for November.

LOCALE_SMONTHNAME12

Native long name for December.

LOCALE_SMONTHNAME13

Native name for 13th month, if exists.

LOCALE_SABBREVMONTHNAME1

Native abbreviated name for January.

LOCALE_SABBREVMONTHNAME2

Native abbreviated name for February.

LOCALE_SABBREVMONTHNAME3

Native abbreviated name for March.

LOCALE_SABBREVMONTHNAME4

Native abbreviated name for April.

LOCALE_SABBREVMONTHNAME5

Native abbreviated name for May.

LOCALE_SABBREVMONTHNAME6

Native abbreviated name for June.

LOCALE_SABBREVMONTHNAME7

Native abbreviated name for July.

LOCALE_SABBREVMONTHNAME8

Native abbreviated name for August.

LOCALE_SABBREVMONTHNAME9

Native abbreviated name for September.

LOCALE_SABBREVMONTHNAME10

Native abbreviated name for October.

LOCALE_SABBREVMONTHNAME11

Native abbreviated name for November.

LOCALE_SABBREVMONTHNAME12

Native abbreviated name for December.

LOCALE_SABBREVMONTHNAME13

Native abbreviated name for 13th month, if exists.

LOCALE_SPOSITIVESIGN

String value for the positive sign.

LOCALE_SNEGATIVESIGN

String value for the negative sign.

LOCALE_IPOSSIGNPOSN

Formatting index for positive values. The maximum number of characters allowed for this string is 2. The index can be one of the following values:

0 | Parentheses surround the amount and the monetary symbol.
1 | The sign string precedes the amount and the monetary symbol.
2 | The sign string succeeds the amount and the monetary symbol.
3 | The sign string immediately precedes the monetary symbol.
4 | The sign string immediately succeeds the monetary symbol.

LOCALE_INEGSIGNPOSN

Formatting index for negative values. This index uses the same values as LOCALE_IPOSSIGNPOSN. The maximum number of characters allowed for this string is 2.

LOCALE_IPOSSYMPRECEDES

Position of monetary symbol in a positive monetary value. This value is 1 if the monetary symbol precedes the positive amount, 0 if it follows it. The maximum number of characters allowed for this string is 2.

LOCALE_IPOSSEPBYSPACE

Separation of monetary symbol in a positive monetary value. This value is 1 if the monetary symbol is separated by a space from a positive amount, 0 if it is not. The maximum number of characters allowed for this string is 2.

LOCALE_INEGSYMPRECEDES

Position of monetary symbol in a negative monetary value. This value is 1 if the monetary symbol precedes the negative amount, 0 if it follows it. The maximum number of characters allowed for this string is 2.

LOCALE_INEGSEPBYSPACE

Separation of monetary symbol in a negative monetary value. This value is 1 if the monetary symbol is separated by a space from the negative amount, 0 if it is not. The maximum number of characters allowed for this string is 2.

LOCALE_NOUSEROVERRIDE

Can be combined with other LOCALE values to bypass any user override and return the system default value for the given locale information.

Many of the locale types listed above are closely related, such that changing one affects the value of others. The following shows the relationships between these types.

LOCALE_ICURRENCY

LOCALE_IPOSSEPBYSPACE, LOCALE_IPOSSYMPRECEDES

LOCALE_INEGCURR

LOCALE_INEGSEPBYSPACE, LOCALE_INEGSYMPRECEDES, LOCALE_INEGSIGNPOSN, LOCALE_IPOSSIGNPOSN

LOCALE_SSHORTDATE

LOCALE_SDATE, LOCALE_IDATE

LOCALE_SLONGDATE

LOCALE_ILDATE

LOCALE_STIMEFORMAT

LOCALE_STIME, LOCALE_ITIME, LOCALE_ITLZERO

+$#A Day, Month, Year, and Era Format Pictures

The format picture for a date string consists of a combination of null-terminated strings.

Day |
d | Day of the month as digits without leading zeros for single digit days.
dd | Day of the month as digits with leading zeros for single digit days
ddd | Day of the week as a 3-letter abbreviation as given by a LOCALE_SABBREVDAYNAME value.
dddd | Day of the week as given by a LOCALE_SDAYNAME value.
Month |
M | Month as digits without leading zeros for single digit months.
MM | Month as digits with leading zeros for single digit months
MMM | Month as a three letter abbreviation as given by a LOCALE_SABBREVMONTHNAME value.
MMMM | Month as given by a LOCALE_SMONTHNAME value.
Year |
y | Year represented only be the last digit.
yy | Year represented only be the last two digits.
yyyy | Year represented by the full 4 digits.
Period/Era |
gg | Period/era string as given by the CAL_SERASTRING value.

The "gg" format picture in a date string is ignored if there is no associated era string.

+$#A Hour, Minute, and Second Format Pictures

The format picture for a time string consists of a combination of one string from each of these format types:

Hours |
h | Hours without leading zeros for single digit hours (12 hour clock)
hh | Hours with leading zeros for single digit hours (12 hour clock)
H | Hours without leading zeros for single digit hours (24 hour clock)
HH | Hours with leading zeros for single digit hours (24 hour clock)
Minutes |
m | Minutes without leading zeros for single digit minutes
mm | Minutes with leading zeros for single digit minutes
Seconds |
s | Seconds without leading zeros for single digit seconds
ss | Seconds with leading zeros for single digit seconds
Time Marker |
t | One character time marker string
tt | Multi-character time marker string

+$#A Primary Language Identifiers

0x00 | Neutral
0x36 | Afrikaans
0x1c | Albanian
0x01 | Arabic
0x2d | Basque
0x02 | Bulgarian
0x23 | Byelorussian
0x03 | Catalan
0x04 | Chinese
0x05 | Czech
0x06 | Danish
0x13 | Dutch
0x09 | English
0x25 | Estonian
0x29 | Farsi
0x0b | Finnish
0x0c | French
0x07 | German
0x08 | Greek
0x0d | Hebrew
0x0e | Hungarian
0x0f | Icelandic
0x21 | Indonesian
0x10 | Italian
0x11 | Japanese
0x2c | Kampuchean
0x12 | Korean
0x2b | Laotian
0x26 | Latvian
0x27 | Lithuanian
0x2f | Macedonian
0x28 | Maori
0x14 | Norwegian
0x15 | Polish
0x16 | Portuguese
0x17 | Rhaeto Roman
0x18 | Romanian
0x19 | Russian
0x1a | Serbo Croatian
0x1b | Slovak
0x24 | Slovenian
0x2e | Sorbian
0x0a | Spanish
0x30 | Sutu
0x1d | Swedish
0x1e | Thai
0x31 | Tsonga
0x32 | Tswana
0x1f | Turkish
0x22 | Ukrainian
0x20 | Urdu
0x33 | Venda
0x2a | Vietnamese
0x34 | Xhosa
0x35 | Zulu

+$#A Secondary Language Identifiers

0x00 | Neutral
0x01 | Default
0x02 | System Default
0x01 | Arabic (Saudi Arabia)
0x02 | Arabic (Iraq)
0x03 | Arabic (Egypt)
0x04 | Arabic (Libya)
0x05 | Arabic (Algeria)
0x06 | Arabic (Morocco)
0x07 | Arabic (Tunisia)
0x08 | Arabic (Oman)
0x09 | Arabic (Yemen)
0x10 | Arabic (Syria)
0x11 | Arabic (Jordan)
0x12 | Arabic (Lebanon)
0x13 | Arabic (Kuwait)
0x14 | Arabic (U.A.E.)
0x15 | Arabic (Bahrain)
0x16 | Arabic (Qatar)
0x01 | Chinese (Taiwan)
0x02 | Chinese (PRC)
0x03 | Chinese (Hong Kong)
0x04 | Chinese (Singapore)
0x01 | Dutch
0x02 | Dutch (Belgian)
0x01 | English (US)
0x02 | English (UK)
0x03 | English (Australian)
0x04 | English (Canadian)
0x05 | English (New Zealand)
0x06 | English (Ireland)
0x01 | French
0x02 | French (Belgian)
0x03 | French (Canadian)
0x04 | French (Swiss)
0x05 | French (Luxembourg)
0x01 | German
0x02 | German (Swiss)
0x03 | German (Austrian)
0x04 | German (Luxembourg)
0x05 | German (Liechtenstein)
0x04 | Hebrew (Israel)
0x01 | Italian
0x02 | Italian (Swiss)
0x01 | Norwegian (Bokmal)
0x02 | Norwegian (Nynorsk)
0x01 | Portuguese (Brazilian)
0x02 | Portuguese
0x01 | Serbo Croatian (Latin)
0x02 | Serbo Croatian (Cyrillic)
0x01 | Spanish (Traditional Sort)
0x02 | Spanish (Mexican)
0x03 | Spanish (Modern Sort)

+$#A Language Identifiers

Identifier | Locale
0x0436 | Afrikaans
0x041C | Albania
0x1401 | Algeria
0x0409 | American
0x0C09 | Australian
0x0C07 | Austrian
0x3C01 | Bahrain
0x042D | Basque
0x080C | Belgian
0x0813 | Belgian (Flemish)
0x0809 | British
0x0402 | Bulgaria
0x0423 | Byelorussia
0x1009 | Canadian
0x0403 | Catalan
0x041A | Croatian
0x0405 | Czech
0x0406 | Danish
0x0413 | Dutch (Standard)
0x0C01 | Egypt
0x0425 | Estonia
0x0429 | Farsi
0x040B | Finnish
0x040C | French (Standard)
0x0C0C | French Canadian
0x0407 | German (Standard)
0x042E | Germany
0x0408 | Greek
0x0C04 | Hong Kong
0x040E | Hungarian
0x040F | Icelandic
0x0421 | Indonesia
0x0801 | Iraq
0x1809 | Ireland
0x040D | Israel
0x0410 | Italian (Standard)
0x0411 | Japan
0x2C01 | Jordan
0x0412 | Korea
0x3401 | Kuwait
0x0426 | Latvia
0x3001 | Lebanon
0x1001 | Libya
0x1407 | Liechtenstein
0x0427 | Lithuania
0x140C | Luxembourg (French)
0x1007 | Luxembourg (German)
0x042f | Macedonia
0x080A | Mexican
0x0818 | Moldavia
0x0819 | Moldavia
0x1801 | Morocco
0x1409 | New Zealand
0x0414 | Norwegian (Bokmal)
0x0814 | Norwegian (Nynorsk)
0x2001 | Oman
0x0415 | Polish
0x0416 | Portuguese (Brazilian)
0x0816 | Portuguese (Standard)
0x0804 | PRC
0x4001 | Qatar
0x0417 | Rhaeto-Romanic
0x0418 | Romania
0x0419 | Russian
0x0401 | Saudi Arabia
0x081A | Serbian
0x1004 | Singapore
0x041B | Slovak
0x0424 | Slovenia
0x0C0A | Spanish (Modern Sort)
0x040A | Spanish (Traditional Sort)
0x0430 | Sutu
0x041D | Swedish
0x100C | Swiss (French)
0x0807 | Swiss (German)
0x0810 | Swiss (Italian)
0x2801 | Syria
0x0404 | Taiwan
0x041E | Thailand
0x0431 | Tsonga
0x0432 | Tswana
0x1C01 | Tunisia
0x041f | Turkish
0x3801 | U.A.E.
0x0422 | Ukraine
0x0420 | Urdu
0x0433 | Venda
0x0435 | Xhosa
0x2401 | Yemen
0x0436 | Zulu

+$#A Calendar Type Information

CAL_ICALINTVALUE

An integer value indicating the calendar type of the alternate calendar.

CAL_IYEAROFFSETRANGE

One or more null-terminated strings that specify the year offsets for each of the era ranges. The last string has an extra terminating null character.

CAL_SABBREVDAYNAME1

Abbreviated native name of the first day of the week.

CAL_SABBREVDAYNAME2

Abbreviated native name of the second day of the week.

CAL_SABBREVDAYNAME3

Abbreviated native name of the third day of the week.

CAL_SABBREVDAYNAME4

Abbreviated native name of the fourth day of the week.

CAL_SABBREVDAYNAME5

Abbreviated native name of the fifth day of the week.

CAL_SABBREVDAYNAME6

Abbreviated native name of the sixth day of the week.

CAL_SABBREVDAYNAME7

Abbreviated native name of the seventh day of the week.

CAL_SABBREVMONTHNAME1

Abbreviated native name of the first month of the year.

CAL_SABBREVMONTHNAME2

Abbreviated native name of the second month of the year.

CAL_SABBREVMONTHNAME3

Abbreviated native name of the third month of the year.

CAL_SABBREVMONTHNAME4

Abbreviated native name of the fourth month of the year.

CAL_SABBREVMONTHNAME5

Abbreviated native name of the fifth month of the year.

CAL_SABBREVMONTHNAME6

Abbreviated native name of the sixth month of the year.

CAL_SABBREVMONTHNAME7

Abbreviated native name of the seventh month of the year.

CAL_SABBREVMONTHNAME8

Abbreviated native name of the eighth month of the year.

CAL_SABBREVMONTHNAME9

Abbreviated native name of the ninth month of the year.

CAL_SABBREVMONTHNAME10

Abbreviated native name of the tenth month of the year.

CAL_SABBREVMONTHNAME11

Abbreviated native name of the eleventh month of the year.

CAL_SABBREVMONTHNAME12

Abbreviated native name of the twelfth month of the year.

CAL_SABBREVMONTHNAME13

Abbreviated native name of the thirteenth month of the year, if it exists.

CAL_SCALNAME

The native name of the alternate calendar.

CAL_SDAYNAME1

Native name of the first day of the week.

CAL_SDAYNAME2

Native name of the second day of the week.

CAL_SDAYNAME3

Native name of the third day of the week.

CAL_SDAYNAME4

Native name of the fourth day of the week.

CAL_SDAYNAME5

Native name of the fifth day of the week.

CAL_SDAYNAME6

Native name of the sixth day of the week.

CAL_SDAYNAME7

Native name of the seventh day of the week.

CAL_SERASTRING

One or more null-terminated strings that specify each of the Unicode codepoints specifying the era associated with the given CAL_IYEAROFFSETRANGE. The last string has an extra terminating null character. See example below.

CAL_SLONGDATE

Long date formats for this calendar type.

CAL_SMONTHNAME1

Native name of the first month of the year.

CAL_SMONTHNAME2

Native name of the second month of the year.

CAL_SMONTHNAME3

Native name of the fifth month of the year.

CAL_SMONTHNAME4

Native name of the fourth month of the year.

CAL_SMONTHNAME5

Native name of the fifth month of the year.

CAL_SMONTHNAME6

Native name of the sixth month of the year.

CAL_SMONTHNAME7

Native name of the seventh month of the year.

CAL_SMONTHNAME8

Native name of the eighth month of the year.

CAL_SMONTHNAME9

Native name of the ninth month of the year.

CAL_SMONTHNAME10

Native name of the tenth month of the year.

CAL_SMONTHNAME11

Native name of the eleventh month of the year.

CAL_SMONTHNAME12

Native name of the twelfth month of the year.

CAL_SMONTHNAME13

Native name of the thirteenth month of the year, if it exists.

CAL_SSHORTDATE

Short date formats for this calendar type.

If the native name for the day of the week or for a month is an empty string, that name is identical to the name given in the corresponding locale information and therefore is not duplicated here.

The CAL_IYEAROFFSETRANGE and CAL_SERASTRING values vary in format depending on the type of optional calendar. The following example shows the values for these types (for each supported alternate calendar type) along with the formula for how to use the CAL_IYEAROFFSETRANGE value to compute the correct year given the Gregorian current year value Y:

CAL_ICALINTVALUE = "1"
CAL_IYEAROFFSETRANGE = ""
CAL_SERASTRING = ""

CAL_ICALINTVALUE = "2"
CAL_IYEAROFFSETRANGE = ""
CAL_SERASTRING = ""

CAL_ICALINTVALUE = "3"
CAL_IYEAROFFSETRANGE = "1989\01926\01912\01868\0"
CAL_SERASTRING = "Ux337B\0Ux337C\0Ux337D\0Ux337E\0"
if (Y>=1989) { Y = (Y-1989)+1; }
if (Y>=1926 && Y<1989) { Y = (Y-1926)+1; }
if (Y>=1912 && Y<1926) { Y = (Y-1912)+1; }
if (Y>=1868 && Y<1912) { Y = (Y-1868)+1; }
if (Y<1868) { Y = Y; }

CAL_ICALINTVALUE = "4"
CAL_IYEAROFFSETRANGE = "1912\0"
CAL_SERASTRING = "Ux4E2D\0Ux83EF\0Ux6C11\0Ux570B\0"
if (Y>=1912) { Y = (Y-1912)+1; }
if (Y<1912) { Y = Y; }

CAL_ICALINTVALUE = "5"
CAL_IYEAROFFSETRANGE = "2333\0"
CAL_SERASTRING = ""
Y = Y+2333;

+$#A Language Code and Scripts

Language/Locale | Code | Script
Greek | ELL | Other
Russian | RUS | Cyrillic
Turkish | TRK | Latin 2
Polish | PLK | Latin 2
Czech | CSY | Latin 2
Slovak | SKY | Latin 2
Hungarian | HUN | Latin 2
Danish | DAN | Latin 1
Dutch (Standard) | NLD | Latin 1
Belgian (Flemish) | NLB | Latin 1
English (American) | ENU | Latin 1
English (British) | ENG | Latin 1
English (Australian) | ENA | Latin 1
English (Canadian) | ENC | Latin 1
English (New Zealand) | ENZ | Latin 1
English (Ireland) | ENI | Latin 1
Finnish | FIN | Latin 1
French (Standard) | FRA | Latin 1
French (Belgian) | FRB | Latin 1
French (Canadian) | FRC | Latin 1
French (Swiss) | FRS | Latin 1
German (Standard) | DEU | Latin 1
German (Swiss) | DES | Latin 1
German (Austrian) | DEA | Latin 1
Icelandic | ISL | Latin 1
Italian (Standard) | ITA | Latin 1
Italian (Swiss) | ITS | Latin 1
Norwegian (Bokmal) | NOR | Latin 1
Norwegian (Nynorsk) | NON | Latin 1
Portuguese (Brazilian) | PTB | Latin 1
Portuguese (Standard) | PTG | Latin 1
Swedish | SVE | Latin 1
Spanish (Standard/Traditional) | ESP | Latin 1
Spanish (Mexican) | ESM | Latin 1
Spanish (Modern) | ESN | Latin 1

Créé avec HelpNDoc Personal Edition: Produire des livres EPub gratuitement


Network DDE.rtf

+$#AK Network DDE

The Micrsoft® Win32® application programming interface (API) provides network dynamic data exchange (network DDE) services.

+$#A About Network DDE

Dynamic data exchange lets a process establish data conversations with other processes executing on the same computer. Network DDE lets a process establish data conversations with other processes executing on other computers over a network.

+$#AK Network DDE Reference

The following functions and structures are associated with network dynamic data exchange.

+$#A Network DDE Functions

Use these functions to implement network DDE functionality:

NDdeGetErrorString
NDdeGetShareSecurity
NDdeGetTrustedShare
NDdeIsValidAppTopicList
NDdeIsValidShareName
NDdeSetShareSecurity
NDdeSetTrustedShare
NDdeShareAdd
NDdeShareDel
NDdeShareEnum
NDdeShareGetInfo
NDdeShareSetInfo
NDdeTrustedShareEnum

+$#A Network DDE Structures

Use this structure to implement network DDE functionality:

NDDESHAREINFO

Créé avec HelpNDoc Personal Edition: Sites web iPhone faciles


Networks.rtf

+$#AK Networks

An application written for Microsoft® Windows® can use the network functions in the Microsoft® Win32® application programming interface (API) to implement networking capabilities without making allowances for a particular network provider or physical network implementation.

+$#A About Networks

Win32 network functions are network independent. Applications can use these functions to add and cancel network connections and to retrieve information about the current configuration of the network. In addition, the Win32 API implements a version of the NetBIOS network interface for applications that require it.

In addition to using the network functions, processes can also use the Win32 API implementations of mailslots and named pipes to communicate with one another. For more information about these subjects, see Mailslots and Pipes.

The following figure shows the structure of a typical network.

{ewc msdncd, EWGraphic, group10691 0 /a "SDK_01.bmp"}

In the preceding figure, the hierarchy for Microsoft Advanced Server resources is given in detail as Network provider #1. Network resources from other providers have different hierarchical systems. An application does not need information about the hierarchy before it begins to work with a network. It can proceed from the network root (that is, the topmost container resource) and retrieve information about the network's resources as the information is required.

Network resources that contain other resources are called containers. Container resources are in boxes in the preceding figure.

Resources that do not contain other resources are objects. Sharepoint #1 and Sharepoint #2 in the figure are objects. A sharepoint is an object that is accessible across the network, such as a printer or a shared directory.

+$#A Network-Independent Functions

Windows version 3.1 included the following network-independent network functions:

WNetAddConnection
WNetCancelConnection
WNetGetConnection

The Win32 API supports these functions and has added the following network and network enumeration functions:

WNetAddConnection2 | WNetEnumResource
WNetCancelConnection2 | WNetGetLastError
WNetCloseEnum | WNetGetUser
WNetConnectionDialog | WNetOpenEnum
WNetDisconnectDialog |

Many of these functions use a NETRESOURCE structure, which is also part of the Win32 API. WNetAddConnection and WNetCancelConnection are supported for compatibility with earlier versions of Windows. However, new applications typically use WNetAddConnection2 and WNetCancelConnection2 instead.

An application can use the network functions to browse, add, or cancel network connections anywhere in the hierarchy. By starting at the network root, the resources from all network providers can be enumerated.

A persistent connection is a network connection that Windows automatically restores when the user logs on. Using WNetAddConnection2 and WNetCancelConnection2, an application can control whether a network connection is persistent from one session to the next.

+$#A The Netbios Function

The Netbios function is provided primarily for applications that were written for the NetBIOS interface and need to be ported to Windows. Applications not requiring compatibility with NetBIOS can usually use other interfaces, such as mailslots and named pipes, to accomplish tasks similar to those supported by Netbios.

Netbios in the Win32 API takes one parameter: a pointer to a structure describing a network control block (NCB). The NCB structure contains information about the network environment and a pointer to a buffer that is used for messages or for further data about the network.

+$#A Windows NetBios Enhancements

The Win32 implementation of the Netbios function includes the following enhancements that are not part of the NetBIOS 3.0 specification:

• POST routines can be called from the C programming language by means of the ncb_post member of the NCB structure.

• Each asynchronous network control block can supply an event (in the ncb_event member of the NCB structure) set to the signaled state when the NCB completes.

• An application can enumerate all available LAN adapters by using the NCBENUM command in the ncb_command member of the NCB structure.

+$#A Differences Between Windows and NetBIOS

In addition to the enhancements listed in the preceding section, the following differences exist between the Win32 implementation of the Netbios function and the NetBIOS 3.0 specification:

• The system does not monitor the number of outstanding NCB commands specified when the NCBRESET command is issued. The memory quota limits for Windows are used to limit the total resources an application can use.

• Asynchronous commands on invalid sessions are rejected when they are submitted.

• The numbers of the network names (the ncb_num member of the NCB structure) are assigned on a per-process basis.

• Requests for the status of the local adapter retrieve only the names that were added by the process making the request.

• The value 1 for the ncb_lana_num member of the NCB structure is not exclusive when the NCBRESET command is issued. All MS-DOS and 16-bit Windows-based applications also share access to name number 1.

+$#A Using Networks

The Windows network functions enable your application to query and control network connections directly or to give direct control of the network connections to the user. To use these functions, you must link to the multiple-provider router library (MPR.LIB). This section discusses how to use the network functions in your application.

• Using the connections dialog box

• Enumerating network resources

• Adding a network connection

• Retrieving the connection name

• Retrieving the user name

• Canceling a network connection

• Retrieving network errors

+$#A Using the Connections Dialog Box

The WNetConnectionDialog function creates a dialog box that allows the user to browse and connect to network resources. Conversely, the WNetDisconnectDialog function creates a dialog box that allows the user to disconnect from them.

The following example shows how to use the WNetConnectionDialog function to create a dialog box that displays disk resources.

DWORD dwResult;

dwResult = WNetConnectionDialog(hwnd,

RESOURCETYPE_DISK);

if(dwResult != NO_ERROR) {

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetConnectionDialog");

return FALSE;

}

+$#A Enumerating Network Resources

To enumerate a network container resource, your application should pass the address of a NETRESOURCE structure to the WNetOpenEnum function. WNetOpenEnum creates a handle to the resource described by the NETRESOURCE structure. The application then passes this handle to the WNetEnumResource function, which returns information about the resource in the form of an array of NETRESOURCE structures. When the handle is no longer needed, the application can close it by calling the WNetCloseEnum function.

Your application can continue enumerating any container resource described in the array of NETRESOURCE structures retrieved by WNetEnumResource. If the dwUsage member of the NETRESOURCE structure is RESOURCEUSAGE_CONTAINER, the application can pass the address of that structure to WNetOpenEnum to open the container and continue the enumeration. If dwUsage is RESOURCEUSAGE_CONNECTABLE, the application can pass the structure's address to the WNetAddConnection2 function.

The following example illustrates an application-defined function (EnumerateFunc) that enumerates all the resources on a network. When calling this function, specify NULL for the pointer to the NETRESOURCE structure. When WNetOpenEnum receives the NULL pointer, it retrieves a handle to the root of the network. Whenever a NETRESOURCE structure retrieved by WNetEnumResource is RESOURCEUSAGE_CONTAINER, the EnumerateFunc function calls itself and uses a pointer to that structure in its call to WNetOpenEnum.

BOOL FAR PASCAL EnumerateFunc(hwnd, hdc, lpnr)

HWND hwnd;

HDC hdc;

LPNETRESOURCE lpnr;

{

DWORD dwResult, dwResultEnum;

HANDLE hEnum;

DWORD cbBuffer = 16384; /* 16K is reasonable size */

DWORD cEntries = 0xFFFFFFFF; /* enumerate all possible entries */

LPNETRESOURCE lpnrLocal; /* pointer to enumerated structures */

DWORD i;

dwResult = WNetOpenEnum(RESOURCE_GLOBALNET,

RESOURCETYPE_ANY,

0, /* enumerate all resources */

lpnr, /* NULL first time this function is called */

&hEnum); /* handle to resource */

if (dwResult != NO_ERROR) {

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetOpenEnum");

return FALSE;

}

do {

/* Allocate memory for NETRESOURCE structures. */

lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);

dwResultEnum = WNetEnumResource(hEnum, /* resource handle */

&cEntries, /* defined locally as 0xFFFFFFFF */

lpnrLocal, /* LPNETRESOURCE */

&cbBuffer); /* buffer size */

if (dwResultEnum == NO_ERROR) {

for(i = 0; i < cEntries; i++) {

/*

* Following is an application-defined function for

* displaying contents of NETRESOURCE structures.

*/

DisplayStruct(hdc, &lpnrLocal[i]);

/*

* If this NETRESOURCE is a container, call the function

* recursively.

*/

if(RESOURCEUSAGE_CONTAINER ==

(lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER))

if(!EnumerateFunc(hwnd, hdc, &lpnrLocal[i]))

TextOut(hdc, 10, 10,

"EnumerateFunc returned FALSE.", 29);

}

}

else if (dwResultEnum != ERROR_NO_MORE_ITEMS) {

NetErrorHandler(hwnd, dwResultEnum, (LPSTR)"WNetEnumResource");

break;

}

}

while(dwResultEnum != ERROR_NO_MORE_ITEMS);

GlobalFree((HGLOBAL) lpnrLocal);

dwResult = WNetCloseEnum(hEnum);

if(dwResult != NO_ERROR) {

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCloseEnum");

return FALSE;

}

return TRUE;

}

+$#A Adding a Network Connection

To make a connection to a network resource described by a NETRESOURCE structure, an application can call the WNetAddConnection2 function, as shown in the following example.

DWORD dwResult;

NETRESOURCE nr;

dwResult = WNetAddConnection2(&nr, /* NETRESOURCE from enumeration */

(LPSTR) NULL, /* no password */

(LPSTR) NULL, /* logged-in user */

CONNECT_UPDATE_PROFILE); /* update profile with connect info */

if (dwResult == ERROR_ALREADY_ASSIGNED) {

TextOut(hdc, 10, 10, "Already connected to specified resource.", 40);

return FALSE;

}

else if (dwResult == ERROR_DEVICE_ALREADY_REMEMBERED) {

TextOut(hdc, 10, 10,

"Attempted reassignment of remembered device.", 44);

return FALSE;

}

else if(dwResult != NO_ERROR) {

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetAddConnection2");

return FALSE;

}

TextOut(hdc, 10, 10, "Connected to specified resource.", 32);

return TRUE;

The WNetAddConnection function is supported for compatibility with earlier versions of Windows. For new applications, use WNetAddConnection2.

+$#A Retrieving the Connection Name

To retrieve the name of the network resource associated with a local device, an application can use the WNetGetConnection function, as shown in the following example.

CHAR szDeviceName[80];

DWORD dwResult, cchBuff = sizeof(szDeviceName);

dwResult = WNetGetConnection("z:",

(LPSTR) szDeviceName,

&cchBuff);

switch (dwResult) {

case NO_ERROR:

TextOut(hdc, 10, 10, (LPSTR) szDeviceName,

lstrlen((LPSTR) szDeviceName));

break;

case ERROR_NOT_CONNECTED:

TextOut(hdc, 10, 10, "Device z: not connected.", 24);

case ERROR_CONNECTION_UNAVAIL:

/* A connection is remembered but not connected. */

TextOut(hdc, 10, 10, "Connection unavailable.", 23);

default:

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetGetConnection");

return FALSE;

}

+$#A Retrieving the User Name

To retrieve the name of the user associated either with a local device connected to a network resource or with the name of a network, an application can call the WNetGetUser function. The following example uses the device name to retrieve the name of the user.

CHAR szUserName[80];

DWORD dwResult, cchBuff = 80;

dwResult = WNetGetUser("z:",

(LPSTR) szUserName,

&cchBuff);

if(dwResult == NO_ERROR)

TextOut(hdc, 10, 10,

(LPSTR) szUserName,

lstrlen((LPSTR) szUserName));

else {

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetGetUser");

return FALSE;

}

+$#A Canceling a Network Connection

To cancel a connection to a network resource, an application can call the WNetCancelConnection2 function, as shown in the following example.

DWORD dwResult;

dwResult = WNetCancelConnection2("z:",

CONNECT_UPDATE_PROFILE, /* remove connection from profile */

FALSE); /* fail if open files or jobs */

if (dwResult == ERROR_NOT_CONNECTED) {

TextOut(hdc, 10, 10, "Drive z: not connected.", 23);

return FALSE;

}

else if(dwResult != NO_ERROR) {

/*

* An application-defined error handler is demonstrated in the

* section titled "Retrieving Network Errors."

*/

NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCancelConnection2");

return FALSE;

}

TextOut(hdc, 10, 10, "Connection closed for z:.", 25);

The WNetCancelConnection function is supported for compatibility with earlier versions of Windows. For new applications, use WNetCancelConnection2.

+$#A Retrieving Network Errors

When one of the Windows network functions returns WN_EXTENDED_ERROR, an application can call the WNetGetLastError function to get more information about the error that occurred. This information is usually specific to the network provider.

The following example illustrates an application-defined error-handling function (NetErrorHandler) that takes three arguments: a window handle, the error code returned by one of the Windows network functions, and the name of the function that produced the error. If the error code is WN_EXTENDED_ERROR, NetErrorHandler calls WNetGetLastError to get extended error information.

BOOL WINAPI NetErrorHandler(hwnd, dwErrorCode, lpszFunction)

HWND hwnd;

DWORD dwErrorCode;

LPSTR lpszFunction;

{

DWORD dwWNetResult, dwLastError;

CHAR szError[256];

CHAR szCaption[256];

CHAR szDescription[256];

CHAR szProvider[256];

/* The following code performs standard error-handling. */

if (dwErrorCode != ERROR_EXTENDED_ERROR) {

wsprintf((LPSTR) szError, "%s failed; \nResult is %ld",

lpszFunction, dwErrorCode);

wsprintf((LPSTR) szCaption, "%s error", lpszFunction);

MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);

return TRUE;

}

/*

* The following code performs error-handling when the

* ERROR_EXTENDED_ERROR return value indicates that WNetGetLastError

* can retrieve additional information.

*/

else {

dwWNetResult = WNetGetLastError(&dwLastError,

(LPSTR) szDescription, /* buffer for error description */

sizeof(szDescription),

(LPSTR) szProvider, /* buffer for provider name */

sizeof(szProvider));

if(dwWNetResult != NO_ERROR) {

wsprintf((LPSTR) szError,

"WNetGetLastError failed; error %ld", dwWNetResult);

MessageBox(hwnd, (LPSTR) szError,

"WNetGetLastError", MB_OK);

return FALSE;

}

wsprintf((LPSTR) szError,

"%s failed with code %ld;\n%s",

(LPSTR) szProvider, dwLastError, (LPSTR) szDescription);

wsprintf((LPSTR) szCaption, "%s error", lpszFunction);

MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);

return TRUE;

}

}

+$#A Network Functions

Following are the functions used with networks.

Netbios
WNetAddConnection
WNetAddConnection2
WNetAddConnection3
WNetCancelConnection
WNetCancelConnection2
WNetCloseEnum
WNetConnectionDialog
WNetDisconnectDialog
WNetEnumResource
WNetGetConnection
WNetGetLastError
WNetGetUniversalName
WNetGetUser
WNetOpenEnum

Créé avec HelpNDoc Personal Edition: Générateur de documentation d'aide HTML gratuit


Painting and Drawing.rtf

+$#AK Painting and Drawing

This overview describes how Windows manages output to the screen and explains what applications must do to draw in a window. In particular, this overview describes display device contexts (or, more simply, display DCs) and how to prepare and use them. The overview does not explain how to use graphics device interface (GDI) functions to generate output, nor does it explain how to print.

+$#A About Painting and Drawing

Nearly all applications use the screen to display the data they manipulate. An application paints images, draws figures, and writes text so that the user can view data as it is created, edited, and printed. Microsoft® Windows® provides a rich environment for painting and drawing, but, because Windows is a multitasking operating system, applications must cooperate with one another when accessing the screen.

To keep all applications functioning smoothly and cooperatively, Windows manages all output to the screen. Applications use windows as their primary output device rather than the screen itself. Windows supplies display DCs that uniquely correspond to the windows. Applications use display DCs to direct their output to the given windows. Drawing in a window (directing output to it) prevents an application from interfering with the output of other applications and allows applications to coexist with one another while still taking full advantage of the graphics capabilities of Windows.

+$#A When to Draw in a Window

An application draws in a window at a variety of times: when first creating a window, when changing the size of the window, when moving the window from behind another window, when minimizing or maximizing the window, when displaying data from an opened file, and when scrolling, changing, or selecting a portion of the displayed data.

Windows manages actions such as moving and sizing a window. If an action affects the content of the window, Windows marks the affected portion of the window as ready for updating and, at the next opportunity, sends a WM_PAINT message to the window procedure of the window. The message is a signal to the application to determine what must be updated and to carry out the necessary drawing.

Some actions are managed by the application, such as displaying open files and selecting displayed data. For these actions, an application can mark for updating the portion of the window affected by the action, causing a WM_PAINT message to be sent at the next opportunity. If an action requires immediate feedback, the application can draw while the action takes place, without waiting for WM_PAINT. For example, a typical application highlights the area the user selects rather than waiting for the next WM_PAINT message to update the area.

In all cases, an application can draw in a window as soon as it is created. To draw in the window, the application must first retrieve a handle of a display DC for the window. Ideally, an application carries out most of its drawing operations during the processing of WM_PAINT messages. In this case, the application retrieves a display DC by calling the BeginPaint function. If an application draws at any other time, such as from within WinMain or during processing of keyboard or mouse messages, it calls the GetDC or GetDCEx function to retrieve the display DC.

+$#A The WM_PAINT Message

Typically, an application draws in a window in response to a WM_PAINT message. Windows sends this message to a window procedure when changes to the window have altered the content of the client area. Windows sends the message only if there are no other messages in the application message queue.

Upon receiving a WM_PAINT message, an application can call BeginPaint to retrieve the display DC for the client area and use it in calls to GDI functions to carry out whatever drawing operations are necessary to update the client area. After completing the drawing operations, the application calls the EndPaint function to release the display DC.

Before BeginPaint returns the display DC, Windows prepares the DC for the given window. It first sets the clipping region for the DC to be equal to the intersection of the portion of the window that needs updating and the portion that is visible to the user. Only those portions of the window that have changed are redrawn. Attempts to draw outside this region are clipped and do not appear on the screen.

Windows can also send WM_NCPAINT and WM_ERASEBKGND messages to the window procedure before BeginPaint returns. These messages direct the application to draw the nonclient area and window background. The nonclient area is the part of a window that is outside of the client area. The area includes features such as the title bar, System menu (also known as the Control menu), and scroll bars. Most applications rely on the default window function, DefWindowProc, to draw this area and therefore pass the WM_NCPAINT message to this function. The window background is the color or pattern a window is filled with before other drawing operations begin. The background covers any images previously in the window or on the screen under the window. If a window belongs to a window class having a class background brush, the DefWindowProc function draws the window background automatically.

BeginPaint fills a PAINTSTRUCT structure with information such as the dimensions of the portion of the window to be updated and a flag indicating whether the window background has been drawn. The application can use this information to optimize drawing. For example, it can use the dimensions of the update region, specified by the rcPaint member, to limit drawing to only those portions of the window that need updating. If an application has very simple output, it can ignore the update region and draw in the entire window, relying on Windows to discard (clip) any unneeded output. Because the system clips drawing that extends outside the clipping region, only drawing that is in the update region is visible.

BeginPaint sets the update region of a window to NULL. This clears the region, preventing it from generating subsequent WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message.

After the application finishes drawing, it should call EndPaint. For most windows, EndPaint releases the display DC, making it available to other windows. EndPaint also shows the caret, if it was previously hidden by BeginPaint. BeginPaint hides the caret to prevent drawing operations from corrupting it.

+$#A The Update Region

The update region identifies the portion of a window that is out-of-date or invalid and in need of redrawing. Windows uses the update region to generate WM_PAINT messages for applications and to minimize the time applications spend bringing the contents of their windows up to date. Windows adds only the invalid portion of the window to the update region, requiring only that portion to be drawn.

When Windows determines that a window needs updating, it sets the dimensions of the update region to the invalid portion of the window. Setting the update region does not immediately cause the application to draw. Instead, the application continues retrieving messages from the application message queue until no messages remain. Windows then checks the update region, and if the region is not empty (non-NULL), it sends a WM_PAINT message to the window procedure.

An application can use the update region to generate its WM_PAINT messages. For example, an application that loads data from open files typically sets the update region while loading so that new data is drawn during processing of the next WM_PAINT message. In general, an application should not draw at the time its data changes, but route all drawing operations through the WM_PAINT message.

+$#A Invalidating and Validating the Update Region

An application invalidates a portion of a window and sets the update region by using the InvalidateRect or InvalidateRgn function. These functions add the specified rectangle or region (given in client coordinates) to the update region, combining the rectangle or region with anything Windows or the application may have previously added to the update region.

The InvalidateRect and InvalidateRgn functions do not generate WM_PAINT messages. Instead, Windows accumulates the changes made by these functions and its own changes while a window processes other messages in its message queue. By accumulating changes, a window processes all changes at once instead of updating bits and pieces one step at a time.

The ValidateRect and ValidateRgn functions validate a portion of the window by removing a specified rectangle or region from the update region. These functions are typically used when the window has updated a specific part of the screen in the update region before receiving the WM_PAINT message.

+$#A Retrieving the Update Region

The GetUpdateRect and GetUpdateRgn functions retrieve the current update region for the window. GetUpdateRect retrieves the smallest rectangle (in client coordinates) that encloses the entire update region. GetUpdateRgn retrieves the update region itself. These functions can be used to calculate the current size of the update region to determine where to carry out a drawing operation.

BeginPaint also retrieves the dimensions of the smallest rectangle enclosing the current update region, copying the dimensions to the rcPaint member in the PAINTSTRUCT structure. Because BeginPaint validates the update region, any call to GetUpdateRect and GetUpdateRgn immediately after a call to BeginPaint returns an empty update region.

+$#A Excluding the Update Region

The ExcludeUpdateRgn function excludes the update region from the clipping region for the display DC. This function is useful when drawing in a window other than when a WM_PAINT message is processing. It prevents drawing in the areas that will be updated during the next WM_PAINT message.

+$#A Synchronous and Asynchronous Drawing

Most drawing carried out during processing of the WM_PAINT message is asynchronous; that is, there is a delay between the time a portion of the window is invalidated and the time WM_PAINT is sent. During the delay, the application typically retrieves messages from the queue and carries out other tasks. The reason for the delay is that Windows generally treats drawing in a window as a low-priority operation and works as though user-input messages and messages that may affect the position or size of a window will be processed before WM_PAINT.

In some cases, it is necessary for an application to draw synchronously ⎯ that is, draw in the window immediately after invalidating a portion of the window. A typical application draws its main window immediately after creating the window to signal the user that the application has started successfully. Windows draws some control windows synchronously, such as buttons, because such windows serve as the focus for user input. Although any window with a simple drawing routine can be drawn synchronously, all such drawing should be done quickly and should not interfere with the application's ability to respond to user input.

The UpdateWindow and RedrawWindow functions allow for synchronous drawing. UpdateWindow sends a WM_PAINT message directly to the window if the update region is not empty. RedrawWindow also sends a WM_PAINT message, but gives the application greater control over how to draw the window, such as whether to draw the nonclient area and window background or whether to send the message regardless of whether the update region is empty. These functions send the WM_PAINT message directly to the window, regardless of the number of other messages in the application message queue.

Any window requiring time-consuming drawing operations should be drawn asynchronously to prevent pending messages from being blocked as the window is drawn. Also, any application that frequently invalidates small portions of a window should allow these invalid portions to consolidate into a single asynchronous WM_PAINT message, rather than a series of synchronous WM_PAINT messages.

+$#A Drawing Without the WM_PAINT Message

Although applications carry out most drawing operations while the WM_PAINT message is processing, it is sometimes more efficient for an application to draw directly in a window without relying on the WM_PAINT message. This can be useful when the user needs immediate feedback, such as when selecting text and dragging or sizing an object. In such cases, the application usually draws while processing keyboard or mouse messages.

To draw in a window without using a WM_PAINT message, the application uses the GetDC or GetDCEx function to retrieve a display DC for the window. With the display DC, the application can draw in the window and avoid intruding into other windows. When the application has finished drawing, it calls the ReleaseDC function to release the display DC for use by other applications.

When drawing without using a WM_PAINT message, the application usually does not invalidate the window. Instead, it draws in such a fashion that it can easily restore the window and remove the drawing. For example, when the user selects text or an object, the application typically draws the selection by inverting whatever is already in the window. The application can remove the selection and restore the original contents of the window by simply inverting again.

The application is responsible for carefully managing any changes it makes to the window. In particular, if an application draws a selection and an intervening WM_PAINT message occurs, the application must ensure that any drawing done during the message does not corrupt the selection. To avoid this, many applications remove the selection, carry out usual drawing operations, and then restore the selection when drawing is complete.

+$#A Window Coordinate System

The coordinate system for a window is based on the coordinate system of the display device. The basic unit of measure is the device unit (typically, the pixel). Points on the screen are described by x- and y-coordinate pairs. The x-coordinates increase to the right; y-coordinates increase from top to bottom. The origin (0,0) for the system depends on the type of coordinates being used.

Windows and applications specify the position of a window on the screen in screen coordinates. For screen coordinates, the origin is the upper left corner of the screen. The full position of a window is often described by a RECT structure containing the screen coordinates of two points that define the upper left and lower right corners of the window.

Windows and applications specify the position of points in a window by using client coordinates. The origin in this case is the upper left corner of the window or client area. Client coordinates ensure that an application can use consistent coordinate values while drawing in the window, regardless of the position of the window on the screen.

The dimensions of the client area are also described by a RECT structure that contains client coordinates for the area. In all cases, the upper left coordinate of the rectangle is included in the window or client area, while the lower right coordinate is excluded. Graphics operations in a window or client area are excluded from the right and lower edges of the enclosing rectangle.

Occasionally, applications may be required to map coordinates in one window to those of another window. An application can map coordinates by using the MapWindowPoints function. If one of the windows is the desktop window, the function effectively converts screen coordinates to client coordinates and vice versa; the desktop window is always specified in screen coordinates.

+$#A Window Regions

In addition to the update region, every window has a visible region that defines the window portion visible to the user. The system changes the visible region for the window whenever the window changes size or whenever another window is moved such that it obscures or exposes a portion of the window. Applications cannot change the visible region directly, but Windows automatically uses the visible region to create the clipping region for any display DC retrieved for the window.

The clipping region determines where the system permits drawing. When the application retrieves a display DC using the BeginPaint, GetDC, or GetDCEx function, the system sets the clipping region for the DC to the intersection of the visible region and the update region. Applications can change the clipping region by using functions such as SetWindowRgn, SelectClipPath and SelectClipRgn, to further limit drawing to a particular portion of the update area.

The WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles further specify how Windows calculates the visible region for a window. If a window has one or both of these styles, the visible region excludes any child window or sibling windows (windows having the same parent window). Therefore, drawing that would otherwise intrude in these windows will always be clipped.

+$#A Window Background

The window background is the color or pattern used to fill the client area before a window begins drawing. The window background covers whatever was on the screen before the window was moved there, erasing existing images and preventing the application's new output from being mixed with unrelated information.

Windows paints the background for a window or gives the window the opportunity to do so by sending it a WM_ERASEBKGND message when the application calls BeginPaint. If an application does not process the message but passes it to DefWindowProc, Windows erases the background by filling it with the pattern in the background brush specified by the window's class. If the brush is not valid or the class has no background brush, Windows sets the fErase member in the PAINTSTRUCT structure BeginPaint returns, but carries out no other action. The application then has a second chance to draw the window background, if necessary.

If it processes WM_ERASEBKGND, the application should use the message's wParam parameter to draw the background. This parameter contains the handle of the display DC for the window. After drawing the background, the application should return a nonzero value. This ensures that BeginPaint does not erroneously set the fErase member of the PAINTSTRUCT structure to a nonzero value (indicating the background should be erased) when the application processes the subsequent WM_PAINT message.

An application can define a class background brush by assigning a brush handle or a system color value to the hbrBackground member of the WNDCLASS structure when registering the class with the RegisterClass function. The GetStockObject or CreateSolidBrush function can be used to create a brush handle. A system color value can be one of those defined for the SetSysColors function. (The value must be increased by one before it is assigned to the member.)

An application can process the WM_ERASEBKGND message even though a class background brush is defined. This is typical in applications that enable the user to change the window background color or pattern for a given window without affecting other windows in the class. In such cases, the application must not pass the message to DefWindowProc.

It is not necessary for an application to align brushes, because the system draws the brush using the window origin as the point of reference. Given this, the user can move the window without affecting the alignment of pattern brushes.

+$#A Minimized Windows

Windows reduces an application's main window (overlapping style) to a minimized window when the user chooses the Minimize command from the System menu or the application calls the ShowWindow function and specifies a value such as SW_MINIMIZE. Minimizing a window speeds up system performance by reducing the amount of work an application must do when updating its main window.

For a typical application, Windows draws an icon, called the class icon, when the window is minimized, labeling the icon with the name of the window. The class icon, a static image that represents the application, is specified by the application when it registers the window class. The application assigns the handle of the class icon to the hIcon member of WNDCLASS before calling RegisterClass. The application can use the LoadIcon function to retrieve the icon handle.

Before drawing the class icon, Windows sends a WM_ICONERASEBKGND message to the window procedure, enabling the application to prepare the background for drawing the icon by setting the best possible background colors for the icon. This is useful for applications that combine the icon with the current background colors. If the application processes the message, it should use the display DC provided with the message to draw the background (the wParam parameter contains the handle of the display DC). If the application does not process the WM_ICONERASEBKGND message, it should pass the message to DefWindowProc; the function fills the icon area with the current desktop color and pattern. After sending the WM_ICONERASEBKGND message, Windows sends the WM_PAINTICON message to the window procedure. The application should immediately forward this internal message to DefWindowProc.

Windows does not require that a window class have a class icon. If an application sets the hIcon member of WNDCLASS to NULL, a class icon is not defined. In this case, Windows sends the WM_ERASEBKGND message (instead of WM_ICONERASEBKGND) to a window of the class whenever the window must paint the icon background. Windows then sends a WM_PAINT message and the application draws an icon or another image representing the minimized window. In such cases, the application must determine when the window is minimized and draw accordingly. It can do so by calling the IsIconic function. If the function returns TRUE, the window is minimized. If an application has no class icon and fails to process WM_ERASEBKGND and WM_PAINT, the area that Windows reserves for the application's icon will contain whatever was previously on the screen.

+$#A Window Size

Windows changes the size of a window when the user chooses System menu commands, such as Size and Maximize, or when the application calls functions, such as the SetWindowPos function. When a window changes size, Windows assumes that the contents of the previously exposed portion of the window are not affected and need not be redrawn. Windows invalidates only the newly exposed portion of the window, which saves time when the eventual WM_PAINT message is processed by the application. In this case, WM_PAINT is not generated when the size of the window is reduced.

For some windows, any change to the size of the window invalidates the contents. For example, a clock application that adapts the face of the clock to fit neatly within its window must redraw the clock whenever the window changes size. To force Windows to invalidate the entire client area of the window when a vertical, horizontal, or both vertical and horizontal change is made, an application must specify the CS_VREDRAW or CS_HREDRAW style, or both, when registering the window class. Any window belonging to a window class having these styles is invalidated each time the user or the application changes the size of the window.

+$#A Nonclient Area

Windows sends a WM_NCPAINT message to the window whenever a part of the nonclient area of the window, such as the title bar, menu bar, or window frame, must be updated. Windows may also send other messages to direct a window to update a portion of its client area; for example, when a window becomes active or inactive, it sends the WM_NCACTIVATE message to update its title bar. In general, processing these messages for standard windows is not recommended, because the application must be able to draw all the required parts of the nonclient area for the window. For this reason, most applications pass these messages to DefWindowProc for default processing.

An application that creates custom nonclient areas for its windows must process these messages. When doing so, the application must use a window DC to carry out drawing in the window. The window DC enables the application to draw in all portions of the window, including the nonclient area. An application retrieves a window DC by using the GetWindowDC or GetDCEx function and, when drawing is complete, must release the window DC by using the ReleaseDC function.

Windows maintains an update region for the nonclient area. When an application receives a WM_NCPAINT message, the wParam parameter contains the handle of a region defining the dimensions of the update region. The application can use the handle to combine the update region with the clipping region for the window DC. Windows does not automatically combine the update region when retrieving the window DC unless the application uses GetDCEx and specifies both the region handle and the DCX_INTERSECTRGN flag. If the application does not combine the update region, only drawing operations that would otherwise extend outside the window are clipped. The application is not responsible for clearing the update region, regardless of whether it uses the region.

If an application processes the WM_NCACTIVATE message, after processing it must return TRUE to direct Windows to complete the change of active window. If the window is minimized when the application receives the WM_NCACTIVATE message, it should pass the message to DefWindowProc. In such cases, the default function redraws the label for the icon.

+$#A Child Windows

A child window is a window with the WS_CHILD or WS_CHILDWINDOW style. Like other window styles, child windows receive WM_PAINT messages to prompt updating. Each child window has an update region, which either Windows or the application can set to generate eventual WM_PAINT messages.

A child window's update and visible regions are affected by the child's parent window; this is not true for windows of other styles. Windows often sets the child window's update region when it sets the parent window's update region, causing the child window to receive WM_PAINT messages when the parent window receives them. Windows limits the location of the child window's visible region to within the client area of the parent window and clips any portion of the child window moved outside the parent window.

Windows sets the update region for a child window whenever part of the parent window's update region includes a portion of the child window. In such cases, Windows first sends a WM_PAINT message to the parent window and then sends a message to the child window, allowing the child to restore any portions of the window that the parent may have drawn over.

Windows does not set the parent's update region when the child's is set. An application cannot generate a WM_PAINT message for the parent window by invalidating the child window. Similarly, an application cannot generate a WM_PAINT message for the child by invalidating a portion of the parent's client area that lies entirely under the child window. In such cases, neither window receives a WM_PAINT message.

An application can prevent a child window's update region from being set when the parent window's is set by specifying the WS_CLIPCHILDREN style when creating the parent window. When this style is set, Windows excludes the child windows from the parent's visible region and therefore ignores any portion of the update region that may contain the child windows. When the application paints in the parent window, any drawing that would cover the child window is clipped, making a subsequent WM_PAINT message to the child window unnecessary.

The update and visible regions of a child window are also affected by the child window's siblings. Sibling windows are any windows that have a common parent window. If sibling windows overlap, then setting the update region for one affects the update region of another, causing WM_PAINT messages to be sent to both windows. Sibling windows receive WM_PAINT messages in the reverse order of their position in the Z order. Given this, the window highest in the Z order (on the top) receives its WM_PAINT message last, and vice versa.

Sibling windows are not automatically clipped. One sibling can draw over another overlapping sibling even if the window that is drawing has a lower position in the Z order. An application can prevent this by specifying the WS_CLIPSIBLINGS style when creating the windows. When this style is set, Windows excludes all portions of an overlapping sibling window from a window's visible region if the overlapping sibling window has a higher position in the Z order.

Note The update and visible regions for windows that have the WS_POPUP or WS_POPUPWINDOW style are not affected by their parent windows.

+$#A About Display Device Contexts

A display DC is a device context, created by Windows, that an application uses to paint and draw a window. Windows prepares each display DC for output to a window, setting the drawing objects, colors, and modes for the window instead of for the display device. When the application supplies the display DC through calls to GDI functions, GDI uses the information in the context to generate output in the given window without intruding on other windows or other parts of the screen.

Windows provides five kinds of display DC: common, class, parent, private, and window. The common, class, and private DCs permit drawing in the client area of a given window. The parent and window DCs permit drawing anywhere in the window. Although the parent DC also permits drawing in the parent window, it is not intended to be used in this way.

Windows supplies a common, class, parent, or private DC to a window based on the type of display DC specified in that window's class style. Windows supplies a window DC only when the application explicitly requests one ⎯ for example, by calling the GetWindowDC or GetDCEx function. In all cases, an application can use the WindowFromDC function to determine which window a display DC currently represents.

+$#A Display Device Context Cache

Windows maintains a cache of display DCs that it uses for common, parent, and window DCs. Windows retrieves a DC from the cache whenever an application calls the GetDC or BeginPaint function; Windows returns the DC to the cache when the application subsequently calls the ReleaseDC or EndPaint function.

In Windows 3.x, the cache contains five display DCs, but only five DCs from the cache can be active at a time. To ensure that other applications have access to these DCs, an application must release a device context immediately after using it. Failure to do so eventually causes the application to fail.

There is no predetermined limit on the amount of DCs that a cache can hold; Windows creates a new display DC for the cache if none is available. Given this, a Win32-based application can have more than five active DCs from the cache at a time. However, the application must continue to release these DCs after use. Because new display DCs for the cache are allocated in the application's heap space, failing to release the DCs eventually consumes all available heap space. Windows indicates this failure by returning an error when it cannot allocate space for the new DC. Other functions unrelated to the cache may also return errors.

Portability To minimize porting efforts, applications that use common DCs should be limited to no more than five and ensure that a common DC is released as soon as possible after it is used.

+$#A Display Device Context Defaults

Upon first creating a display DC, Windows assigns default values for the attributes (that is, drawing objects, colors, and modes) that comprise the DC. The following table shows the default values for the attributes of a display DC.

Attribute | Default value
Background color | Background color setting from Windows Control Panel (typically, white).
Background mode | OPAQUE.
Bitmap | None.
Brush | WHITE_BRUSH.
Brush origin | (0,0).
Clipping region | Entire window or client area with the update region clipped, as appropriate. Child and pop-up windows in the client area may also be clipped.
Palette | DEFAULT_PALETTE.
Current pen position | (0,0).
Device origin | Upper left corner of the window or the client area.
Drawing mode | R2_COPYPEN.
Font | SYSTEM_FONT (SYSTEM_FIXED_FONT for applications written to run with Windows versions 3.0 and earlier).
Intercharacter spacing | 0.
Mapping mode | MM_TEXT.
Pen | BLACK_PEN.
Polygon-fill mode | ALTERNATE.
Stretch mode | BLACKONWHITE.
Text color | Text color setting from Control Panel (typically, black).
Viewport extent | (1,1).
Viewport origin | (0,0).
Window extent | (1,1).
Window origin | (0,0).

An application can modify the values of the display DC attributes by using selection and attribute functions, such as SelectObject, SetMapMode, and SetTextColor. For example, an application can modify the default units of measure in the coordinate system by using SetMapMode to change the mapping mode.

Changes to the attribute values of a common, parent, or window DC are not permanent. When an application releases these DCs, the current selections, such as mapping mode and clipping region, are lost as the context is returned to the cache. Changes to a class or private DC persist indefinitely. To restore them to their original defaults, an application must explicitly set each attribute.

+$#A Common Display Device Contexts

A common DC is used for drawing in the client area of the window. The Win32 API provides a common DC by default for any window whose window class does not explicitly specify a display DC style. Common DCs are typically used with windows that can be drawn without extensive changes to the DC attributes. Common DCs are convenient because they do not require additional memory or system resources, but they can be inconvenient if the application must set up many attributes before using them.

Windows retrieves all common DCs from the display DC cache. An application can retrieve a common DC immediately after the window is created. Because the common DC is from the cache, the application must always release the DC as soon as possible after drawing. After the common DC is released, it is no longer valid and the application must not attempt to draw with it. To draw again, the application must retrieve a new common DC, and continue to retrieve and release a common DC each time it draws in the window. If the application retrieves the DC handle by using the GetDC function, it must use the ReleaseDC function to release the handle. Similarly, for each BeginPaint function, the application must use a corresponding EndPaint function.

When the application retrieves the DC, Windows adjusts the origin so that it aligns with the upper left corner of the client area. It also sets the clipping region so that output to the DC is clipped to the client area. Any output that would otherwise appear outside the client area is clipped. If the application retrieves the common DC by using BeginPaint, Windows also includes the update region in the clipping region to further restrict the output.

When an application releases a common DC, Windows restores the default values for the attributes of the DC. An application that modifies attribute values must do so each time it retrieves a common DC. Releasing the DC releases any drawing objects the application may have selected into it, so the application need not release these objects before releasing the DC. In all cases, an application must never assume that the common DC retains nondefault selections after being released.

+$#A Private Display Device Contexts

A private DC enables an application to avoid retrieving and initializing a display DC each time the application must draw in a window. Private DCs are useful for windows that require many changes to the values of the attributes of the DC to prepare it for drawing. Private DCs reduce the time required to prepare the DC and therefore the time needed to carry out drawing in the window.

An application directs Windows to create a private DC for a window by specifying the CS_OWNDC style in the window class. Windows creates a unique private DC each time it creates a new window belonging to the class. Initially, the private DC has the same default values for attributes as a common DC, but the application can modify these at any time. Windows preserves changes to the DC for the life of the window or until the application makes additional changes.

An application can retrieve the handle of the private DC by using the GetDC function any time after the window is created. The application must retrieve the handle only once. Thereafter, it can keep and use the handle any number of times. Because a private DC is not part of the display DC cache, an application need never release the DC by using the ReleaseDC function.

Windows automatically adjusts the DC to reflect changes to the window, such as moving or sizing. This ensures that any overlapping windows are always properly clipped; that is, no action is required by the application to ensure clipping. However, Windows does not revise the DC to include the update region. Therefore, when processing a WM_PAINT message, the application must incorporate the update region either by calling BeginPaint or by retrieving the update region and intersecting it with the current clipping region. If the application does not call BeginPaint, it must explicitly validate the update region by using the ValidateRect or ValidateRgn function. If the application does not validate the update region, the window receives an endless series of WM_PAINT messages.

Because BeginPaint hides the caret if a window is showing it, an application that calls BeginPaint should also call the EndPaint function to restore the caret. EndPaint has no other effect on a private DC.

Although a private DC is convenient to use, it is expensive in terms of system resources, requiring 800 or more bytes to store. Private DCs are recommended when performance considerations outweigh storage costs.

Windows includes the private DC when sending the WM_ERASEBKGND message to the application. The current selections of the private DC, including mapping mode, are in effect when the application or Windows processes these messages. To avoid undesirable effects, Windows uses logical coordinates when erasing the background; for example, it uses the GetClipBox function to retrieve the logical coordinates of the area to erase and passes these coordinates to the FillRect function. Applications that process these messages can use similar techniques. Windows supplies a window DC with the WM_ICONERASEBKGND message regardless of whether the corresponding window has a private DC.

An application can use the GetDCEx function to force Windows to return a common DC for the window that has a private DC. This is useful for carrying out quick touch-ups to a window without changing the current values of the attributes of the private DC.

+$#A Class Display Device Contexts

By using a class DC, an application can use a single display DC for every window belonging to a given class. Class DCs are often used with control windows that are drawn using the same attribute values. Like private DCs, class DCs minimize the time required to prepare a DC for drawing.

Windows supplies a class DC for a window if it belongs to a window class having the CS_CLASSDC style. Windows creates the DC when creating the first window belonging to the class and then uses the same DC for all subsequently created windows in the class. Initially, the class DC has the same default values for attributes as a common DC, but the application can modify these at any time. Windows preserves all changes, except for the clipping region and device origin, until the last window in the class has been destroyed. A change made for one window applies to all windows in that class.

An application can retrieve the handle for the class DC by using the GetDC function any time after the first window has been created. The application can keep and use the handle without releasing it because the class DC is not part of the display DC cache. If the application creates another window in the same window class, the application must retrieve the class DC again. Retrieving the DC sets the correct device origin and clipping region for the new window. After the application retrieves the class DC for a new window in the class, the DC can no longer be used to draw in the original window without again retrieving it for that window. In general, each time it must draw in a window, an application must explicitly retrieve the class DC for the window.

Applications that use class DCs should always call BeginPaint when processing a WM_PAINT message. The function sets the correct device origin and clipping region for the window, and incorporates the update region. The application should also call EndPaint to restore the caret if BeginPaint hid it. EndPaint has no other effect on a class DC.

Windows passes the class DC when sending the WM_ERASEBKGND message to the application, permitting the current attribute values to affect any drawing carried out by the application or Windows when processing this message. Windows supplies a window DC with the WM_ICONERASEBKGND message regardless of whether the corresponding window has a class DC. As it could with a window having a private DC, an application can use GetDCEx to force Windows to return a common DC for the window that has a class DC.

Using class DCs is not recommended.

+$#A Window Display Device Contexts

A window DC enables an application to draw anywhere in a window, including the nonclient area. Window DCs are typically used by applications that process the WM_NCPAINT and WM_NCACTIVATE messages for windows with custom nonclient areas. Using a window DC is not recommended for any other purpose.

An application can retrieve a window DC by using the GetWindowDC or GetDCEx function with the DCX_WINDOW option specified. The function retrieves a window DC from the display DC cache. A window that uses a window DC must release it after drawing by using the ReleaseDC function as soon as possible. Window DCs are always from the cache; the CS_OWNDC and CS_CLASSDC class styles do not affect the DC.

When an application retrieves a window DC, Windows sets the device origin to the upper left corner of the window instead of the upper left corner of the client area. It also sets the clipping region to include the entire window, not just the client area. Windows sets the current attribute values of a window DC to the same default values as a common DC. An application can change the attribute values, but Windows does not preserve any changes when the DC is released.

+$#A Parent Display Device Contexts

A parent DC enables an application to minimize the time necessary to set up the clipping region for a window. An application typically uses parent DCs to speed up drawing for control windows without requiring a private or class DC. For example, Windows uses parent DCs for push button and edit controls. Parent DCs are intended for use with child windows only, never with top-level or pop-up windows.

Windows supplies a parent DC for any window belonging to a window class specifying the CS_PARENTDC style. An application can retrieve the parent DC by using GetDC immediately after the window is created. Windows always retrieves the parent DC from the display DC cache even if the parent uses a private or class DC, so the application must release the DC by using ReleaseDC as soon as possible after drawing. When processing a WM_PAINT message, the application must retrieve the parent DC by using BeginPaint and release it by using EndPaint.

When a parent DC is retrieved, Windows searches the cache for the display DC previously used by the parent window. In most cases, the parent window retrieves and releases a display DC before the child window attempts to retrieve one, so the parent DC is usually in the cache. If Windows finds the display DC, Windows uses it and its corresponding clipping region, which was previously calculated for the parent DC. The time saved is in skipping the calculation required to define the clipping region. If Windows does not find the parent DC, it retrieves a common DC for the window and calculates the clipping region for the window rather than for the parent window.

Windows sets the device origin for the parent DC to the upper left corner of the child window. Attribute values set by the parent window are not preserved for the child window; for example, the parent window cannot set the brush for its child windows. The only property preserved is the clipping region. The window must clip its own output to the limits of the window. Because the clipping region for the parent DC is identical to the parent window, the child window can potentially draw over the entire parent window, but the parent DC must not be used in this way.

Windows ignores the CS_PARENTDC style if the parent window uses a private or class DC, if the parent window clips its child windows, or if the child window clips its child windows or sibling windows. In these cases, Windows always retrieves a common DC for the window.

+$#A Window Update Lock

A window update lock is a temporary suspension of drawing in a window. Windows uses the lock to prevent other windows from drawing over the tracking rectangle whenever the user moves or sizes a window. Applications can use the lock to prevent drawing if they carry out similar moving or sizing operations with their own windows.

An application uses the LockWindowUpdate function to set or clear a window update lock, specifying the window to lock. The lock applies to the given window and all of its child windows. When the lock is set, the GetDC and BeginPaint functions return a display DC with a visible region that is empty. Given this, the application can continue to draw in the window, but all output is clipped. The lock persists until the application clears it by calling LockWindowUpdate, specifying NULL for the window. Although LockWindowUpdate forces a window's visible region to be empty, the function does not make the given window invisible and does not clear the WS_VISIBLE style bit.

After the lock is set, the application can use the GetDCEx function, with the DCX_LOCKWINDOWUPDATE value, to retrieve a display DC to draw over the locked window. This allows the application to draw a tracking rectangle when processing keyboard or mouse messages. Windows uses this method when the user moves and sizes windows. GetDCEx retrieves the display DC from the display DC cache, so the application must release the DC as soon as possible after drawing.

While a window update lock is set, the system creates an accumulated bounding rectangle for each locked window. When the lock is cleared, Windows uses this bounding rectangle to set the update region for the window and its child windows, forcing an eventual WM_PAINT message. If the accumulated bounding rectangle is empty (that is, if no drawing has occurred while the lock was set), the update region is not set.

+$#A Accumulated Bounding Rectangle

The accumulated bounding rectangle is the smallest rectangle enclosing the portion of a window or client area affected by recent drawing operations. An application can use this rectangle to conveniently determine the extent of changes caused by drawing operations. It is sometimes used in conjunction with LockWindowUpdate to determine which portion of the client area must be redrawn after the update lock is cleared.

An application uses the SetBoundsRect function (specifying DCB_ENABLE) to begin accumulating the bounding rectangle. Windows subsequently accumulates points for the bounding rectangle as the application uses the specified display DC. The application can retrieve the current bounding rectangle at any time by using the GetBoundsRect function. The application stops the accumulation by calling SetBoundsRect again, specifying the DCB_DISABLE value.

+$#A Using the WM_PAINT Message

You can use the WM_PAINT message to carry out the drawing necessary for displaying information. Because Windows sends WM_PAINT messages to your application when your window must be updated or when you explicitly request an update, you can consolidate the code for drawing in your application's window procedure. You can then use this code whenever your application must draw either new or existing information.

The following sections show a variety of ways to use the WM_PAINT message to draw in a window.

• Drawing in the client area

• Redrawing the entire client area

• Redrawing in the update region

• Invalidating the client area

• Drawing a minimized window

• Drawing a custom window background

+$#A Drawing in the Client Area

You use the BeginPaint and EndPaint functions to prepare for and complete the drawing in the client area. BeginPaint returns a handle of the display DC used for drawing in the client area; EndPaint ends the paint request and releases the DC.

In the following example, the window procedure writes the message "Hello, Windows!" in the client area. To make sure the string is visible when the window is first created, the WinMain function calls UpdateWindow immediately after creating and showing the window. This causes a WM_PAINT message to be sent immediately to the window procedure.

LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

PAINTSTRUCT ps;

HDC hdc;

switch (message) {

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

TextOut(hdc, 0, 0, "Hello, Windows!", 15);

EndPaint(hwnd, &ps);

return 0L;

.

.

.

}

}

int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hInstance; /* handle of current instance */

HINSTANCE hPrevInstance; /* handle of previous instance */

LPSTR lpCmdLine; /* address of command line */

int nCmdShow; /* show-window type (open/icon) */

{

HWND hwnd;

hwnd = CreateWindowEx( /* parameters */ );

ShowWindow(hwnd, SW_SHOW);

UpdateWindow(hwnd);

.

.

.

return msg.wParam;

}

+$#A Redrawing the Entire Client Area

You can have your application redraw the entire contents of the client area whenever the window changes size by setting the CS_HREDRAW and CS_VREDRAW styles for the window class. Applications that adjust the size of the drawing based on the size of the window use these styles to ensure that they start with a completely empty client area when drawing.

In the following example, the window procedure draws a five-pointed star that fits neatly in the client area. It uses a common DC and must set the mapping mode as well as window and viewport extents each time the WM_PAINT message is processed.

LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

PAINTSTRUCT ps;

HDC hdc;

RECT rc;

POINT aptStar[6] = {50,2, 2,98, 98,33, 2,33, 98,98, 50,2};

.

.

.

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

GetClientRect(hwnd, &rc);

SetMapMode(hdc, MM_ANISOTROPIC);

SetWindowExtEx(hdc, 100, 100, NULL);

SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);

Polyline(hdc, aptStar, 6);

EndPaint(hwnd, &ps);

return 0L;

.

.

.

}

int APIENTRY WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)

HINSTANCE hInstance; /* handle of current instance */

HINSTANCE hPrevInstance; /* handle of previous instance */

LPSTR lpCmdLine; /* address of command line */

int nCmdShow; /* show-window type (open/icon) */

{

WNDCLASS wc;

.

.

.

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC) WndProc;

.

.

.

RegisterClass(&wc);

.

.

.

return msg.wParam;

}

+$#A Redrawing in the Update Region

You can limit the amount of drawing your application carries out when processing the WM_PAINT message by determining the size and location of the update region. Because Windows uses the update region when creating the clipping region for the window's display DC, you can indirectly determine the update region by examining the clipping region.

In the following example, the window procedure draws a triangle, a rectangle, a pentagon, and a hexagon, but only if all or a portion of each figure lies within the update region. The window procedure uses the RectVisible function and a 100-by-100 rectangle to determine whether a figure is within the clipping region (and therefore the update region) for the common DC retrieved by BeginPaint.

POINT aptTriangle[4] = {50,2, 98,86, 2,86, 50,2},

aptRectangle[5] = { 2,2, 98,2, 98,98, 2,98, 2,2},

aptPentagon[6] = {50,2, 98,35, 79,90, 21,90, 2,35, 50,2},

aptHexagon[7] = {50,2, 93,25, 93,75, 50,98, 7,75, 7,25, 50,2};

.

.

.

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

SetRect(&rc, 0, 0, 100, 100);

if (RectVisible(hdc, &rc))

Polyline(hdc, aptTriangle, 4);

SetViewportOrgEx(hdc, 100, 0, NULL);

if (RectVisible(hdc, &rc))

Polyline(hdc, aptRectangle, 5);

SetViewportOrgEx(hdc, 0, 100, NULL);

if (RectVisible(hdc, &rc))

Polyline(hdc, aptPentagon, 6);

SetViewportOrgEx(hdc, 100, 100, NULL);

if (RectVisible(hdc, &rc))

Polyline(hdc, aptHexagon, 7);

EndPaint(hwnd, &ps);

return 0L;

.

.

.

The coordinates of each figure in this example lie within the same 100-by-100 rectangle. Before drawing a figure, the window procedure sets the viewport origin to a different part of the client area by using the SetViewportOrgEx function. This prevents figures from being drawn one on top of the other. Changing the viewport origin does not affect the clipping region, but does affect how the coordinates of the rectangle passed to RectVisible are interpreted. Changing the origin also allows you to use a single rectangle to check the update region rather than individual rectangles for each figure.

+$#A Invalidating the Client Area

Windows is not the only source of WM_PAINT messages. The InvalidateRect or InvalidateRgn function can indirectly generate WM_PAINT messages for your windows. These functions mark all or part of a client area as invalid (that must be redrawn).

In the following example, the window procedure invalidates the entire client area when processing WM_CHAR messages. This allows the user to change the figure by typing a number and view the results; these results are drawn as soon as there are no other messages in the application's message queue.

POINT aptPentagon[6] = {50,2, 98,35, 79,90, 21,90, 2,35, 50,2},

aptHexagon[7] = {50,2, 93,25, 93,75, 50,98, 7,75, 7,25, 50,2};

POINT *ppt = aptPentagon;

int cpt = 6;

.

.

.

case WM_CHAR:

switch (wParam) {

case '5':

ppt = aptPentagon;

cpt = 6;

break;

case '6':

ppt = aptHexagon;

cpt = 7;

break;

}

InvalidateRect(hwnd, NULL, TRUE);

return 0L;

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

GetClientRect(hwnd, &rc);

SetMapMode(hdc, MM_ANISOTROPIC);

SetWindowExtEx(hdc, 100, 100, NULL);

SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);

Polyline(hdc, ppt, cpt);

EndPaint(hwnd, &ps);

return 0L;

In this example, the NULL argument used by InvalidateRect specifies the entire client area; the TRUE argument causes the background to be erased. If you do not want the application to wait until the application's message queue has no other messages, use the UpdateWindow function to force the WM_PAINT message to be sent immediately. If there is any invalid part of the client area, UpdateWindow sends the WM_PAINT message for the given window directly to the window procedure.

+$#A Drawing a Minimized Window

You can draw your own minimized windows rather than having Windows draw them for you. Most applications define a class icon when registering the window class for the window, and Windows draws the icon when the window is minimized. If you set the class icon to NULL, however, Windows sends a WM_PAINT message to your window procedure whenever the window is minimized, enabling the window procedure to draw in the minimized window.

In the following example, the window procedure draws a star in the minimized window. The procedure uses the IsIconic function to determine when the window is minimized. This ensures that the star is drawn only when the window is minimized.

POINT aptStar[6] = {50,2, 2,98, 98,33, 2,33, 98,98, 50,2};

.

.

.

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

/* Determine whether the window is minimized. */

if (IsIconic(hwnd)) {

GetClientRect(hwnd, &rc);

SetMapMode(hdc, MM_ANISOTROPIC);

SetWindowExtEx(hdc, 100, 100, NULL);

SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);

Polyline(hdc, aptStar, 6);

} else {

TextOut(hdc, 0,0, "Hello, Windows!", 15);

}

EndPaint(hwnd, &ps);

return 0L;

You set the class icon to NULL by setting the hIcon member of the WNDCLASS structure to NULL before calling the RegisterClass function for the window class.

+$#A Drawing a Custom Window Background

You can draw your own window background rather than having Windows draw it for you. Most applications specify a brush handle or system color value for the class background brush when registering the window class; Windows uses the brush or color to draw the background. If you set the class background brush to NULL, however, Windows sends a WM_ERASEBKGND message to your window procedure whenever the window background must be drawn, letting you draw a custom background.

In the following example, the window procedure draws a large checkerboard pattern that fits neatly in the window. The procedure fills the client area with a white brush and then draws thirteen 20-by-20 rectangles using a gray brush. The display DC to use when drawing the background is specified in the wParam parameter for the message.

HBRUSH hbrWhite, hbrGray;

.

.

.

case WM_CREATE:

hbrWhite = GetStockObject(WHITE_BRUSH);

hbrGray = GetStockObject(GRAY_BRUSH);

return 0L;

case WM_ERASEBKGND:

hdc = (HDC) wParam;

GetClientRect(hwnd, &rc);

SetMapMode(hdc, MM_ANISOTROPIC);

SetWindowExtEx(hdc, 100, 100, NULL);

SetViewportExtEx(hdc, rc.right, rc.bottom, NULL);

FillRect(hdc, &rc, hbrWhite);

for (i = 0; i < 13; i++) {

x = (i * 40) % 100;

y = ((i * 40) / 100) * 20;

SetRect(&rc, x, y, x + 20, y + 20);

FillRect(hdc, &rc, hbrGray);

}

return 1L;

If the application draws its own minimized window, Windows also sends the WM_ERASEBKGND message to the window procedure to draw the background for the minimized window. You can use the same technique used by WM_PAINT to determine whether the window is minimized ⎯ that is, call the IsIconic function and check for the return value TRUE.

+$#A Using the GetDC Function

You use the GetDC function to carry out drawing that must occur instantly rather than when a WM_PAINT message is processing. Such drawing is usually in response to an action by the user, such as making a selection or drawing with the mouse. In such cases, the user should receive instant feedback and must not be forced to stop selecting or drawing in order for the application to display the result. The following sections show how to use GetDC to draw in a window.

+$#A Drawing with the Mouse

You can permit the user to draw lines with the mouse by having your window procedure draw while processing the WM_MOUSEMOVE message. Windows sends the WM_MOUSEMOVE message to the window procedure whenever the user moves the cursor within the window. To draw lines, the window procedure can retrieve a display DC and draw a line in the window between the current and previous cursor positions.

In the following example, the window procedure prepares for drawing when the user presses and holds the left mouse button (sending the WM_LBUTTONDOWN message). As the user moves the cursor within the window, the window procedure receives a series of WM_MOUSEMOVE messages. For each message, the window procedure draws a line connecting the previous position and the current position. To draw the line, the procedure uses GetDC to retrieve a display DC; then, as soon as drawing is complete and before returning from the message, the procedure uses the ReleaseDC function to release the display DC. As soon as the user releases the mouse button, the window procedure clears the flag, and the drawing stops (which sends the WM_LBUTTONUP message).

BOOL fDraw = FALSE;

POINT ptPrevious;

.

.

.

case WM_LBUTTONDOWN:

fDraw = TRUE;

ptPrevious.x = LOWORD(lParam);

ptPrevious.y = HIWORD(lParam);

return 0L;

case WM_LBUTTONUP:

if (fDraw) {

hdc = GetDC(hwnd);

MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);

LineTo(hdc, LOWORD(lParam), HIWORD(lParam));

ReleaseDC(hwnd, hdc);

}

fDraw = FALSE;

return 0L;

case WM_MOUSEMOVE:

if (fDraw) {

hdc = GetDC(hwnd);

MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL);

LineTo(hdc, ptPrevious.x = LOWORD(lParam),

ptPrevious.y = HIWORD(lParam));

ReleaseDC(hwnd, hdc);

}

return 0L;

An application that enables drawing, as in this example, typically records either the points or lines so that the lines can be redrawn whenever the window is updated. Drawing applications often use a memory DC and an associated bitmap to store lines that were drawn by using a mouse.

+$#A Drawing at Timed Intervals

You can draw at timed intervals by creating a timer with the SetTimer function. By using a timer to send WM_TIMER messages to the window procedure at regular intervals, an application can carry out simple animation in the client area while other applications continue running.

In the following example, the application bounces a star from side to side in the client area. Each time the window procedure receives a WM_TIMER message, the procedure erases the star at the current position, calculates a new position, and draws the star within the new position. The procedure starts the timer by calling SetTimer while processing the WM_CREATE message.

RECT rcCurrent = {0,0,20,20};

POINT aptStar[6] = {10,1, 1,19, 19,6, 1,6, 19,19, 10,1};

int X = 2, Y = -1, idTimer = -1;

BOOL fVisible = FALSE;

HDC hdc;

LRESULT APIENTRY WndProc(hwnd, message, wParam, lParam)

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

{

PAINTSTRUCT ps;

RECT rc;

switch (message) {

case WM_CREATE:

/* Calculate the starting point. */

GetClientRect(hwnd, &rc);

OffsetRect(&rcCurrent, rc.right / 2, rc.bottom / 2);

/* Initialize the private DC. */

hdc = GetDC(hwnd);

SetViewportOrgEx(hdc, rcCurrent.left,

rcCurrent.top, NULL);

SetROP2(hdc, R2_NOT);

/* Start the timer. */

SetTimer(hwnd, idTimer = 1, 10, NULL);

return 0L;

case WM_DESTROY:

KillTimer(hwnd, 1);

PostQuitMessage(0);

return 0L;

case WM_SIZE:

switch (wParam) {

case SIZE_MINIMIZED:

/* Stop the timer if the window is minimized. */

KillTimer(hwnd, 1);

idTimer = -1;

break;

case SIZE_RESTORED:

/*

* Move the star back into the client area

* if necessary.

*/

if (rcCurrent.right > (int) LOWORD(lParam))

rcCurrent.left =

(rcCurrent.right =

(int) LOWORD(lParam)) - 20;

if (rcCurrent.bottom > (int) HIWORD(lParam))

rcCurrent.top =

(rcCurrent.bottom =

(int) HIWORD(lParam)) - 20;

/* Fall through to the next case. */

case SIZE_MAXIMIZED:

/* Start the timer if it had been stopped. */

if (idTimer == -1)

SetTimer(hwnd, idTimer = 1, 10, NULL);

break;

}

return 0L;

case WM_TIMER:

/* Hide the star if it is visible. */

if (fVisible)

Polyline(hdc, aptStar, 6);

/* Bounce the star off a side if necessary. */

GetClientRect(hwnd, &rc);

if (rcCurrent.left + X < rc.left ||

rcCurrent.right + X > rc.right)

X = -X;

if (rcCurrent.top + Y < rc.top ||

rcCurrent.bottom + Y > rc.bottom)

Y = -Y;

/* Show the star in its new position. */

OffsetRect(&rcCurrent, X, Y);

SetViewportOrgEx(hdc, rcCurrent.left,

rcCurrent.top, NULL);

fVisible = Polyline(hdc, aptStar, 6);

return 0L;

case WM_ERASEBKGND:

/* Erase the star. */

fVisible = FALSE;

return DefWindowProc(hwnd, message, wParam, lParam);

case WM_PAINT:

/*

* Show the star if it is not visible. Use BeginPaint

* to clear the update region.

*/

BeginPaint(hwnd, &ps);

if (!fVisible)

fVisible = Polyline(hdc, aptStar, 6);

EndPaint(hwnd, &ps);

return 0L;

}

return DefWindowProc(hwnd, message, wParam, lParam);

}

This application uses a private DC to minimize the time required to prepare the DC for drawing. The window procedure retrieves and initializes the private DC when processing the WM_CREATE message, setting the binary raster operation mode to allow the star to be erased and drawn using the same call to the Polyline function. The window procedure also sets the viewport origin to allow the star to be drawn using the same set of points regardless of the star's position in the client area.

The application uses the WM_PAINT message to draw the star whenever the window must be updated. The window procedure draws the star only if it is not visible; that is, only if it has been erased by the WM_ERASEBKGND message. The window procedure intercepts the WM_ERASEBKGND message to set the fVisible variable, but passes the message to DefWindowProc so that Windows can draw the window background.

The application uses the WM_SIZE message to stop the timer when the window is minimized and to restart the timer when the minimized window is restored. The window procedure also uses the message to update the current position of the star if the size of the window has been reduced so that the star is no longer in the client area. The application keeps track of the star's current position by using the structure specified by rcCurrent, which defines the bounding rectangle for the star. Keeping all corners of the rectangle in the client area keeps the star in the area. The window procedure initially centers the star in the client area when processing the WM_CREATE message.

+$#AK Painting and Drawing Reference

The following functions and messages are associated with painting and drawing.

+$#A Painting and Drawing Functions

Following are the functions used with painting and drawing.

BeginPaint
DrawAnimatedRects
DrawCaption
DrawEdge
DrawFocusRect
DrawFrameControl
DrawState
DrawStateProc
DrawTextEx
EndPaint
ExcludeUpdateRgn
GdiFlush
GdiGetBatchLimit
GdiSetBatchLimit
GetBkColor
GetBkMode
GetBoundsRect
GetROP2
GetUpdateRect
GetUpdateRgn
GetWindowDC
GrayString
InvalidateRect
InvalidateRgn
LockWindowUpdate
OutputProc
PaintDesktop
RedrawWindow
ResetDisplay
SetBkColor
SetBkMode
SetBoundsRect
SetRectRgn
SetROP2
UpdateWindow
ValidateRect
ValidateRgn
WindowFromDC

+$#A Painting and Drawing Messages

Following are the messages used with painting and drawing.

WM_DISPLAYCHANGE
WM_ERASEBKGND
WM_ICONERASEBKGND
WM_NCPAINT
WM_PAINT
WM_PAINTICON
WM_PRINT
WM_PRINTCLIENT
WM_SETREDRAW

Créé avec HelpNDoc Personal Edition: Création d'aide CHM, PDF, DOC et HTML d'une même source


Paths.rtf

+$#AK Paths

A path is one or more figures (or shapes) that are filled, outlined, or both filled and outlined. Applications written for Microsoft® Windows® use paths in many ways. Paths are used in drawing and painting applications. Computer-aided design (CAD) applications use paths to create unique clipping regions, to draw outlines of irregular shapes, and to fill the interiors of irregular shapes. An irregular shape is a shape composed of Bézier curves and straight lines. (A regular shape is an ellipse, a circle, a rectangle, or a polygon.)

+$#A About Paths

A path is one of the objects associated with a device context (DC). However, unlike the default objects (the pen, the brush, and the font) that are part of any new DC, there is no default path. For more information about DCs, see Device Contexts.

To create a path and select it into a DC, it is first necessary to define the points that describe it. This is done by calling the BeginPath function, specifying the appropriate drawing functions, and then by calling the EndPath function. This combination of functions (BeginPath, drawing functions, and EndPath) constitute a path bracket. The following functions can be used in a path bracket.

AngleArc | LineTo | Polyline
Arc | MoveToEx | PolylineTo
ArcTo | Pie | PolyPolygon
Chord | PolyBezier | PolyPolyline
CloseFigure | PolyBezierTo | Rectangle
Ellipse | PolyDraw | RoundRect
ExtTextOut | Polygon | TextOut

When an application calls EndPath, Windows selects the associated path into the specified DC. (If another path had previously been selected into the DC, Windows deletes that path without saving it.) After Windows selects the path into the DC, an application can operate on the path in one of the following ways:

• Draw the outline of the path (using the current pen).

• Paint the interior of the path (using the current brush).

• Draw the outline and fill the interior of the path.

• Modify the path (converting curves to line segments).

• Convert the path into a clip path.

• Convert the path into a region.

• Flatten the path by converting each curve in the path into a series of line segments.

• Retrieve the coordinates of the lines and curves that compose a path.

+$#A Outlined and Filled Paths

An application can draw the outline of a path by calling the StrokePath function, it can fill the interior of a path by calling the FillPath function, and it can both outline and fill the path by calling the StrokeAndFillPath function.

Whenever an application fills a path, Windows uses the DC's current fill mode. An application can retrieve this mode by calling the GetPolyFillMode function, and it can set a new fill mode by calling the SetPolyFillMode function. For a description of the two fill modes, see Regions.

The following illustration shows the cross-section of an object created by a computer-aided design (CAD) application using paths that were both outlined and filled.

{ewc msdncd, EWGraphic, group10662 0 /a "SDK_01.bmp"}

+$#A Transformations of Paths

Paths are defined using logical units and current transformations. (If the SetWorldTransform function has been called, the logical units are world units; otherwise, the logical units are page units.) An application can use world transformations to scale, rotate, shear, translate, and reflect the lines and Bézier curves that define a path.

Note A world transformation within a path bracket only affects those lines and curves drawn after the transformation was set. It will have no affect on those lines and curves that were drawn before it was set. For a description of the world transformation, see Coordinate Spaces and Transformations.

An application can also use SetWorldTransform to outline the shape of the pen used to outline a path if the pen is a geometric pen. For a description of geometric pens, see Pens.

+$#A Clip Paths and Graphic Effects

An application can use clipping and paths to create special graphic effects. The following illustration shows a string of text drawn with a large Arial font.

{ewc msdncd, EWGraphic, group10662 1 /a "SDK_02.bmp"}

The next illustration shows the result of selecting the text as a clip path and drawing radial lines for a circle whose center is located above and left of the string.

{ewc msdncd, EWGraphic, group10662 2 /a "SDK_03.bmp"}

Note Before graphics device interface (GDI) adds text created with a bitmapped font to a path, it converts the font to an outline or vector font.

An application creates a clip path by generating a path bracket and then calling the SelectClipPath function. After a clip path is selected into a DC, output only appears within the borders of the path.

In addition to creating special graphics effects, clip paths are also useful in applications that save images as enhanced metafiles. By using a clip path, an application is able to ensure device independence because the units used to specify a path are logical units (as opposed to device units that are used to specify a region).

+$#A Conversion of Paths to Regions

An application can convert a path into a region by calling the PathToRegion function. Like SelectClipPath, PathToRegion is useful in the creation of special graphics effects. For example, there are no functions that allow an application to offset a path; however, there is a function that enables an application to offset a region (OffsetRgn). Using PathToRegion, an application can create the effect of animating a complex shape by creating a path that defines the shape, converting the path into a region (by calling PathToRegion), and then repeatedly painting, moving, and erasing the region (by calling functions in a sequence, such as FillRgn, OffsetRgn, and FillRgn).

+$#A Curved Paths

An application can flatten the curves in a path by calling the FlattenPath function. This function is especially useful for applications that fit text onto the contour of a path which contains curves. To fit the text, the application must perform the following steps:

1. Create the path where the text appears.

2. Call the FlattenPath function to convert the curves in a path into line segments.

3. Call the GetPath function to retrieve those line segments.

4. Calculate the length of each line and the width of each character in the string.

5. Use line-width and character-width data to position each character along the curve.

+$#A Using Paths

This section contains a code sample that enables the user to select a font of a particular point size (by using the Choose Font dialog box), select a clip path (by using text drawn with this font), and then view the result of clipping to the text.

This code sample was used to create the illustration that appears in Clip Paths.

CHOOSEFONT cf; /* common dialog box font structure */

LOGFONT lf; /* logical font structure */

HFONT hfont; /* new logical font handle */

HFONT hfontOld; /* original logical font handle */

HDC hdc; /* display DC handle */

int nXStart, nYStart; /* drawing coordinates */

RECT rc; /* rect structure for painting window */

SIZE sz; /* structure that receives text extents */

double aflSin[90]; /* sine of 0-90 degrees */

double aflCos[90]; /* cosine of 0-90 degrees */

double flRadius,a; /* radius of circle */

int iMode; /* clipping mode */

HRGN hrgn; /* clip region handle */

LRESULT APIENTRY MainWndProc(

HWND hwnd, /* window handle */

UINT message, /* type of message */

WPARAM wParam, /* additional information */

LPARAM lParam) /* additional information */

{

PAINTSTRUCT ps;

switch (message) {

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

EndPaint(hwnd, &ps);

break;

case WM_COMMAND: /* command from app's menu */

switch (wParam) {

case IDM_VANISH: /* erases client area */

hdc = GetDC(hwnd);

GetClientRect(hwnd, &rc);

FillRect(hdc, &rc, GetStockObject(WHITE_BRUSH));

ReleaseDC(hwnd, hdc);

break;

case IDM_AND: /* sets clip mode to RGN_AND */

iMode = RGN_AND;

break;

case IDM_COPY: /* sets clip mode to RGN_COPY */

iMode = RGN_COPY;

break;

case IDM_DIFF: /* sets clip mode to RGN_DIFF */

iMode = RGN_DIFF;

break;

case IDM_OR: /* sets clip mode to RGN_OR */

iMode = RGN_OR;

break;

case IDM_XOR: /* sets clip mode to RGN_XOR */

iMode = RGN_XOR;

break;

case IDM_CLIP_PATH:

/* Retrieve a cached DC for the window. */

hdc = GetDC(hwnd);

/*

* Use the font requested by the user in the

* Choose Font dialog box to create a logical font,

* then select that font into the device context.

*/

hfont = CreateFontIndirect(cf.lpLogFont);

hfontOld = SelectObject(hdc, hfont);

/*

* Retrieve the dimensions of the rectangle

* that surrounds the text.

*/

GetTextExtentPoint32(hdc, "Clip Path", 9, &sz);

/*

* Set a clipping region using the rect that

* surrounds the text.

*/

hrgn = CreateRectRgn(nXStart, nYStart,

nXStart + sz.cx,

nYStart + sz.cy);

SelectClipRgn(hdc, hrgn);

/*

* Create a clip path using text drawn with

* the user's requested font.

*/

BeginPath(hdc);

TextOut(hdc, nXStart, nYStart, "Clip Path", 9);

EndPath(hdc);

SelectClipPath(hdc, iMode);

/* Compute the sine of 0, 1, ... 90 degrees. */

for (i = 0; i < 90; i++) {

aflSin[i] = sin( (((double)i) / 180.0)

* 3.14159);

}

/* Compute the cosine of 0, 1,... 90 degrees. */

for (i = 0; i < 90; i++) {

aflCos[i] = cos( (((double)i) / 180.0)

* 3.14159);

}

/* Set the radius value. */

flRadius = (double)(2 * sz.cx);

/*

* Draw the 90 rays extending from the

* radius to the edge of the circle.

*/

for (i = 0; i < 90; i++) {

MoveToEx(hdc, nXStart, nYStart,

(LPPOINT) NULL);

LineTo(hdc, nXStart + ((int) (flRadius

* aflCos[i])),

nYStart + ((int) (flRadius

* aflSin[i])));

}

/* Reselect the original font into the DC. */

SelectObject(hdc, hfontOld);

/* Delete the user's font. */

DeleteObject(hfont);

/* Release the DC. */

ReleaseDC(hwnd, hdc);

break;

case IDM_FONT:

/* Initialize necessary members. */

cf.lStructSize = sizeof (CHOOSEFONT);

cf.hwndOwner = hwnd;

cf.lpLogFont = &lf;

cf.Flags = CF_SCREENFONTS | CF_EFFECTS;

cf.rgbColors = RGB(0, 255, 255);

cf.nFontType = SCREEN_FONTTYPE;

/*

* Display the Font dialog box, allow the user

* to choose a font, and render text in the

* window with that selection.

*/

if (ChooseFont(&cf)) {

hdc = GetDC(hwnd);

hfont = CreateFontIndirect(cf.lpLogFont);

hfontOld = SelectObject(hdc, hfont);

crOld = SetTextColor(hdc, cf.rgbColors);

TextOut(hdc, nXStart, nYStart,

"Clip Path", 9);

SetTextColor(hdc, crOld);

SelectObject(hdc, hfontOld);

DeleteObject(hfont);

ReleaseDC(hwnd, hdc);

}

break;

default:

return DefWindowProc(hwnd, message, wParam,

lParam);

}

break;

case WM_DESTROY: /* window is being destroyed */

PostQuitMessage(0);

break;

default: /* if unprocessed... */

return DefWindowProc(hwnd, message, wParam, lParam);

}

return 0;

}

+$#A Path Functions

Use the following functions to create, alter, or draw paths.

AbortPath
BeginPath
CloseFigure
EndPath
FillPath
FlattenPath
GetMiterLimit
GetPath
PathToRegion
SetMiterLimit
StrokeAndFillPath
StrokePath
WidenPath

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques facilement


Pens.rtf

+$#AK Pens

A pen is a graphics tool that an application for Microsoft® Windows® uses to draw lines and curves. Drawing applications use pens to draw freehand lines, straight lines, and curves. Computer-aided design (CAD) applications use pens to draw visible lines, hidden lines, section lines, center lines, and so on. Word processing and desktop publishing applications use pens to draw borders and rules. Spreadsheet applications use pens to designate trends in graphs and to outline bar graphs and pie charts.

+$#A About Pens

There are two types of pen: cosmetic and geometric. A cosmetic pen is used with applications requiring lines of fixed width and lines that are quickly drawn. A CAD application, for example, uses a cosmetic pen to generate hidden, section, center, and dimension lines that are between .015 and .022 inches wide ⎯ regardless of the scale factor. A geometric pen is used with applications requiring scalable lines, lines with unique end or join styles, and lines that are wider than a single pixel. A spreadsheet application, for example, uses a geometric pen to define each of the bars in a bar graph as a wide line.

+$#A Cosmetic Pens

The dimensions of a cosmetic pen are specified in device units. Therefore, lines drawn with a cosmetic pen always have a fixed width. Lines drawn with a cosmetic pen are generally drawn three to ten times faster than lines drawn with a geometric pen. Cosmetic pens have three attributes: width, style, and color. For more information about these attributes, see Pen Attributes.

To create a cosmetic pen, an application uses the CreatePen, CreatePenIndirect, or ExtCreatePen function. To retrieve one of the three stock cosmetic pens managed by window manager, the application uses the GetStockObject function.

After a pen is created (or a handle identifying one of the stock pens is obtained), the pen is selected into the application's device context (DC) by calling the SelectObject function. From this point on, the application uses this pen for any line-drawing operations in its client area.

+$#A Geometric Pens

The dimensions of a geometric pen are specified in logical units. Therefore, lines drawn with a geometric pen can be scaled ⎯ that is, they may appear wider or narrower, depending on the current world transformation. For more information about the world transformation, see Coordinate Spaces and Transformations.

In addition to the three attributes shared with cosmetic pens (width, style, and color), geometric pens possess the following four attributes: pattern, optional hatch, end style, and join style. For more information about these attributes, see Pen Attributes.

To create a geometric pen, an application uses the ExtCreatePen function. As with cosmetic pens, the SelectObject function selects a geometric pen into the application's DC.

+$#A Pen Attributes

There are seven pen attributes that define the type of pen and its characteristics: width, style, color, pattern, hatch, end style, and join style. Both cosmetic and geometric pens have the width, style, and color attributes. Only geometric pens have the pattern, hatch, end style, and join style attributes. The pattern and optional hatch attribute are usually associated with a brush, but can also be used with geometric pens.

+$#A Width

The width attribute specifies a cosmetic pen width in device units. When used with a geometric pen, however, it specifies the pen's width in logical units. For more information about device units, see Coordinate Spaces and Transformations.

Currently, Windows limits the width of cosmetic pens to a single pixel; however, future versions may remove this limitation.

+$#A Style

The style attribute specifies the line pattern that appears when a particular cosmetic or geometric pen is used. There are eight predefined pen styles. The following illustration shows the seven of these styles that are defined by Windows.

{ewc msdncd, EWGraphic, group10663 0 /a "SDK_01.bmp"}

The inside-frame style is identical to the solid style for cosmetic pens. However, it operates differently when used with a geometric pen. If the geometric pen is wider than a single pixel and a drawing function uses the pen to draw a border around a filled object, Windows draws the border inside the object's frame. By using the inside-frame style, an application can ensure that an object appears entirely within the specified dimensions, regardless of the geometric pen width.

In addition to the seven styles defined by Windows, there is an eighth style that is user (or application) defined. A user-defined style generates lines with a customized series of dashes and dots.

Use the CreatePen, CreatePenIndirect, or ExtCreatePen function to create a pen that has the Windows-defined styles. Use the ExtCreatePen function to create a pen that has a user-defined style.

+$#A Color

The color attribute specifies the pen's color. An application can create a cosmetic pen with a unique color by storing the red, green, blue (RGB) triplet that specifies the desired color in a COLORREF structure and passing this structure's address to the CreatePen, CreatePenIndirect, or ExtCreatePen function. (The stock pens are limited to black, white, and invisible.) For more information about RGB triplets and color, see Colors.

+$#A Pattern

The pattern attribute specifies the pattern of a geometric pen.

The following illustration shows lines drawn with different geometric pens. Each pen was created using a different pattern attribute.

{ewc msdncd, EWGraphic, group10663 1 /a "SDK_02.bmp"}

The first line in the previous illustration is drawn using one of the six available hatch patterns; for more information about hatch patterns, see Hatch. The next line is drawn using the hollow pattern, identical to the null pattern. The third line is drawn using a custom pattern created from an 8- by 8-pixel bitmap. (For more information about bitmaps and their creation, see Bitmaps.) The last line is drawn using a solid pattern. Creating a brush and passing its handle to the ExtCreatePen function creates a pattern.

+$#A Hatch

The hatch attribute specifies the hatch type of a geometric pen with the hatch pattern attribute. There are six patterns available. The following illustration shows lines drawn using different hatch patterns.

{ewc msdncd, EWGraphic, group10663 2 /a "SDK_03.bmp"}

+$#A End Cap

The end cap attribute specifies the shape of a geometric pen: round, square, or flat. The following illustration shows parallel lines drawn using each type of end cap.

{ewc msdncd, EWGraphic, group10663 3 /a "SDK_04.bmp"}

The round and square end caps extend past the starting and ending points of a line drawn with a geometric pen; the flat end cap does not.

+$#A Join

The join attribute specifies how the ends of two geometric lines are joined: beveled, mitered, or round. The following illustration shows pairs of connected lines drawn using each type of join.

{ewc msdncd, EWGraphic, group10663 4 /a "SDK_05.bmp"}

+$#A Using Pens

This section contains sample code that demonstrates the appearance of lines drawn using various pen styles and attributes.

LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,

LPARAM lParam)

{

PAINTSTRUCT ps;

LOGBRUSH lb;

RECT rc;

HDC hdc;

int i;

HPEN hPen, hPenOld;

DWORD dwPenStyle[] = {

PS_DASH,

PS_DASHDOT,

PS_DOT,

PS_INSIDEFRAME,

PS_NULL,

PS_SOLID

};

UINT uHatch[] = {

HS_BDIAGONAL,

HS_CROSS,

HS_DIAGCROSS,

HS_FDIAGONAL,

HS_HORIZONTAL,

HS_VERTICAL

};

switch (uMsg)

{

case WM_PAINT:

{

GetClientRect(hWnd, &rc);

rc.left += 10;

rc.top += 10;

rc.bottom -= 10;

/* Initialize the pen's "brush." */

lb.lbStyle = BS_SOLID;

lb.lbColor = RGB(255,0,0);

lb.lbHatch = 0;

hdc = BeginPaint(hWnd, &ps);

for (i = 0; i < 6; i++)

{

hPen = ExtCreatePen(PS_COSMETIC | dwPenStyle[i],

1, &lb, 0, NULL);

hPenOld = SelectObject(hdc, hPen);

MoveToEx(hdc, rc.left + (i * 20), rc.top, NULL);

LineTo(hdc, rc.left + (i * 20), rc.bottom);

SelectObject(hdc, hPenOld);

DeleteObject(hPen);

}

rc.left += 150;

for (i = 0; i < 6; i++)

{

lb.lbStyle = BS_HATCHED;

lb.lbColor = RGB(0,0,255);

lb.lbHatch = uHatch[i];

hPen = ExtCreatePen(PS_GEOMETRIC,

5, &lb, 0, NULL);

hPenOld = SelectObject(hdc, hPen);

MoveToEx(hdc, rc.left + (i * 20), rc.top, NULL);

LineTo(hdc, rc.left + (i * 20), rc.bottom);

SelectObject(hdc, hPenOld);

DeleteObject(hPen);

}

EndPaint(hWnd, &ps);

}

break;

case WM_DESTROY:

DeleteObject(hPen);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, uMsg, wParam, lParam);

}

return FALSE;

}

+$#A Pen Functions

Following are the functions that support the pen interface.

CreatePen
CreatePenIndirect
ExtCreatePen

Créé avec HelpNDoc Personal Edition: Générateur complet de livres électroniques ePub


Performance Monitoring.rtf

+$#AK Performance Monitoring

Performance monitoring refers to the procedure for determining the system resources that an application consumes, such as processor time and memory.

+$#A About Performance Monitoring

An application developer can use the Microsoft® Win32® performance monitoring functions to determine whether an application uses system resources efficiently and to fine-tune its performance. These functions are especially helpful in evaluating the effects of changes as developers migrate Microsoft® Windows® version 3.x applications to Win32.

+$#A Counters and Elapsed Times

A counter is a general term used in programming to refer to an incrementing variable. Some systems include a high-resolution performance counter that provides high-resolution elapsed times. The elapsed time can be used to fine-tune sections of source code. Win32 provides two functions that enable application developers to obtain the value of a counter.

If a high-resolution performance counter exists on the system, the QueryPerformanceFrequency function can be used to express the frequency, in counts per second. The value of the count is processor dependent. On some processors, for example, the count might be the cycle rate of the processor clock.

The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter (if one exists on the system). By calling this function at the beginning and end of a section of code, an application essentially uses the counter as a stopwatch.

For example, QueryPerformanceFrequency might indicate that the frequency of the high-resolution performance counter on the system is 50,000 counts per second. If the application calls QueryPerformanceCounter immediately before and immediately after the section of code to be timed, the counter values might be 1500 counts and 3500 counts, respectively. These values would indicate that .04 seconds (2000 counts) elapsed while the code executed.

+$#A Registry Database

Windows includes several tools that monitor the system. To present data in a different format, to use data inside another application, or to create a performance monitor, an application must retrieve information through the registry. For more information about the registry, see Registry.

+$#A Getting Performance Data

An application can obtain system-performance information by using the RegQueryValueEx registry function with the HKEY_PERFORMANCE_DATA key. The application can use the RegOpenKey function to open the HKEY_PERFORMANCE_DATA handle, but it should use RegCloseKey to close the handle when it has finished running. This allows monitored software to be to be removed or installed, which cannot happen while they are being monitored. Network transports and drivers are examples of monitored software you may wish to remove.

Performance data is not stored in the registry. Using the RegQueryValueEx function with the HKEY_PERFORMANCE_DATA key causes the system to collect the data from the appropriate system object managers.

If an application is accessing a remote system, it must use the RegConnectRegistry function with the name of the remote system and the HKEY_PERFORMANCE_DATA key. RegConnectRegistry retrieves a key that the application can use with RegQueryValueEx to retrieve performance data from the remote system.

The basic layout of the data retrieved by RegQueryValueEx is a PERF_DATA_BLOCK structure followed by one PERF_OBJECT_TYPE structure and accompanying data for each type of object being monitored. The system being observed defines objects that can be monitored. Typical object types include processors, disks, and memory.

The PERF_DATA_BLOCK structure describes the system and the performance data. This structure contains the size of the data, the number of objects being monitored, the frequency and value of the high-resolution performance counter, an offset to the system name, and so on.

The PERF_OBJECT_TYPE structure describes the performance data for one type of object. This structure contains the size of the structure and the object-specific data that follows the structure, the number of counters defined for the object type, the number of instances of this object type, the level of informational details provided, the code page, indices to the object name and Explain title in the title database, and so on.

Following each PERF_OBJECT_TYPE structure, there is a list of PERF_COUNTER_DEFINITION structures, one for each counter defined for the object. The PERF_COUNTER_DEFINITION structure specifies the type and size of the counter, the offset to the counter, the detail level, the counter name, and Explain title in the registry, and so on.

An instance is a unique copy of a particular object type. The number of instances of a particular object type determines what follows the list of counter definitions. Not all object types have instances. A memory object, for example, has no instances because the system has only one memory. If the object type does not have instances, the counter definitions are followed by a single PERF_COUNTER_BLOCK structure and data for each counter. PERF_COUNTER_BLOCK contains the total length of the structure, in bytes, and the counter data that follows it.

If the object type has one or more instances, the list of counter definitions is followed by a PERF_INSTANCE_DEFINITION structure and a PERF_COUNTER_BLOCK structure for each instance. The PERF_INSTANCE_DEFINITION structure includes the name, identifier, and name of the parent of the instance. For example, the thread object type has instances; each thread in the system is an instance.

The number of object types, instances, and counters may vary from one call to the RegQueryValueEx function to another. Therefore, the number and size of structures included in the performance data as well as the size of the data that follows each structure may also vary. To ensure that it is receiving the appropriate information, an application must use the offsets included in the performance structures to navigate through the performance data. Every offset is relative to the beginning of the structure containing it.

+$#A Getting Descriptive Text

The names of the objects and counters, as well as the text that explains their meaning, can be obtained by calling the RegQueryValueEx function with hkey set to HKEY_PERFORMANCE_DATA and lpszValueName set to "Counter <langid>" or "Explain <langid>", where <langid> is a language identifier string. A language identifier string is the ASCII representation of a 3-digit hexadecimal language identifier. For example, the code for English is "009"; calling RegQueryValueEx with "Counter 009" will obtain the object names in English.

The 'Counter' and 'Explain' data are stored as MULTI_SZ strings. This type of data consists of a list of Unicode strings, each terminated with UNICODE_NULL. The last string is followed by an additional UNICODE_NULL.

The strings are listed in pairs. The first item is the Unicode string of the index contained in PERF_OBJECT_TYPE.ObjectNameTitleIndex or PERF_COUNTER_DEFINITION.CounterNameTitleIndex. The second item is the actual name of the index. Following is a fragment of the paired data:

2 System

4 Memory

6 % Processor Time

10 Read Operations/sec

12 Write Operations/sec

To retrieve a name or the Explain text for an item, an application should perform the following steps:

1. If the system is remote, call the RegConnectRegistry function to establish a connection to the remote machine's registry.

2. Call the RegQueryValueEx function, specifying either 'Counter <langid>' or 'Explain <langid>' as the name of the value to query. This provides MULTI_SZ data containing either the names of all the objects and counters, or Help information for all the objects and counters. Note that these query strings are case-sensitive.

If the call to RegQueryValueEx is successful, the return value is ERROR_SUCCESS, and the names are returned in the buffer pointed to by the function's lpbData parameter.

If the input buffer size is 0, the RegQueryValueEx return value is the buffer size needed to store the names.

If the input buffer size is too small, the RegQueryValueEx return value is ERROR_MORE_DATA.

If the specified language code is not installed in the system, the RegQueryValueEx return value is ERROR_FILE_NOT_FOUND.

Refer to RegQueryValueEx for further details on its operation.

3. Convert the appropriate index (PERF_OBJECT_TYPE.ObjectNameTitleIndex, PERF_OBJECT_TYPE.ObjectHelpTitleIndex, PERF_COUNTER_DEFINITION.CounterNameTitleIndex, or PERF_COUNTER_DEFINITION.CounterHelpTitleIndex) into Unicode or ASCII, depending on whether the application is Unicode or ASCII.

4. Search the MULTI_SZ data for the appropriate index.

5. Retrieve the string following the matching index as the object's name or descriptive information.

See Displaying Object, Counter, and Instance Names and Displaying Counter Title Indices for sample code illustrating these tasks.

+$#A Getting Selected Data

When using the RegQueryValueEx function, an application must use the lpcbData parameter to specify a byte count of the amount of data to retrieve. Estimating this amount is difficult because the number of object types and their instances is determined by the system configuration and system activity. The amount may vary from one request to another. If the function does not pass enough space, the return value will be ERROR_MORE_DATA. Therefore, an application should include a retry loop in which it passes increasing amounts of buffer space until it no longer gets this error. The successful buffer size should be the starting point for subsequent calls to RegQueryValueEx.

In some cases, the application may not need to retrieve all the performance data provided by the system. Retrieving all of that data may be costly in terms of processor or memory requirements. The lpszValueName parameter of RegQueryValueEx indicates the amount of information to retrieve. This parameter can be in one of the following formats. The strings are case-sensitive. If a string includes more than one word, the words must be separated by a space.

String | Description
Global | The function returns all data for counters on the local computer except those included in the Costly category, described later in this table.
nnn xx yyy | Each of these strings is a Unicode string representing the decimal value for PERF_OBJECT_TYPE.ObjectNameTitleIndex. The function returns data only about the specified object types on the local computer.
 | Note that more object types may be returned than are requested. This situation occurs if other object types are needed to support the requested data; for example, processes are needed to identify threads, so processes will also be returned even if only threads are requested.
Foreign ssss | The string ssss is the name of a foreign computer ⎯ that is, one that does not support the registry calls for returning data remotely. The function returns all data for counters on the foreign computer if your system is capable of collecting data from a foreign computer. This approach can be tried if RegConnectRegistry fails to connect to a foreign computer.
Foreign ssss nnn xx yyy | This format combines Foreign ssss and nnn xx yyy, discussed previously. The function returns all data for the specified counters on the foreign computer if the system is capable of collecting data from a foreign computer. An application can try this approach if RegConnectRegistry fails to connect to a foreign computer, but all of the available data is not wanted.
Costly | The function returns data for object types whose data is expensive to collect in terms of processor time or memory usage. A secondary thread may be started if an application needs to respond to the user during data collection.

+$#A Using Performance Monitoring

This section includes examples of code that display objects, counters, and Explain text for a system.

• Displaying object, counter, and instance names

• Displaying counter title indices

+$#A Displaying Object, Counter, and Instance Names

The example in this section shows how an application can display object, counter, and instance names.

#define UNICODE 1

#define _UNICODE 1

//

// "C" Include files

//

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <malloc.h>

//

// Windows Include files

//

#include <windows.h>

#include <winperf.h>

#include <tchar.h>

//

// Local constants

//

#define RESERVED 0L // reserved argument in fn. calls

#define INITIAL_SIZE 40960L // initial perf data buff size

#define EXTEND_SIZE 4096L // extend size of perf data buff

#define LINE_LENGTH 80 // display line length

#define WRAP_POINT LINE_LENGTH-12 // wrap point for explain text

#define OBJECT_TEXT_INDENT 4 // indent of object explain text

//

// Command line option values

//

#define COMMAND_ERROR 0

#define COMMAND_HELP 1

#define COMMAND_COUNTER 2

#define COMMAND_NOT_FOUND 3

//

//

// Text string Constant definitions

//

const LPTSTR NamesKey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");

const LPTSTR DefaultLangId = TEXT("009");

const LPTSTR Counters = TEXT("Counter ");

const LPTSTR Help = TEXT("Explain ");

const LPTSTR LastHelp = TEXT("Last Help");

const LPTSTR LastCounter = TEXT("Last Counter");

const LPTSTR Slash = TEXT("\\");

const LPTSTR Global = TEXT("Global");

//

// Global Buffers

//

TCHAR cValueName[LINE_LENGTH]; // used to store counter value string

CHAR cExeName[LINE_LENGTH*2]; // used in processing command line

CHAR cCtrName[LINE_LENGTH]; // used in processing command line

BOOL

StringToInt (

IN LPTSTR InStr,

OUT PDWORD pdwOutInt

)

/*++

StringToInt

Returns the decimal interpretation of the string in the InStr

argument. Works for either Unicode or ANSI strings.

Arguments:

InStr

string to translate

pdwOutInt

base 10 version of the number in the InStr argument

Return Value:

TRUE if conversion was successful

FALSE if not

--*/

{

// check for NULL pointers passed to function

if ((!InStr) || (!pdwOutInt)) {

SetLastError (ERROR_BAD_ARGUMENTS);

return FALSE;

}

if ( (_stscanf (InStr, TEXT(" %d"), pdwOutInt)) == 1) {

return TRUE;

} else {

return FALSE;

}

} // StringToInt

VOID

DisplayCommandHelp (

)

/*++

DisplayCommandHelp

displays usage of command line arguments

Arguments

NONE

Return Value

NONE

--*/

{

printf ("\n\nPERFEX:");

printf ("\n Sample program to demonstrate Win32 Performance API");

printf ("\n\nUsage:");

printf ("\n\n perfex [-? | counter]");

printf ("\n\n -? Displays a list of all counter objects in the system");

printf ("\n counter is the name of a counter object");

printf ("\n and displays the performance counters for that object");

printf ("\n");

} // DisplayCommandHelp

VOID

DisplayNotFound (

)

/*++

DisplayNotFound

displays not found & usage of command line arguments

Arguments

NONE

Return Value

NONE

--*/

{

printf ("\n*** Counter object not found in the system***\n");

DisplayCommandHelp(); // display command line usage

} // DisplayNotFound

LPTSTR

*BuildNameTable(

HKEY hKeyRegistry, // handle to registry db with counter names

LPTSTR lpszLangId, // unicode value of Language subkey

PDWORD pdwLastItem // size of array in elements

)

/*++

BuildNameTable

Caches the counter names and explain text to accelerate name lookups

for display.

Arguments:

hKeyRegistry

Handle to an open registry (this can be local or remote.) and

is the value returned by RegConnectRegistry or a default key.

lpszLangId

The unicode id of the language to look up. (default is 009)

pdwLastItem

The last array element

Return Value:

pointer to an allocated table.

(the caller must free it when finished!)

the table is an array of pointers to zero terminated TEXT strings.

A NULL pointer is returned if an error occured. (error value is

available using the GetLastError function).

The structure of the buffer returned is:

Array of pointers to zero terminated strings consisting of

pdwLastItem elements

MULTI_SZ string containing counter id's and names returned from

registry for the specified language

MULTI_SZ string containing explain text id's

and explain text strings

as returned by the registry for the specified language

The structures listed above are contiguous so that they may be freed

by a single "free" call when finished with them, however only the

array elements are intended to be used.

--*/

{

LPTSTR *lpReturnValue; // returned pointer to buffer

LPTSTR *lpCounterId; //

LPTSTR lpCounterNames; // pointer to Names buffer returned by reg.

LPTSTR lpHelpText ; // pointet to exlpain buffer returned by reg.

LPTSTR lpThisName; // working pointer

BOOL bStatus; // return status from TRUE/FALSE fn. calls

LONG lWin32Status; // return status from fn. calls

DWORD dwHelpItems; // number of explain text items

DWORD dwCounterItems; // number of counter text items

DWORD dwValueType; // value type of buffer returned by reg.

DWORD dwArraySize; // size of pointer array in bytes

DWORD dwBufferSize; // size of total buffer in bytes

DWORD dwCounterSize; // size of counter text buffer in bytes

DWORD dwHelpSize; // size of help text buffer in bytes

DWORD dwThisCounter; // working counter

DWORD dwLastId; // largest ID value used by explain/counter text

HKEY hKeyValue; // handle to Perflib entry in registry

TCHAR CounterNameBuffer[LINE_LENGTH]; // buffer conatining Counter query

TCHAR HelpNameBuffer[LINE_LENGTH]; // buffer conatining Help query

BOOL bClosePerformanceKey; // flag for closing HKEY_PERFORMANCE_DATA key

//initialize data

lpReturnValue = NULL;

hKeyValue = NULL;

bClosePerformanceKey = FALSE;

// check for null arguments and insert defaults if necessary

if (!lpszLangId) {

lpszLangId = DefaultLangId;

}

// open handle to Perflib key in registry to get number of items

// used in order to compute array and buffer sizes

lWin32Status = RegOpenKeyEx (

hKeyRegistry,

NamesKey,

RESERVED,

KEY_READ,

&hKeyValue);

// on error, free buffers, close keys and return NULL pointer

if (lWin32Status != ERROR_SUCCESS) {

goto BNT_BAILOUT;

}

// query registry to get number of Explain text items

dwBufferSize = sizeof (dwHelpItems);

lWin32Status = RegQueryValueEx (

hKeyValue,

LastHelp,

RESERVED,

&dwValueType,

(LPBYTE)&dwHelpItems,

&dwBufferSize);

if ((lWin32Status != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) {

goto BNT_BAILOUT;

}

// query registry to get number of counter and object name items

dwBufferSize = sizeof (dwCounterItems);

lWin32Status = RegQueryValueEx (

hKeyValue,

LastCounter,

RESERVED,

&dwValueType,

(LPBYTE)&dwCounterItems,

&dwBufferSize);

if ((lWin32Status != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) {

goto BNT_BAILOUT;

}

// use the greater of Help items or Counter Items to size array

if (dwHelpItems >= dwCounterItems) {

dwLastId = dwHelpItems;

} else {

dwLastId = dwCounterItems;

}

// array size is # of elements (+ 1, since names are "1" based)

// times the size of a pointer

dwArraySize = (dwLastId + 1) * sizeof(LPTSTR);

// build the Query strings for the specified language ID

lstrcpy (CounterNameBuffer, Counters);

lstrcat (CounterNameBuffer, lpszLangId);

lstrcpy (HelpNameBuffer, Help);

lstrcat (HelpNameBuffer, lpszLangId);

// get size of counter names

dwBufferSize = 0;

lWin32Status = RegQueryValueEx (

HKEY_PERFORMANCE_DATA,

CounterNameBuffer,

RESERVED,

&dwValueType,

NULL,

&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

bClosePerformanceKey = TRUE;

dwCounterSize = dwBufferSize;

// get size of help text

dwBufferSize = 0;

lWin32Status = RegQueryValueEx (

HKEY_PERFORMANCE_DATA,

HelpNameBuffer,

RESERVED,

&dwValueType,

NULL,

&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

dwHelpSize = dwBufferSize;

// allocate buffer with room for pointer array, counter name

// strings and help name strings

lpReturnValue = malloc (dwArraySize + dwCounterSize + dwHelpSize);

if (!lpReturnValue) {

lWin32Status = ERROR_OUTOFMEMORY;

goto BNT_BAILOUT;

}

// initialize pointers into buffer

lpCounterId = lpReturnValue;

lpCounterNames = (LPTSTR)((LPBYTE)lpCounterId + dwArraySize);

lpHelpText = (LPTSTR)((LPBYTE)lpCounterNames + dwCounterSize);

// initialize the pointers to NULL

for (dwBufferSize = 0; dwBufferSize <= dwLastId; dwBufferSize++) {

lpCounterId[dwBufferSize] = NULL;

}

// read counter names into buffer. Counter names will be stored as

// a MULTI_SZ string in the format of "###" "Name"

dwBufferSize = dwCounterSize;

lWin32Status = RegQueryValueEx (

HKEY_PERFORMANCE_DATA,

CounterNameBuffer,

RESERVED,

&dwValueType,

(LPVOID)lpCounterNames,

&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

// read explain text into buffer. Counter names will be stored as

// a MULTI_SZ string in the format of "###" "Text..."

dwBufferSize = dwHelpSize;

lWin32Status = RegQueryValueEx (

HKEY_PERFORMANCE_DATA,

HelpNameBuffer,

RESERVED,

&dwValueType,

(LPVOID)lpHelpText,

&dwBufferSize);

if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;

// load counter array items, by locating each text string

// in the returned buffer and loading the

// address of it in the corresponding pointer array element.

for (lpThisName = lpCounterNames;

*lpThisName;

lpThisName += (lstrlen(lpThisName)+1) ) {

// first string should be an integer

// (in decimal digit characters)

// so translate to an integer for use in array

// element identification

bStatus = StringToInt (lpThisName, &dwThisCounter);

if (!bStatus) {

// error is in GetLastError

goto BNT_BAILOUT; // bad entry

}

// point to corresponding counter name which follows the id number

// string.

lpThisName += (lstrlen(lpThisName)+1);

// and load array element with pointer to string

lpCounterId[dwThisCounter] = lpThisName;

}

// repeat the above for the explain text strings

for (lpThisName = lpHelpText;

*lpThisName;

lpThisName += (lstrlen(lpThisName)+1) ) {

// first string should be an integer (in decimal unicode digits)

bStatus = StringToInt (lpThisName, &dwThisCounter);

if (!bStatus) {

// error is in GetLastError

goto BNT_BAILOUT; // bad entry

}

// point to corresponding counter name

lpThisName += (lstrlen(lpThisName)+1);

// and load array element;

lpCounterId[dwThisCounter] = lpThisName;

}

// if the last item arugment was used,

// then load the last ID value in it

if (pdwLastItem) *pdwLastItem = dwLastId;

// close the registry keys

RegCloseKey (hKeyValue);

RegCloseKey (HKEY_PERFORMANCE_DATA);

// exit returning the pointer to the buffer

return lpReturnValue;

BNT_BAILOUT:

if (lWin32Status != ERROR_SUCCESS) {

// if lWin32Status has error, then set last error value to it,

// otherwise assume that last error already has value in it

SetLastError (lWin32Status);

}

if (lpReturnValue) {

free ((LPVOID)lpReturnValue);

}

if (bClosePerformanceKey == TRUE) RegCloseKey (HKEY_PERFORMANCE_DATA);

if (hKeyValue) RegCloseKey (hKeyValue);

return NULL;

} // BuildNameTable

DWORD

GetSystemPerfData (

IN HKEY hKeySystem,

IN LPTSTR pValue,

IN PPERF_DATA_BLOCK *pPerfData

)

/*++

GetSystemPerfData

Allocates data buffer as required and queries performance data

specified in pValue from registry.

Arguments

hKeySystem

Handle to performance data in registry

pValue

Value string to return from registry

pPerfData

address of pointer to allocated perf data block that is

filled in by call to RegQueryValue.

Return Value

ERROR_SUCCESS if data returned

WIN 32 ERROR if error encountered

--*/

{ // GetSystemPerfData

LONG lError ; // Win 32 Error returned by fn. calls

DWORD Size; // size of data buffer passed to fn. call

DWORD Type; // type of data buffer returned by fn. call

// allocate initial buffer if one is not passed into this routine

if (*pPerfData == NULL) {

*pPerfData = malloc (INITIAL_SIZE);

if (*pPerfData == NULL) {

return ERROR_OUTOFMEMORY;

}

}

while (TRUE) {

Size = _msize (*pPerfData); // query the size of the data buffer

lError = RegQueryValueEx ( // get performance data from reg.

hKeySystem,

pValue,

RESERVED,

&Type,

(LPBYTE)*pPerfData,

&Size) ;

// check for success and valid perf data block signature

if ((!lError) &&

(Size > 0) &&

(*pPerfData)->Signature[0] == (WCHAR)'P' &&

(*pPerfData)->Signature[1] == (WCHAR)'E' &&

(*pPerfData)->Signature[2] == (WCHAR)'R' &&

(*pPerfData)->Signature[3] == (WCHAR)'F' ) {

return (ERROR_SUCCESS) ;

}

// if buffer is not big enough, reallocate and try again

if (lError == ERROR_MORE_DATA) {

*pPerfData = realloc (

*pPerfData,

_msize (*pPerfData) +

EXTEND_SIZE) ;

if (!*pPerfData) {

return (lError) ;

}

} else {

return (lError) ;

}

}

} // GetSystemPerfData

/*++

Pointer Functions

These functions are used to walk down the various structures

in the perf data block returned by the call to RegQueryValueEx

--*/

PPERF_OBJECT_TYPE

FirstObject (

PPERF_DATA_BLOCK pPerfData

)

{

return ((PPERF_OBJECT_TYPE) ((PBYTE) pPerfData + pPerfData->HeaderLength)) ;

}

PPERF_OBJECT_TYPE

NextObject (

PPERF_OBJECT_TYPE pObject

)

{ // NextObject

return ((PPERF_OBJECT_TYPE) ((PBYTE) pObject + pObject->TotalByteLength)) ;

} // NextObject

PERF_COUNTER_DEFINITION *

FirstCounter(

PERF_OBJECT_TYPE *pObjectDef

)

{

return (PERF_COUNTER_DEFINITION *)

((PCHAR) pObjectDef + pObjectDef->HeaderLength);

}

PERF_COUNTER_DEFINITION *

NextCounter(

PERF_COUNTER_DEFINITION *pCounterDef

)

{

return (PERF_COUNTER_DEFINITION *)

((PCHAR) pCounterDef + pCounterDef->ByteLength);

}

PERF_INSTANCE_DEFINITION *

FirstInstance (

PERF_OBJECT_TYPE *pObject

)

{

return (PERF_INSTANCE_DEFINITION *)

((PBYTE) pObject + pObject->DefinitionLength);

}

PERF_INSTANCE_DEFINITION *

NextInstance (

PERF_INSTANCE_DEFINITION *pInstance

)

{

// next instance is after

// this instance + this instances counter data

PERF_COUNTER_BLOCK *pCtrBlk;

pCtrBlk = (PERF_COUNTER_BLOCK *)

((PBYTE)pInstance + pInstance->ByteLength);

return (PERF_INSTANCE_DEFINITION *)

((PBYTE)pInstance + pInstance->ByteLength + pCtrBlk->ByteLength);

}

LONG

PrintExplainText(

DWORD Indent,

LPTSTR szTextString

)

/*++

PrintExplainText

Prints Long strings in a wrapped format for display on screen

Arguments

Indent

number of characters to indent text from left margin

szTextString

string to print on stdout

ReturnValue

number of lines of text printed

--*/

{

LPTSTR szThisChar;

BOOL bNewLine;

DWORD dwThisPos;

DWORD dwLinesUsed;

szThisChar = szTextString;

dwLinesUsed = 0;

// check arguments

if (!szTextString) {

return dwLinesUsed;

}

if (Indent > WRAP_POINT) {

return dwLinesUsed; // can't do this

}

bNewLine = TRUE;

// print text

while (*szThisChar) {

if (bNewLine){

for (dwThisPos = 0; dwThisPos < Indent; dwThisPos++) {

printf (" ");

}

bNewLine = FALSE;

}

if ((*szThisChar == ' ') && (dwThisPos >= WRAP_POINT)) {

printf ("\n");

bNewLine = TRUE;

// go to next printable character

while (*szThisChar <= ' ') {

szThisChar++;

}

dwLinesUsed++;

} else {

printf ("%c", *szThisChar);

szThisChar++;

}

dwThisPos++;

}

printf ("\n");

bNewLine = TRUE;

dwLinesUsed++;

return dwLinesUsed;

} // PrintExplainText

DWORD

ProcessCommandLine (

IN LPTSTR lpCmdLine,

IN LPTSTR *lppCounterNames,

IN DWORD dwLastCounter,

OUT LPTSTR *lpValueString

)

/*++

ProcesCommandLine

Reads arguments in command line and determines

what program should do.

Command line syntax is:

PERFEX [ ? | Object]

if "?" is in command line, then list of counter objects is displayed

if a text string (other than "?") then string is translated to a

counter object id and the counters for that object are displayed

Arguments

lpCmdLine

null terminated string that contains command line arguments

lppCounterNames

pointer to array of counter names (used for lookup)

dwLastCounter

number of elements in CounterNames array

lpValueString

value string to send to ReqQueryValue in order to return counter

data for specified counter

ReturnValue

TRUE if valid data is returned

FALSE if unable to process command line

(command syntax help should be displayed).

--*/

{

LPTSTR lpLocalString;

DWORD dwReturnValue;

DWORD dwElem;

TCHAR LocalBuffer[100];

INT BufferSize;

INT Index;

dwReturnValue = COMMAND_ERROR; // assume error until

// proven otherwise

lpLocalString = lpCmdLine;

// move pointer past exe name

while ((*lpLocalString > TEXT(' ')) && (*lpLocalString)) lpLocalString++;

// move pointer to next non-space char;

while ((*lpLocalString <= TEXT(' ')) && (*lpLocalString)) lpLocalString++;

if (*lpLocalString) {

// check for "HELP" command line argument

if (*lpLocalString == TEXT('-')) {

if (*++lpLocalString == TEXT('?')) {

*lpValueString = Global;

dwReturnValue = COMMAND_HELP;

}

} else { // first arg is not preceded by a '-'

// so compare to counters

// see if command line matches a counter name

// duplicate the LocalString

BufferSize = sizeof(LocalBuffer) / sizeof(TCHAR) - 1;

for (Index=0; Index < BufferSize; Index++) {

if (*lpLocalString == TEXT('\0') ||

*lpLocalString == TEXT(' ')){

LocalBuffer[Index] = TEXT('\0');

break;

} else {

LocalBuffer[Index] = *lpLocalString++;

}

}

LocalBuffer[BufferSize] = TEXT('\0');

dwReturnValue = COMMAND_NOT_FOUND;

for (dwElem = 0; dwElem <= dwLastCounter; dwElem++) {

// compare counter name to command line

if (lppCounterNames[dwElem]) {

if ((lstrcmpi(lppCounterNames[dwElem], LocalBuffer))

== 0) {

// match found

// make element Number a string and exit

_stprintf ((LPTSTR)&cValueName[0], TEXT("%d"), dwElem);

*lpValueString = (LPTSTR)&cValueName[0];

dwReturnValue = COMMAND_COUNTER;

break; // out of for loop

} // endif command line matches string

} // endif lppCounterNames[dwElem] not equal NULL

} // end for loop that searches table

} // if not "-?"

} else {

dwReturnValue = COMMAND_ERROR;

}

return dwReturnValue;

} // ProcessCommandLine

VOID

DisplayObjectNames (

PPERF_DATA_BLOCK pPerf,

LPTSTR *pNames

)

/*++

DisplayObjectNames

Scans the perf data and prints the name of each object found in the

list

Arguments

pPerf

pointer to the perf data block returned by the call to

RegQueryValueEx

pNames

pointer to the name table containing the array of counter names

and explain text pointers.

Return Value

NONE

--*/

{

DWORD dwThisObject; // count of objects

PPERF_OBJECT_TYPE pThisObject; // pointer to object structure

printf ("\nThe available performance counter objects are:");

// loop from first object in buffer returned and display the

// number of objects in the buffer

for (dwThisObject = 0, pThisObject = FirstObject (pPerf);

dwThisObject < pPerf->NumObjectTypes;

dwThisObject++, pThisObject = NextObject(pThisObject)) {

printf ("\n\"%ws\"\n",

pNames[pThisObject->ObjectNameTitleIndex]);

PrintExplainText (OBJECT_TEXT_INDENT,

pNames[pThisObject->ObjectHelpTitleIndex]);

}

} // DisplayObjectNames

VOID

DisplayCounterData (

PPERF_DATA_BLOCK pPerf,

LPTSTR *pNames

)

/*++

DisplayCounterData

Scans the perf data and prints the name of counter and

instance found in the list

Arguments

pPerf

pointer to the perf data block returned by the call to

RegQueryValueEx

pNames

pointer to the name table containing the array of counter names

and explain text pointers.

Return Value

NONE

--*/

{

DWORD dwThisObject; // count of objects

PPERF_OBJECT_TYPE pThisObject; // pointer to object structure

DWORD dwThisCounter; // count of counters

PPERF_COUNTER_DEFINITION pThisCounter;

LONG lThisInstance;

PPERF_INSTANCE_DEFINITION pThisInstance;

// for each object in buffer, display each counter and instance

for (dwThisObject = 0, pThisObject = FirstObject (pPerf);

dwThisObject < pPerf->NumObjectTypes;

dwThisObject++, pThisObject = NextObject(pThisObject)) {

printf ("\n\nThe object \"%ws\", ",

pNames[pThisObject->ObjectNameTitleIndex]);

// if there are instnances, then loop through all instances

// of this object

if (pThisObject->NumInstances != PERF_NO_INSTANCES) {

printf (" which has the following instances:");

for (lThisInstance = 0,

pThisInstance = FirstInstance(pThisObject);

lThisInstance < pThisObject->NumInstances;

lThisInstance++,

pThisInstance = NextInstance(pThisInstance)) {

printf ("\n\t\"%ws\"",

(LPWSTR)((PBYTE)pThisInstance

+ pThisInstance->NameOffset));

}

printf ("\n");

} else {

printf (" which has only one occurance,");

}

// now display all counters described in this object

printf ("\nand has the following counters:");

for (dwThisCounter = 0,

pThisCounter = FirstCounter (pThisObject);

dwThisCounter < pThisObject->NumCounters;

dwThisCounter++, pThisCounter = NextCounter(pThisCounter)) {

printf ("\n \"%ws\"",

pNames[pThisCounter->CounterNameTitleIndex]);

}

}

} // DisplayCounterData

int

_CRTAPI1 main(

int argc,

char *argv[]

)

/*++

main

entry point to perfex

This program illustrates how to:

get performance counter names and explain text from the registry

obtain counter object value strings for use

in querying perf. data

walk the performance data structures returned by the registry

This program does not show how to:

format or process the counter data returned

the counter data returned by the registry depends on the

counter type. This data must be processed and formatted by

the application program.

Arguments

argc

# of command line arguments present

argv

array of pointers to command line strings

(note that these are obtained from the GetCommandLine function in

order to work with both Unicode and ANSI strings.)

ReturnValue

0 (ERROR_SUCCESS) if command was processed

Non-Zero if command error was detected.

--*/

{

LPTSTR *lpCounterText; // array of counter name strings

LPTSTR lpCommandLine; // pointer to command line string

LPTSTR lpValueString; // value string to pass to registry for data

DWORD dwLastElement; // number of elements in lpCounterText

PPERF_DATA_BLOCK pDataBlock; // pointer to perfdata block

DWORD dwStatus; // return status of fn. calls

DWORD dwAction; // display mode indicated by command line

// query counter names from registry and load into table

lpCounterText = BuildNameTable (

HKEY_LOCAL_MACHINE,

DefaultLangId,

&dwLastElement);

if (!lpCounterText) {

printf ("ERROR: Unable to read counter names from registry.");

return (TRUE);

}

lpCommandLine = GetCommandLine(); // get command line

// read command line to determine what to do

dwAction = ProcessCommandLine (lpCommandLine,

lpCounterText,

dwLastElement,

&lpValueString);

if (dwAction == COMMAND_ERROR) { // if unable to read command line

DisplayCommandHelp(); // display command line usage

free (lpCounterText); // free buffer and return error

return (TRUE);

} else if (dwAction == COMMAND_NOT_FOUND) {

DisplayNotFound(); // display not found

// & command line usage

free (lpCounterText); // free buffer and return error

return (TRUE);

} else {

// of command requires processing, then get perf data from reg.

pDataBlock = NULL;

dwStatus = GetSystemPerfData (

HKEY_PERFORMANCE_DATA,

lpValueString,

&pDataBlock);

if (dwStatus != ERROR_SUCCESS) {

printf ("\nERROR: Unable to obtain data for that counter");

free (lpCounterText);

return (dwStatus);

} else {

// if data was returned, then display in format requested by

// command line

if (dwAction == COMMAND_HELP) {

// display object names returned from registry

DisplayObjectNames (pDataBlock, lpCounterText);

} else {

// display counter data for specified object

DisplayCounterData (pDataBlock, lpCounterText);

}

}

free (lpCounterText);

return (ERROR_SUCCESS); // success

}

}

+$#A Displaying Counter Title Indices

This section includes example code that displays counter title indices and their names.

#define UNICODE 1

#define _UNICODE 1

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <windows.h>

#include <string.h>

#include <memory.h>

#include <process.h>

#include <tchar.h>

#define SZ_DUMMY 10

int GetRegDataBound(HKEY hkey)

{

TCHAR chDummy[SZ_DUMMY];

DWORD cSubKeys;

DWORD cbMaxSubkey;

DWORD cbMaxClass;

DWORD cValues;

DWORD cbMaxValueName;

DWORD cbMaxValueData;

DWORD cbSecurityDescriptor;

FILETIME ftLastWriteTime;

DWORD lErr;

DWORD cbClassSize = SZ_DUMMY;

lErr = RegQueryInfoKey(hkey, chDummy, &cbClassSize,

NULL, &cSubKeys, &cbMaxSubkey, &cbMaxClass,

&cValues, &cbMaxValueName, &cbMaxValueData,

&cbSecurityDescriptor, &ftLastWriteTime);

if (lErr == ERROR_SUCCESS)

return cbMaxValueData;

else

return -1;

}

int

_CRTAPI1 main(

)

{

HKEY hKeyData;

LONG lErr;

LPTSTR lptstr;

int iSize;

DWORD dwType;

DWORD dwSize;

int iIndex;

lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,

TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Perflib\\009"),

0, KEY_ALL_ACCESS, &hKeyData);

assert(lErr == ERROR_SUCCESS);

iSize = GetRegDataBound(hKeyData);

if (iSize == -1)

exit(-1);

iSize++;

lptstr = malloc(iSize * sizeof(TCHAR));

dwSize = iSize;

lErr = RegQueryValueEx(hKeyData, TEXT("Counters"), 0, &dwType,

(LPBYTE) lptstr, &dwSize);

assert(lErr == ERROR_SUCCESS);

while (*lptstr) {

_stscanf (lptstr, TEXT(" %d"), &iIndex);

// iIndex = atoi(lptstr);

lptstr += lstrlen(lptstr) + 1;

printf("#define CNTR_%.3d\t \"%ws\"\n", iIndex, lptstr);

lptstr += lstrlen(lptstr) + 1;

}

lErr = RegCloseKey(hKeyData);

assert(lErr == ERROR_SUCCESS);

return 0;

}

+$#A Performance Monitoring Functions

Following are the functions used for performance monitoring.

RegConnectRegistry
RegQueryValueEx
QueryPerformanceCounter
QueryPerformanceFrequency

Obsolete or Deleted Functions

ProfClear
ProfFinish
ProfFlush
ProfInsChk
ProfSampRate
ProfSetup
ProfStart
ProfStop

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide facilement


Pipes.rtf

+$#AK Pipes

A pipe is a communication conduit with two ends: a process with a handle to one end can communicate with a process having a handle to the other end. This overview describes how to create, manage, and use pipes.

+$#A About Pipes

Pipes can be one-way ⎯ where one end is read-only and the other end is write-only, or they can be two-way ⎯ where both ends of the pipe can read or write. The Microsoft® Win32® application programming interface (API) provides both anonymous (unnamed) pipes and named pipes.

+$#A Anonymous Pipes

An anonymous pipe is an unnamed, one-way pipe that transfers data between a parent process and a child process or between two child processes of the same parent process. Although it is possible to use anonymous pipes for communication between unrelated processes, it is easier to use named pipes for that purpose (for more information, see Named Pipes). Anonymous pipes are always local; they cannot be used for communication over a network.

The CreatePipe function creates an anonymous pipe and returns two handles, one to the read end and one to the write end of the pipe. The read handle has only read access to the pipe, and the write handle has only write access to the pipe. To communicate through the pipe, a handle to one of the ends must be passed to another process. Usually, this is done through inheritance; that is, a child process inherits a handle from its parent process. The inheritability of these handles is controlled by the creating process in the following ways:

• The CreatePipe function specifies a SECURITY_ATTRIBUTES structure. If the bInheritHandle member of this structure is TRUE, the handles can be inherited.

• The DuplicateHandle function can change the inheritability of a pipe handle. This function enables a process to create a noninheritable duplicate of an inheritable pipe handle or to create an inheritable duplicate of a noninheritable pipe handle.

• The CreateProcess function enables a parent process to specify whether the new child process will inherit all or none of the parent's inheritable handles.

When a child process inherits a handle, the system enables the process to access the pipe referred to by that handle. However, the parent process must communicate the handle value to the child process. Typically, this is done by using the SetStdHandle and GetStdHandle functions, which set and retrieve the standard handles (standard input, standard output, or standard error) of a process. The parent process can specify a pipe handle in a call to SetStdHandle before the child process is created. The current standard handles of the parent process are inherited by the child process. So, when the child process starts up, it can use the GetStdHandle function to retrieve the handle value. The parent process can change its own standard handles without affecting the standard handles of its existing child processes.

To read from the pipe, a process uses the read handle in a call to the ReadFile function. Unless an error occurs, ReadFile does not return until the specified number of bytes has been read or until all handles to the write end of the pipe have been closed.

To write to the pipe, a process uses the write handle in a call to the WriteFile function. WriteFile does not return until the specified number of bytes has been written or an error occurs. If the pipe's buffer is full and bytes remain to be written, WriteFile does not return until some other process or thread reads from the pipe, making more buffer space available. CreatePipe enables the creating process to specify a buffer size for the pipe, or it can use the default buffer size.

Asynchronous (overlapped) read and write operations are not supported for anonymous pipes. This means that the ReadFileEx and WriteFileEx functions cannot be used with anonymous pipes. The lpOverLapped parameter of the ReadFile and WriteFile functions is ignored when used with an anonymous pipe.

An anonymous pipe exists until all handles to both read and write ends of the pipe are closed by the CloseHandle function.

+$#A Named Pipes

A named pipe is a one-way or two-way pipe for communicating between a server process and one or more client processes. A server process specifies a name when it calls the CreateNamedPipe function to create one or more instances of a named pipe. All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles and provides a separate conduit for client-server communication. When a client process specifies a pipe name in the CreateFile or CallNamedPipe function, it connects to an instance of the pipe. The use of instances enables multiple client processes to use the same named pipe simultaneously.

Subject to security checks, any client process can access a named pipe, making it easy to communicate between related or unrelated processes. Named pipes can be used locally to communicate between processes on the same machine or across a network to connect processes on different machines.

In the Win32 API, the difference between client and server processes is greatly diminished, because any process can act as both a server and a client, making peer-to-peer communication possible. As used here, server refers to a process that uses CreateNamedPipe to create a named pipe, and client refers to a process that uses CreateFile or CallNamedPipe to connect to an instance of a named pipe.

+$#A Pipe Names

Each named pipe must have a unique name that distinguishes it from other named pipes. Use the following form when specifying the name of a pipe in the CreateNamedPipe, CreateFile, WaitNamedPipe, or CallNamedPipe function.

\\servername\pipe\pipename

The server process cannot create a pipe on a computer, so CreateNamedPipe must use a period to indicate the local computer as shown in this example.

\\.\pipe\pipename

Client processes using CreateFile, WaitNamedPipe, or CallNamedPipe can use a period to specify a local pipe, but they must specify the name of a server to open a pipe on a remote computer.

The pipename part of the name can include any character, including numbers and special characters. The entire pipe name string can be up to 256 characters long. Pipe names are not case-sensitive.

+$#A Named Pipe Modes

A number of modes associated with a named pipe or with a handle to a named pipe affect the use and behavior of the pipe. Some of these modes can be set differently for each handle to a pipe, while others must be the same for all handles to a pipe.

The server process specifies the following modes when it calls the CreateNamedPipe function. The type and access modes must be the same for all instances of a pipe.

Mode | Meaning
Type | PIPE_TYPE_BYTE or PIPE_TYPE_MESSAGE. The type mode determines whether data is written to a named pipe as a stream of messages or as a stream of bytes. If no type is specified in CreateNamedPipe, the default is PIPE_TYPE_BYTE. A handle to a byte pipe must be in byte-read mode. A handle to a message pipe can be in either byte-read or message-read mode. For more information about pipe types, see Named Pipe Type and Read Mode.
Access | PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND, or PIPE_ACCESS_DUPLEX. This mode is equivalent to specifying the read or write access of the server's handles to the pipe, where inbound is equivalent to GENERIC_READ access, outbound is equivalent to GENERIC_WRITE access, and duplex is equivalent to GENERIC_READ | GENERIC_WRITE access. Client processes using the CreateFile function to connect to a named pipe must specify an access compatible with the access specified by the server. For example, a client must specify GENERIC_READ access for an outbound pipe.

The remaining pipe modes can differ for each pipe handle. This means that the client and server ends of the same pipe instance can use different modes. CreateNamedPipe sets all of these modes for the pipe handles of a server. CreateFile supports setting the overlapped and write-through modes for the pipe handles of a client. Both client and server processes use the SetNamedPipeHandleState function to set the read and wait modes of a pipe handle.

Mode | Meaning
Read | PIPE_READMODE_BYTE or PIPE_READMODE_MESSAGE. In byte-read mode, data is read from a named pipe as a stream of bytes. In message-read mode, data is read from a named pipe as a stream of messages. Only byte-read mode is allowed for byte-type pipes. Either byte-read or message-read mode is allowed for message-type pipes. The default is byte-type mode, if no read mode is specified in CreateNamedPipe. For more information about read modes, see Named Pipe Type and Read Mode.
Wait | PIPE_WAIT or PIPE_NOWAIT. In blocking-wait mode, the ReadFile, WriteFile, and ConnectNamedPipe functions wait indefinitely in some situations for a process on the other end of the pipe to perform an action. In nonblocking-wait mode, the functions return if they cannot finish their operations immediately. For more information about wait modes, see Blocking and Nonblocking Pipe Operations.
Overlapped | FILE_FLAG_OVERLAPPED. In overlapped mode, functions performing read, write, and connect operations that take a significant time to be completed can return immediately. This enables the thread to perform other operations while a time-consuming operation executes in the background. For more information about overlapped mode, see Synchronous and Overlapped Input and Output.
Write-through | FILE_FLAG_WRITE_THROUGH. In write-through mode, the functions that write to a named pipe do not return until the data written is transmitted across the network and into the pipe's buffer on the remote computer. This mode affects only write operations on byte-type pipes and, then, only when the client and server processes are on different computers. If write-through mode is not enabled, a write function returns successfully, even if the data is not transmitted. For more information about write-through modes, see Write-through to Remote Clients.

The initial read mode for a pipe handle is specified when a server process calls CreateNamedPipe. If PIPE_READMODE_BYTE is specified or if no read mode is specified, the pipe handle is created in byte-read mode. If both PIPE_READMODE_MESSAGE and PIPE_TYPE_MESSAGE are specified, the handle is created in message-read mode. For a client process, a pipe handle returned by CreateFile is always in byte-read mode initially. Both client and server processes can use the SetNamedPipeHandleState function to change the read mode of a pipe handle. For a message-type pipe, the read mode can be different for server and client handles to the same pipe instance.

+$#A Named Pipe Operations

The first time the server calls CreateNamedPipe, the function specifies the maximum number of instances of the pipe that can exist simultaneously. Limited by this maximum, the server can call CreateNamedPipe repeatedly to create additional instances of the pipe. If the function succeeds, each call returns a handle to the server end of a named pipe instance.

As soon as a pipe instance is created, a client process can connect to it by calling the CreateFile or CallNamedPipe function. If a pipe instance is available, CreateFile returns a handle to the client end of the pipe instance. If no instances of the pipe are available, a client process can use the WaitNamedPipe function to wait for one to become available.

A client process can also use CallNamedPipe to combine into a single operation the functions that connect to a pipe instance (waiting for one to be available, if necessary), write a message, read a message, and close its pipe handle. CallNamedPipe can be used only by a client process and only with a message-type pipe.

A server process specifies a handle to a pipe instance in a call to the ConnectNamedPipe function to determine when a client process is connected to it. If the pipe handle is in blocking mode, the ConnectNamedPipe operation does not return until a client is connected.

Client and server processes can call one of several functions ⎯ in addition to CallNamedPipe ⎯ to read from and write to a named pipe. The behavior of these functions depends on the type of pipe and the modes in effect for the specified pipe handle, as follows:

• The ReadFile and WriteFile functions can be used with either byte-type or message-type pipes.

• The ReadFileEx and WriteFileEx functions can be used with either byte-type or message-type pipes if the pipe handle was opened for overlapped operations.

• The PeekNamedPipe function can be used to read without removing the contents of either a byte-type pipe or a message-type pipe. PeekNamedPipe can also return additional information about the pipe instance.

• The TransactNamedPipe function can be used with message-type pipes if the pipe handle of the calling process is set to message-read mode. The function writes a request message and reads a reply message in a single operation, enhancing network performance.

For a pipe handle in byte-read mode, a read operation is completed successfully when all available bytes in the pipe are read or when the specified number of bytes is read.

For a pipe handle in message-read mode, a ReadFile or ReadFileEx operation is completed successfully only when the entire message is read. If the specified number of bytes to read is less than the size of the next message, the function reads as much of the message as possible before returning FALSE (with the GetLastError function returning ERROR_MORE_DATA). However, the remainder of the message can be read by making additional calls to ReadFile, ReadFileEx, or PeekNamedPipe. Note that PeekNamedPipe returns TRUE after reading a complete or partial message, and it reports the number of bytes remaining in a partially read message.

For a message-type pipe with multiple unread messages, a read operation using a message-read pipe handle returns after reading one message. An operation using a byte-read handle does not distinguish between the messages ⎯ it reads all available bytes up to the specified number.

When a client and server finish using a pipe instance, the server calls the DisconnectNamedPipe function to close the connection to the client process. This function makes the client's handle invalid (if it has not already been closed). Any unread data in the pipe is discarded. To ensure that all bytes or messages written to the pipe are read by the client, the server first calls the FlushFileBuffers function, which does not return until the client has read all data from the pipe. After the client is disconnected, the server calls the CloseHandle function to close its handle to the pipe instance. Alternatively, the server can use ConnectNamedPipe to enable a new client to connect to this instance of the pipe.

A process can retrieve information about a named pipe by calling the GetNamedPipeInfo function, which returns the type of the pipe, the size of the input and output buffers, and the maximum number of pipe instances that can be created. The GetNamedPipeHandleState function reports on the read and wait modes of a pipe handle, the current number of pipe instances, and additional relevant information for pipes that communicate over a network. The SetNamedPipeHandleState function sets the read mode and wait modes of a pipe handle. For client processes communicating with a remote server, the function also controls the maximum number of bytes to collect or the maximum time to wait before transmitting a message (assuming the client's handle was not opened with write-through mode enabled).

+$#A Named Pipe Server Process

The simplest server process can use the CreateNamedPipe function to create a single instance of a pipe, connect to a single client, communicate with the client, disconnect the pipe, close the pipe handle, and terminate. Typically, however, a server process must communicate with multiple client processes. A server process can use a single pipe instance by connecting to and disconnecting from each client in sequence, but performance would be poor. To handle multiple clients simultaneously, the server process must create multiple pipe instances.

The Win32 API supports three basic strategies for servicing multiple pipe instances. The server process can perform the following tasks:

• Create multiple threads (and/or processes) with a separate thread for each instance of the pipe. For an example of a multithreaded server process, see Multithreaded Server.

• Overlap operations by specifying an OVERLAPPED structure in the ReadFile, WriteFile, and ConnectNamedPipe functions. For an example of a server process that uses overlapped operations, see Server Using Overlapped Input and Output.

• Overlap operations by using the ReadFileEx and WriteFileEx functions, which specify a completion routine to be executed when the operation is complete. For an example of a server process that uses completion routines, see Server Using Completion Routines.

The multithreaded server strategy is easy to write, because the thread for each instance handles communications for only a single client. The system allocates processor time to each thread as needed. But each thread uses system resources, which is a potential disadvantage for a server that handles a large number of clients. Other complications occur if the actions of one client necessitate communications with other clients (as for a network game program, where a move by one player must be communicated to the other players).

With a single-threaded server, it is easier to coordinate operations that affect multiple clients, and it is easier to protect shared resources (for example, a database file) from simultaneous access by multiple clients. The challenge of a single-threaded server is that it requires coordination of overlapped operations in order to allocate processor time for handling the simultaneous needs of the clients.

+$#A Named Pipe Type and Read Mode

The type of a pipe determines how data is written to a named pipe, and the read mode determines how data is read from a named pipe. Data can be transmitted through a named pipe as either a stream of bytes or as a stream of messages. When using the CreateNamedPipe function to create an instance of a named pipe, the server process specifies the pipe's type, the type it must specify for all instances.

A byte-type pipe is created if PIPE_TYPE_BYTE is specified or, by default, if no type is specified. This means that data is written to the pipe as a stream of bytes, and the system does not differentiate between the bytes written in different write operations. A handle to a byte-type pipe can be in byte-read mode only.

A message-type pipe is created if PIPE_TYPE_MESSAGE is specified. This means that the system treats each write operation to the pipe as a message unit. A handle to a message-type pipe can be in either byte-read or message-read mode.

+$#A Blocking and Nonblocking Pipe Operations

The wait mode (blocking or nonblocking) of a pipe handle determines whether the ReadFile, WriteFile, and ConnectNamedPipe functions return immediately in situations that would otherwise require an indefinite wait. By default, all named pipe handles returned by the CreateNamedPipe or CreateFile function are created with blocking-wait mode enabled. Nonblocking-wait mode can be enabled for a server's pipe handle by specifying PIPE_NOWAIT in CreateNamedPipe. Both server and client processes can change a pipe handle's wait mode by specifying either PIPE_WAIT or PIPE_NOWAIT in a call to the SetNamedPipeHandleState function.

Note that nonblocking-wait mode is supported for compatibility with Microsoft® LAN Manager version 2.0, and it should not be used to achieve overlapped input and output (I/O) with named pipes. Overlapped I/O should be used instead because it enables time-consuming ReadFile, WriteFile, and ConnectNamedPipe operations to execute in the background after the function returns. For more information about overlapped I/O, see Synchronous and Overlapped Input and Output.

A ReadFile operation is affected by the wait mode of a pipe handle when the pipe is empty. With a blocking-wait handle, the operation is not completed successfully until data is available from a thread writing to the other end of the pipe. Using a nonblocking-wait handle, the function returns FALSE immediately, and the GetLastError function returns ERROR_NO_DATA.

A WriteFile operation is affected by a pipe handle's wait mode when there is insufficient space in the pipe's buffer to hold all the data. With a blocking-wait handle, the write operation cannot succeed until sufficient space is created in the buffer by a thread reading from the other end of the pipe. With a nonblocking-wait handle, the write operation returns TRUE immediately, without writing any bytes (for a message-type pipe) or after writing as many bytes as the buffer holds (for a byte-type pipe).

A ConnectNamedPipe operation is affected by the wait mode of a pipe handle when there is no client connected or waiting to connect to the pipe instance. With a blocking-wait handle, the connect operation does not succeed until a client process connects to the pipe instance by calling either the CreateFile or CallNamedPipe function. With a nonblocking-wait handle, the connect operation returns FALSE immediately, and the GetLastError function returns ERROR_PIPE_LISTENING.

The wait mode of a pipe handle does not affect read or write operations when using the TransactNamedPipe or CallNamedPipe function.

+$#A Synchronous and Overlapped Input and Output

The Win32 API supports both synchronous and asynchronous (overlapped) operations on named pipes. The ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe functions can run either synchronously or asynchronously. The ReadFileEx and WriteFileEx functions can run only asynchronously. When functions run synchronously, they do not return until the operation is finished. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. When functions run asynchronously, they return immediately even before their operations are finished. This enables a time-consuming operation to be executed in the background while the calling thread is free to perform other tasks. In the Win32 API, asynchronous execution is known as overlapped I/O.

Overlapped operations make it possible for one pipe to read and write data simultaneously and for a single thread to perform simultaneous I/O operations on multiple pipe handles. This enables a single-threaded server process to handle communications with multiple client processes efficiently. For examples that illustrate overlapped operations by a server process, see Server Using Overlapped Input and Output and Server Using Completion Routines.

For a server process to use synchronous operations to communicate with more than one client, it must create a separate thread for each client process so that one or more threads can run while other threads are waiting. For an example of a multithreaded server process that uses synchronous operations, see Multithreaded Server.

Overlapped operations on a named pipe are enabled by specifying the FILE_FLAG_OVERLAPPED flag when the handle is created (by the CreateNamedPipe function for a server process or the CreateFile function for a client process). If this flag is not specified, overlapped mode is disabled. The overlapped mode of a pipe handle cannot be changed after the pipe handle has been created. The overlapped mode can be different for server and client handles to the same pipe instance.

The ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe functions can be performed asynchronously only if overlapped mode is enabled for the specified pipe handle and a valid pointer to an OVERLAPPED structure is specified. If the OVERLAPPED pointer is NULL, the system attempts to make the operation look synchronous. Depending on the number of pending operations, this attempt may or may not be successful. Therefore, it is strongly recommended that if you create a handle with FILE_FLAG_OVERLAPPED and want asynchronous behavior, you should always specify a valid OVERLAPPED structure or use the functions intended for asynchronous operations such as ReadFileEx and WriteFileEx.

When ReadFile, WriteFile, TransactNamedPipe, and ConnectNamedPipe are performed asynchronously, one of the following outcomes occurs:

• If the operation is complete when the function returns, the return value indicates the success or failure of the operation. If an error occurs, the return value is FALSE and the GetLastError function returns something other than ERROR_IO_PENDING.

• If the operation has not finished when the function returns, the return value is FALSE and GetLastError returns ERROR_IO_PENDING. In this case, the calling thread must wait until the operation has finished. The calling thread must then call the GetOverlappedResult function to determine the results.

The hEvent member of the specified OVERLAPPED structure must contain a handle to a manual-reset ⎯ not an auto-reset ⎯ event object. This is a synchronization object created by the CreateEvent function. The thread that initiates the overlapped operation uses the event object to determine when the operation is finished. For more information about event objects, see Synchronization.

An event object is either in a signaled or nonsignaled state. If an overlapped function returns before its operation has finished, the system sets the state of the event object to nonsignaled. When the operation is finished, the system sets the state to signaled. The thread can detect the object's state by specifying a handle to the event object in a call to one of the wait functions: WaitForSingleObject, WaitForMultipleObjects, WaitForSingleObjectEx, or WaitForMultipleObjectsEx. These functions do not return until the state of a specified object is signaled or until a time-out interval has elapsed. For the wait functions that wait for multiple objects, the function's return value indicates which of the objects is signaled. Doing this enables a server process to use a loop that performs the following steps:

1. Specifies multiple event objects in a call to WaitForMultipleObjects or WaitForMultipleObjectsEx, and then waits for one of the objects to be in a signaled state.

2. Uses the wait function's return value to determine which overlapped operation is finished.

3. Performs the tasks necessary to clean up the completed operation and initiate the next operation for that pipe handle. This may involve starting another overlapped operation for the same pipe handle.

If the hEvent member of the OVERLAPPED structure is a NULL pointer instead of a handle to an event object, the calling thread can use the pipe handle to determine when an overlapped operation has finished. In this case, the system sets the state of the pipe handle to signaled or nonsignaled, as described previously for the state of an event object. It is strongly recommended that you always use an event object when performing overlapped operations rather than relying on the pipe handle. In particular, you should not use the pipe handle for this purpose when performing simultaneous operations on the same handle because there is no way of knowing which operation's completion caused the pipe handle to be signaled. The only safe technique for performing simultaneous operations on the same pipe handle is to use a separate OVERLAPPED structure with its own event object for each operation.

The ReadFileEx and WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped ReadFile and WriteFile functions, which use an event object to signal completion, the extended functions specify a completion routine. A completion routine is a function that is queued for execution when the read or write operation is finished. The completion routine is not executed until the thread that called ReadFileEx and WriteFileEx enters an alertable wait by calling one of the extended wait functions: WaitForSingleObjectEx, WaitForMultipleObjectsEx, or SleepEx. These functions are like the normal wait functions in that they return when a specified object is in the signaled state or when a time-out interval has elapsed. However, these functions can also perform an alertable wait that occurs when their fAlertable parameter is set to TRUE. In an alertable wait, the functions also return when a ReadFileEx or WriteFileEx completion routine is queued for execution. A server process can use the extended functions to perform a sequence of read and write operations for each client that connects to it. Each read or write operation in the sequence specifies a completion routine, and each completion routine initiates the next step in the sequence.

+$#A Write-through to Remote Clients

The write-through mode of a named pipe handle affects the behavior of byte-type pipes when data is transmitted across a network. To enable write-through mode for a named pipe handle, a process specifies the FILE_FLAG_WRITE_THROUGH flag in the CreateNamedPipe function for a server process or in the CreateFile function for a client process. If this flag is not specified, write-through mode is disabled. The write-through mode of a pipe handle cannot be changed after the pipe handle has been created. The write-through mode can be different for server and client handles to the same pipe instance.

If write-through mode is not enabled, the system enhances the efficiency of network operations by buffering data until a minimum number of bytes have accumulated or until a maximum time period has elapsed. Buffering enables multiple write operations to be combined into a single network transmission. This means that a write operation can be successfully completed after the data is in the outbound buffer but before it is transmitted across the network.

A client process can use the SetNamedPipeHandleState function to control the number of bytes and the time-out period before transmission for a pipe on which write-through mode is disabled.

If write-through mode is enabled, the transmission across the network is not delayed and the write operation is not completed until the data is in the pipe buffer on the remote machine. Write-through mode is useful for applications that require synchronization for every write operation.

The system always performs write operations on message-type pipes as if write-through mode were enabled.

+$#A Pipe Security

A server process uses the ImpersonateNamedPipeClient function to assume the security token of the client process connected to the specified pipe instance. This function can be useful in determining whether to grant the request of a client process. For example, a named pipe server can provide access to a database or file system to which the server process has privileged access. When a client process makes a request from the server, the client typically has some lesser level of security access. By assuming the security token of the client, the server can attempt to access the protected database. The system then grants or denies the server's access, based on the security level of the client. When the server is finished, it uses the RevertToSelf function to restore its original security token.

+$#A Using Pipes

Pipes are useful in applications that must pass a continuous stream of data between processes.

• Anonymous pipes overview

• Multithreaded server

• Server using overlapped input and output

• Server using completion routines

• Named pipe client process

• Transactions on named pipes

+$#A Anonymous Pipes Overview

A typical use of an anonymous pipe is to create a channel for communication between a parent process and its child process by redirecting the standard input or standard output handles of the child process. To redirect the standard output handle of a child process, the parent process performs the following steps:

1. Calls the GetStdHandle function to get the current standard output handle, which it saves for later use.

2. Calls the CreatePipe function to create an anonymous pipe. This function returns handles to the read and write ends of the pipe.

3. Calls the SetStdHandle function to set its standard output to be the write handle of the pipe.

4. Calls the CreateProcess function to create the child process. The child process inherits the inheritable handles of the parent process. It also inherits the values of the standard handles of its parent process, which it can retrieve using the GetStdHandle function.

5. Calls the CloseHandle function to close the parent's handle to the write end of the pipe. After the child process inherits this handle, the parent process no longer needs its copy of the handle.

6. Calls the ReadFile function to read from the pipe. This operation enables the parent process to read the data written to standard output by the child process.

The child process uses the GetStdHandle function to get its standard output handle, which is actually a handle to the write end of the pipe. The child process then uses the WriteFile function to write its output to the pipe.

Data is written to an anonymous pipe as a stream of bytes. This means that a process reading from a pipe cannot distinguish between the bytes written in separate write operations, unless both reading and writing processes use some protocol that lets the reading process know how many bytes to read. Typically, there is no protocol, so the reading process reads from the pipe until all write handles to the pipe are closed, which causes the ReadFile function to return FALSE. When a child process's standard output is redirected, the child process calls CloseHandle or terminates (which automatically closes the handle). Note that it is important for the parent process to close its handle to the write end of the pipe before trying to read from the pipe. Otherwise, its ReadFile operation cannot return FALSE because there is still an open handle to the write end of the pipe.

The procedure for redirecting standard input is similar to that for redirecting standard output, except that the pipe's read handle is used for the child's standard input. In this case, the parent process must ensure that the child process does not inherit the pipe's write handle. Otherwise, the ReadFile operation of the child process cannot return FALSE because the child process has an open handle to the write end of the pipe.

The parent process typically creates the read and write handles to the pipe so that they can be inherited by a child process. It does this by using CreatePipe, specifying a SECURITY_ATTRIBUTES structure with the bInheritHandle member set to TRUE. When a child's standard input is redirected, the child process should not inherit the pipe's write handle. The parent process prevents inheritance by using the DuplicateHandle function to create a noninheritable duplicate of the handle and then using CloseHandle to close the inheritable handle.

For an example program that uses anonymous pipes to redirect the standard handles of a child process, see Processes and Threads.

+$#A Multithreaded Server

This example of a multithreaded server process has a main thread that loops continuously, creating a pipe instance and waiting for a client process to connect. When a client process connects, the server process creates a thread to service that client and the loop starts over. It is possible for a client process to connect successfully to the pipe instance in the interval between calls to the CreateNamedPipe and ConnectNamedPipe functions. If this happens, ConnectNamedPipe returns FALSE, and GetLastError returns ERROR_PIPE_CONNECTED.

The thread created to service each pipe instance reads requests from the pipe and writes replies to the pipe until the client process closes its handle. When this happens, the thread flushes the pipe, disconnects, closes its pipe handle, and terminates.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <windows.h>

VOID InstanceThread(LPVOID);

VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int xx = 0;

DWORD main(VOID) {

BOOL fConnected;

DWORD dwThreadId;

HANDLE hPipe, hThread;

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*

* The main loop creates an instance of the named pipe and

* then waits for a client to connect to it. When the client

* connects, a thread is created to handle communications

* with that client, and the loop is repeated.

*/

for (;;) {

hPipe = CreateNamedPipe(

lpszPipename, /* pipe name */

PIPE_ACCESS_DUPLEX, /* read/write access */

PIPE_TYPE_MESSAGE | /* message type pipe */

PIPE_READMODE_MESSAGE | /* message-read mode */

PIPE_WAIT, /* blocking mode */

PIPE_UNLIMITED_INSTANCES, /* max. instances */

BUFSIZE, /* output buffer size */

BUFSIZE, /* input buffer size */

PIPE_TIMEOUT, /* client time-out */

NULL); /* no security attr. */

if (hPipe == INVALID_HANDLE_VALUE)

MyErrExit("CreatePipe");

/*

* Wait for the client to connect; if it succeeds,

* the function returns TRUE. If the function returns FALSE,

* GetLastError returns ERROR_PIPE_CONNECTED.

*/

fConnected = ConnectNamedPipe(hPipe, NULL) ?

TRUE :

(GetLastError() == ERROR_PIPE_CONNECTED);

if (fConnected) {

/* Create a thread for this client. */

hThread = CreateThread(

NULL, /* no security attr. */

0, /* default stack size */

(LPTHREAD_START_ROUTINE) InstanceThread,

(LPVOID) hPipe, /* thread parameter */

0, /* not suspended */

&dwThreadId); /* returns thread ID */

if (hThread == INVALID_HANDLE_VALUE)

MyErrExit("CreateThread");

} else

/* The client could not connect, so close the pipe. */

CloseHandle(hPipe);

}

return 1;

}

VOID InstanceThread(LPVOID lpvParam) {

CHAR chRequest[BUFSIZE];

CHAR chReply[BUFSIZE];

DWORD cbBytesRead, cbReplyBytes, cbWritten;

BOOL fSuccess;

HANDLE hPipe;

/* The thread's parameter is a handle to a pipe instance. */

hPipe = (HANDLE) lpvParam;

while (1) {

/* Read client requests from the pipe. */

fSuccess = ReadFile(

hPipe, /* handle to pipe */

chRequest, /* buffer to receive data */

BUFSIZE, /* size of buffer */

&cbBytesRead, /* number of bytes read */

NULL); /* not overlapped I/O */

if (! fSuccess || cbBytesRead == 0)

break;

GetAnswerToRequest(chRequest, chReply, &cbReplyBytes);

/* Write the reply to the pipe. */

fSuccess = WriteFile(

hPipe, /* handle to pipe */

chReply, /* buffer to write from */

cbReplyBytes, /* number of bytes to write */

&cbWritten, /* number of bytes written */

NULL); /* not overlapped I/O */

if (! fSuccess || cbReplyBytes != cbWritten) break;

}

/*

* Flush the pipe to allow the client to read the pipe's contents

* before disconnecting. Then disconnect the pipe, and close the

* handle to this pipe instance.

*/

FlushFileBuffers(hPipe);

DisconnectNamedPipe(hPipe);

CloseHandle(hPipe);

}

+$#A Server Using Overlapped Input and Output

This example is a single-threaded server process that uses overlapped operations to service simultaneous connections to multiple client processes. The server process creates a fixed number of pipe instances, each of which can be connected to a separate client process. When a client process has finished using its pipe instance, the server disconnects from the client and reuses the pipe instance to connect to a new client.

Associated with each pipe instance is an OVERLAPPED structure containing an event object. This structure is specified as a parameter in each ReadFile, WriteFile, and ConnectNamedPipe operation on the pipe instance. Although the example shows simultaneous operations on different pipe instances, it avoids simultaneous operations on a single pipe instance. Because the same event object is used for read, write, and connect operations for each instance, there is no way to know which operation's completion caused the event to be set to the signaled state for simultaneous operations using the same pipe instance.

The event handles for each pipe instance are also stored in an array used by the WaitForMultipleObjects function. This function waits for one of the events to be signaled, and its return value is the array index of the event that satisfied the wait. The example uses this index to retrieve a structure containing information for the pipe instance. The server uses the fPendingIO member of the structure to keep track of whether the most recent I/O operation on the instance was pending, necessitating a call to the GetOverlappedResult function. It uses the dwState member of the structure to determine the next operation that must be performed for the instance.

Overlapped ReadFile, WriteFile, and ConnectNamedPipe operations may have finished when the function returns, or they may still be pending when the function returns. If the operation is pending, the event object in the specified OVERLAPPED structure is set to the nonsignaled state before the function returns. When the pending operation has finished, the system sets the state of the event object to signaled. The state of the event object is not changed if the operation finishes before the function returns.

Because the example uses manual reset event objects, the state of the event objects is not changed to nonsignaled by the WaitForMultipleObjects function. This is important, because the example relies on the event objects remaining in the signaled state except when there is a pending operation.

If the operation is already finished when ReadFile, WriteFile, or ConnectNamedPipe returns, the function's return value indicates the result. For read and write operations, the number of bytes transferred is also returned. If the operation is still pending, the ReadFile, WriteFile, or ConnectNamedPipe function returns FALSE and the GetLastError function returns ERROR_IO_PENDING. In this case, the results are retrieved using the GetOverlappedResult function after the operation has finished. GetOverlappedResult returns only the results of operations that were pending, and does not report the results of operations that were completed before the overlapped ReadFile, WriteFile, or ConnectNamedPipe function returned.

Before disconnecting from a client, the multithreaded server example in the previous section used FlushFileBuffers to ensure that the client had read everything written to the pipe. This would defeat the purpose of overlapped I/O, because the flush operation would block the execution of the server thread while it waits for the client to empty the pipe. Consequently, it is necessary to wait for a signal from the client that it has finished before disconnecting. In this example, the signal is the error generated by trying to read from the pipe after the client process closes its handle.

#include <windows.h>

#define CONNECTING_STATE 0

#define READING_STATE 1

#define WRITING_STATE 2

#define INSTANCES 4

typedef struct {

OVERLAPPED oOverlap;

HANDLE hPipeInst;

CHAR chBuf[BUFSIZE];

DWORD cbToWrite;

DWORD dwState;

BOOL fPendingIO;

} PIPEINST, *LPPIPEINST;

VOID DisconnectAndReconnect(DWORD);

BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);

VOID GetDataToWriteToClient(LPPIPEINST);

PIPEINST Pipe[INSTANCES];

HANDLE hEvents[INSTANCES];

DWORD main(VOID) {

DWORD i, dwWait, cbBytes, dwErr;

BOOL fSuccess;

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/*

* The initial loop creates several instances of a named pipe

* along with an event object for each instance. An

* overlapped ConnectNamedPipe operation is started for

* each instance.

*/

for (i = 0; i < INSTANCES; i++) {

/* Create an event object for this instance. */

hEvents[i] = CreateEvent(

NULL, /* no security attr. */

TRUE, /* manual-reset event */

TRUE, /* initial state = signaled */

NULL); /* unnamed event object */

if (hEvents[i] == NULL)

MyErrExit("CreateEvent");

Pipe[i].oOverlap.hEvent = hEvents[i];

Pipe[i].hPipeInst = CreateNamedPipe(

lpszPipename, /* pipe name */

PIPE_ACCESS_DUPLEX | /* read/write access */

FILE_FLAG_OVERLAPPED, /* overlapped mode */

PIPE_TYPE_MESSAGE | /* message-type pipe */

PIPE_READMODE_MESSAGE | /* message-read mode */

PIPE_WAIT, /* blocking mode */

INSTANCES, /* number of instances */

BUFSIZE, /* output buffer size */

BUFSIZE, /* input buffer size */

PIPE_TIMEOUT, /* client time-out */

NULL); /* no security attr. */

if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE)

MyErrExit("CreatePipe");

/* Call the subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(

Pipe[i].hPipeInst,

&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?

CONNECTING_STATE : /* still connecting */

READING_STATE; /* ready to read */

}

while (1) {

/*

* Wait for the event object to be signaled, indicating

* completion of an overlapped read, write, or

* connect operation.

*/

dwWait = WaitForMultipleObjects(

INSTANCES, /* number of event objects */

hEvents, /* array of event objects */

FALSE, /* does not wait for all */

INFINITE); /* waits indefinitely */

/* dwWait shows which pipe completed the operation. */

i = dwWait - WAIT_OBJECT_0; /* determines which pipe */

if (i < 0 || i > (INSTANCES - 1))

MyErrExit("index out of range");

/* Get the result if the operation was pending. */

if (Pipe[i].fPendingIO) {

fSuccess = GetOverlappedResult(

Pipe[i].hPipeInst, /* handle to pipe */

&Pipe[i].oOverlap, /* OVERLAPPED structure */

&cbBytes, /* bytes transferred */

FALSE); /* do not wait */

switch (Pipe[i].dwState) {

/* Pending connect operation */

case CONNECTING_STATE:

if (! fSuccess)

MyErrExit("ConnectNamedPipe");

Pipe[i].dwState = READING_STATE;

break;

/* Pending read operation */

case READING_STATE:

if (! fSuccess || cbBytes == 0) {

DisconnectAndReconnect(i);

continue;

}

Pipe[i].dwState = WRITING_STATE;

break;

/* Pending write operation */

case WRITING_STATE:

if (! fSuccess ||

cbBytes != Pipe[i].cbToWrite) {

DisconnectAndReconnect(i);

continue;

}

Pipe[i].dwState = READING_STATE;

break;

default:

MyErrExit("invalid pipe state");

}

}

/* The pipe state determines which operation to do next. */

switch (Pipe[i].dwState) {

/*

* READING_STATE:

* The pipe instance is connected to the client

* and is ready to read a request from the client.

*/

case READING_STATE:

fSuccess = ReadFile(

Pipe[i].hPipeInst,

Pipe[i].chBuf,

BUFSIZE,

&cbBytes,

&Pipe[i].oOverlap);

/* The read operation completed successfully. */

if (fSuccess && cbBytes != 0) {

Pipe[i].fPendingIO = FALSE;

Pipe[i].dwState = WRITING_STATE;

continue;

}

/* The read operation is still pending */

dwErr = GetLastError();

if (! fSuccess &&

(dwErr == ERROR_IO_PENDING)) {

Pipe[i].fPendingIO = TRUE;

continue;

}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);

break;

/*

* WRITING_STATE:

* The request was successfully read from the client.

* Get the reply data and write it to the client.

*/

case WRITING_STATE:

GetDataToWriteToClient(&Pipe[i]);

fSuccess = WriteFile(

Pipe[i].hPipeInst,

Pipe[i].chBuf,

Pipe[i].cbToWrite,

&cbBytes,

&Pipe[i].oOverlap);

/* The write operation completed successfully. */

if (fSuccess &&

cbBytes == Pipe[i].cbToWrite) {

Pipe[i].fPendingIO = FALSE;

Pipe[i].dwState = READING_STATE;

continue;

}

/* The write operation is still pending. */

dwErr = GetLastError();

if (! fSuccess &&

(dwErr == ERROR_IO_PENDING)) {

Pipe[i].fPendingIO = TRUE;

continue;

}

/* An error occurred; disconnect from the client. */

DisconnectAndReconnect(i);

break;

default:

MyErrExit("invalid pipe state");

}

}

return 0;

}

/*

* DisconnectAndReconnect(DWORD)

* This function is called when an error occurs or when the client

* closes its handle to the pipe. Disconnect from this client, and then

* call ConnectNamedPipe to wait for another client to connect.

*/

VOID DisconnectAndReconnect(DWORD i) {

/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(Pipe[i].hPipeInst) )

MyErrExit("DisconnectNamedPipe");

/* Call a subroutine to connect to the new client. */

Pipe[i].fPendingIO = ConnectToNewClient(

Pipe[i].hPipeInst,

&Pipe[i].oOverlap);

Pipe[i].dwState = Pipe[i].fPendingIO ?

CONNECTING_STATE : /* still connecting */

READING_STATE; /* ready to read */

}

/*

* ConnectToNewClient(HANDLE, LPOVERLAPPED)

* This function is called to start an overlapped connect operation. It

* returns TRUE if an operation is pending or FALSE if the connection

* has been completed.

*/

BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) {

BOOL fConnected, fPendingIO = FALSE;

/* Start an overlapped connection for this pipe instance. */

fConnected = ConnectNamedPipe(hPipe, lpo);

/* Overlapped ConnectNamedPipe should return FALSE. */

if (fConnected)

MyErrExit("ConnectNamedPipe");

switch (GetLastError()) {

/* The overlapped connection in progress. */

case ERROR_IO_PENDING:

fPendingIO = TRUE;

break;

/* Client is already connected, so signal an event. */

case ERROR_PIPE_CONNECTED:

if (SetEvent(lpo->hEvent))

break;

/* If an error occurs during the connect operation... */

default:

MyErrExit("ConnectNamedPipe");

}

return fPendingIO;

}

+$#A Server Using Completion Routines

The ReadFileEx and WriteFileEx functions provide another form of overlapped I/O. Unlike the overlapped ReadFile and WriteFile functions, which use an event object to signal completion, the extended functions specify a completion routine. A completion routine is a function that is queued for execution when the read or write operation is done. The completion routine is not executed until the thread that called ReadFileEx and WriteFileEx enters an alertable wait by calling one of the extended wait functions: WaitForSingleObjectEx, WaitForMultipleObjectsEx, or SleepEx. These functions are like the other wait functions in that they return when a specified object is in the signaled state or when a time-out interval has elapsed. However, these functions can also perform an alertable wait, which occurs when their fAlertable parameter is set to TRUE. In an alertable wait, the functions also return when a ReadFileEx or WriteFileEx completion routine is queued for execution. A server process can use the extended functions to perform a sequence of read and write operations for each client that connects to it. Each read or write operation in the sequence specifies a completion routine, and each completion routine initiates the next step in the sequence.

Like the previous example, this example is a single-threaded server process that creates a message-type pipe and uses overlapped operations. The server process differs in that it uses the extended functions ReadFileEx and WriteFileEx to perform overlapped I/O. Unlike the overlapped ReadFile and WriteFile functions, which signal an event object upon completion, the extended functions specify a completion routine, which is queued for execution when the operation is finished. The server process uses the WaitForSingleObjectEx function, which performs an alertable wait that returns when a completion routine is ready to execute. The wait function also returns when an event object is signaled, which in this example indicates that the overlapped ConnectNamedPipe operation has finished (a new client has connected).

Initially, the server process creates a single instance of the pipe and starts an overlapped ConnectNamedPipe operation. When a client connects, the server allocates a structure to provide storage for that pipe instance and then calls the ReadFileEx function to launch a sequence of I/O operations to handle communications with the client. Each operation specifies a completion routine that performs the next operation in the sequence. The sequence terminates when the client is disconnected and the pipe instance closed. After starting the sequence of operations for the new client, the server creates another pipe instance and waits for the next client to connect.

The parameters of the ReadFileEx and WriteFileEx functions specify a completion routine and a pointer to an OVERLAPPED structure. This pointer is later passed to the completion routine in its lpOverLap parameter. Because the OVERLAPPED structure points to the first member in the structure allocated for each pipe instance, the completion routine can use its lpOverLap parameter to access the structure for the pipe instance.

To avoid duplication, the listing for the ConnectToNewClient subroutine is not shown; it is identical to the one used by the overlapped server process in the previous section.

#include <windows.h>

typedef struct {

OVERLAPPED oOverlap;

HANDLE hPipeInst;

CHAR chBuf[BUFSIZE];

DWORD cbToWrite;

} PIPEINST, *LPPIPEINST;

BOOL CreateAndConnectInstance();

BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);

VOID GetDataToWriteToClient(LPPIPEINST);

VOID DisconnectAndClose(LPPIPEINST);

VOID CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);

VOID CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);

HANDLE hPipe;

DWORD main(VOID) {

HANDLE hConnectEvent;

OVERLAPPED oConnect;

LPPIPEINST lpPipeInst;

DWORD dwWait, cbBytes;

BOOL fSuccess, fPendingIO;

/* Create one event object for the connect operation. */

hConnectEvent = CreateEvent(

NULL, /* no security attr. */

TRUE, /* manual reset event */

TRUE, /* initial state = signaled */

NULL); /* unnamed event object */

if (hConnectEvent == NULL)

MyErrExit("CreateEvent");

oConnect.hEvent = hConnectEvent;

/*

* Call a subroutine to create one instance, and wait for

* the client to connect.

*/

fPendingIO = CreateAndConnectInstance(&oConnect);

while (1) {

/*

* Wait for a client to connect, or for a read or write

* operation to be completed, which causes a completion

* routine to be queued for execution.

*/

dwWait = WaitForSingleObjectEx(

hConnectEvent, /* event object to wait for */

INFINITE, /* waits indefinitely */

TRUE); /* alertable wait enabled */

switch (dwWait) {

/* The wait is satisfied by a completed connect operation. */

case 0:

/* If an operation is pending, get the result of the

* connect operation.

*/

if (fPendingIO) {

fSuccess = GetOverlappedResult(

hPipe, /* pipe handle */

&oConnect, /* OVERLAPPED structure */

&cbBytes, /* bytes transferred */

FALSE); /* does not wait */

if (! fSuccess)

MyErrExit("ConnectNamedPipe");

}

/* Allocate storage for this instance. */

lpPipeInst = (LPPIPEINST) GlobalAlloc(

GPTR, sizeof(PIPEINST));

if (lpPipeInst == NULL)

MyErrExit("GlobalAlloc lpPipeInst");

lpPipeInst->hPipeInst = hPipe;

/*

* Start the read operation for this client.

* Note that this same routine is later used as a

* completion routine after a write operation.

*/

lpPipeInst->cbToWrite = 0;

CompletedWriteRoutine(0, 0,

(LPOVERLAPPED) lpPipeInst);

/* Create new pipe instance for the next client. */

fPendingIO = CreateAndConnectInstance(

&oConnect);

break;

/*

* The wait is satisfied by a completed read or write

* operation. This allows the system to execute the

* completion routine.

*/

case WAIT_IO_COMPLETION:

break;

/* An error occurred in the wait function. */

default:

MyErrExit("WaitForSingleObjectEx");

}

}

return 0;

}

/*

* CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)

* This routine is called as an I/O completion routine after writing to

* the pipe, or when a new client has connected to a pipe instance. It

* starts another read operation.

*/

VOID CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,

LPOVERLAPPED lpOverLap) {

LPPIPEINST lpPipeInst;

BOOL fRead = FALSE;

/* lpOverlap points to storage for this instance. */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*

* The write operation has finished, so read the next request (if

* there is no error).

*/

if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))

fRead = ReadFileEx(

lpPipeInst->hPipeInst,

lpPipeInst->chBuf,

BUFSIZE,

(LPOVERLAPPED) lpPipeInst,

(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);

/* Disconnect if an error occurred. */

if (! fRead)

DisconnectAndClose(lpPipeInst);

}

/*

* CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)

* This routine is called as an I/O completion routine after reading a

* request from the client. It gets data and writes it to the pipe.

*/

VOID CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,

LPOVERLAPPED lpOverLap) {

LPPIPEINST lpPipeInst;

BOOL fWrite = FALSE;

/* lpOverlap points to storage for this instance. */

lpPipeInst = (LPPIPEINST) lpOverLap;

/*

* The read operation has finished, so write a response (if no

* error occurred).

*/

if ((dwErr == 0) && (cbBytesRead != 0)) {

GetDataToWriteToClient(lpPipeInst);

fWrite = WriteFileEx(

lpPipeInst->hPipeInst,

lpPipeInst->chBuf,

lpPipeInst->cbToWrite,

(LPOVERLAPPED) lpPipeInst,

(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);

}

/* Disconnect if an error occurred. */

if (! fWrite)

DisconnectAndClose(lpPipeInst);

}

/*

* DisconnectAndClose(LPPIPEINST)

* This routine is called when an error occurs or when the client closes

* its handle to the pipe.

*/

VOID DisconnectAndClose(LPPIPEINST lpPipeInst)

{

/* Disconnect the pipe instance. */

if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )

MyErrExit("DisconnectNamedPipe");

/* Close the handle to the pipe instance. */

CloseHandle(lpPipeInst->hPipeInst);

/* Release the storage for the pipe instance. */

if (lpPipeInst != NULL)

GlobalFree(lpPipeInst);

}

/*

* CreateAndConnectInstance(LPOVERLAPPED)

* This function creates a pipe instance and connects to the new client.

* It returns TRUE if the connect operation is pending and FALSE if

* the connection has been completed.

*/

BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) {

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

hPipe = CreateNamedPipe(

lpszPipename, /* pipe name */

PIPE_ACCESS_DUPLEX | /* read/write access */

FILE_FLAG_OVERLAPPED, /* overlapped mode */

PIPE_TYPE_MESSAGE | /* message-type pipe */

PIPE_READMODE_MESSAGE | /* message read mode */

PIPE_WAIT, /* blocking mode */

PIPE_UNLIMITED_INSTANCES, /* unlimited instances */

BUFSIZE, /* output buffer size */

BUFSIZE, /* input buffer size */

PIPE_TIMEOUT, /* client time-out */

NULL); /* no security attr. */

if (hPipe == INVALID_HANDLE_VALUE)

MyErrExit("CreatePipe");

/* Call a subroutine to connect to the new client. */

return ConnectToNewClient(hPipe, lpoOverlap);

}

+$#A Named Pipe Client Process

A client process uses the CreateFile function to open a handle to a named pipe. If the pipe exists but all of its instances are busy, CreateFile returns FALSE and the GetLastError function returns ERROR_PIPE_BUSY. When this happens, the client process uses the WaitNamedPipe function to wait for an instance of the pipe to be available.

CreateFile fails if the access specified is incompatible with the access specified (duplex, outbound, or inbound) when the server created the pipe. For a duplex pipe (read/write), the client can specify read, write, or read/write access; for an outbound pipe (server write only), the client must specify read only; and for an inbound pipe (server read only), the client must specify write only.

The handle returned by CreateFile defaults to byte-read mode, blocking-wait mode, overlapped mode disabled, and write-through mode disabled. The client process can use CreateFile to enable overlapped mode by specifying FILE_FLAG_OVERLAPPED or to enable write-through mode by specifying FILE_FLAG_WRITE_THROUGH. The client can use the SetNamedPipeHandleState function to enable nonblocking mode by specifying PIPE_WAIT or to enable message-read mode by specifying PIPE_READMODE_MESSAGE.

The following example shows a client process that opens a named pipe, sets the pipe handle to message-read mode, uses WriteFile to send a request to the server, and uses ReadFile to read the server's reply. This client process can be used with any of the message-type servers shown in the previous sections. With a byte-type server, however, this client process fails when it calls SetNamedPipeHandleState to change to message-read mode. Because the client is reading from the pipe in message-read mode, it is possible for the ReadFile operation to return FALSE after reading a partial message. This happens when the message is larger than the read buffer. In this situation, GetLastError returns ERROR_MORE_DATA, and the remainder of the message is read by additional calls to ReadFile.

#include <windows.h>

DWORD main(int argc, char *argv[]) {

HANDLE hPipe;

LPVOID lpvMessage;

CHAR chBuf[512];

BOOL fSuccess;

DWORD cbRead, cbWritten, dwMode;

LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

/* Try to open a named pipe; wait for it, if necessary. */

while (1) {

hPipe = CreateFile(

lpszPipename, /* pipe name */

GENERIC_READ | /* read/write access */

GENERIC_WRITE,

0, /* no sharing */

NULL, /* no security attr. */

OPEN_EXISTING, /* opens existing pipe */

0, /* default attributes */

NULL); /* no template file */

/* Break if the pipe handle is valid. */

if (hPipe != INVALID_HANDLE_VALUE)

break;

/* Exit if an error other than ERROR_PIPE_BUSY occurs. */

if (GetLastError() != ERROR_PIPE_BUSY)

MyErrExit("Could not open pipe");

/* All pipe instances are busy, so wait for 20 seconds. */

if (! WaitNamedPipe(lpszPipename, 20000) )

MyErrExit("Could not open pipe");

}

/* The pipe connected; change to message-read mode. */

dwMode = PIPE_READMODE_MESSAGE;

fSuccess = SetNamedPipeHandleState(

hPipe, /* pipe handle */

&dwMode, /* new pipe mode */

NULL, /* don't set max. bytes */

NULL); /* don't set max. time */

if (! fSuccess)

MyErrExit("SetNamedPipeHandleState");

/* Send a message to the pipe server. */

lpvMessage = (argc > 1) ? argv[1] : "default message";

fSuccess = WriteFile(

hPipe, /* pipe handle */

lpvMessage, /* message */

strlen(lpvMessage) + 1, /* message length */

&cbWritten, /* bytes written */

NULL); /* not overlapped */

if (! fSuccess)

MyErrExit("WriteFile");

do {

/* Read from the pipe. */

fSuccess = ReadFile(

hPipe, /* pipe handle */

chBuf, /* buffer to receive reply */

512, /* size of buffer */

&cbRead, /* number of bytes read */

NULL); /* not overlapped */

if (! fSuccess && GetLastError() != ERROR_MORE_DATA)

break;

/* Reply from the pipe is written to STDOUT. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),

chBuf, cbRead, &cbWritten, NULL))

break;

} while (! fSuccess); /* repeat loop if ERROR_MORE_DATA */

CloseHandle(hPipe);

return 0;

}

+$#A Transactions on Named Pipes

A named pipe transaction is a client-server communication that combines a write operation and a read operation into a single network operation. A transaction can be used only on a duplex, message-type pipe. Transactions improve the performance of network communications between a client and a remote server. Processes can use the TransactNamedPipe and CallNamedPipe functions to perform named pipe transactions.

TransactNamedPipe is typically used by a client process to write a request message to the named pipe server and read the server's response message. The client process must specify GENERIC_READ | GENERIC_WRITE access when it opens its pipe handle by calling the CreateFile function. Then, the client process sets the pipe handle to message-read mode by calling the SetNamedPipeHandleState function. If the read buffer specified in the call to TransactNamedPipe is not large enough to hold the entire message written by the server, the function returns FALSE and GetLastError returns ERROR_MORE_DATA. The client can read the remainder of the message by calling either the ReadFile, ReadFileEx, or PeekNamedPipe function.

TransactNamedPipe is typically called by client processes, but can also be used by a server process.

The following example shows a client process using TransactNamedPipe. The example assumes that the client process has used CreateFile to connect to the pipe and SetNamedPipeHandleState to set the pipe handle's read mode, as shown in the client process example in the preceding section.

fSuccess = TransactNamedPipe(

hPipe, /* pipe handle */

lpszWrite, /* message to server */

strlen(lpszWrite)+1, /* message length */

chReadBuf, /* buffer to receive reply */

512, /* size of read buffer */

&cbRead, /* number of bytes read */

NULL); /* not overlapped */

/*

* Exit if an error occurs, unless the error indicates there is more

* data in the message.

*/

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))

MyErrExit("TransactNamedPipe");

while(1) {

/* Data from the pipe is written to STDOUT. */

if (! WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),

chReadBuf, cbRead, &cbWritten, NULL) )

break;

/* Break if TransactNamedPipe or ReadFile is successful. */

if (fSuccess)

break;

/* Read from the pipe if there is more data in the message.*/

fSuccess = ReadFile(

hPipe, /* pipe handle */

chReadBuf, /* buffer to receive reply */

512, /* size of buffer */

&cbRead, /* number of bytes read */

NULL); /* not overlapped */

/* Exit if an error other than ERROR_MORE_DATA occurs. */

if (! fSuccess && (GetLastError() != ERROR_MORE_DATA))

break;

}

A client process uses CallNamedPipe to combine the CreateFile, WaitNamedPipe (if necessary), TransactNamedPipe, and CloseHandle operations into a single function. Because the pipe handle is closed before the function returns, any additional bytes in the message are lost if the message is larger than the speciffied size of the read buffer. The following example shows the use of CallNamedPipe.

/* Combines connect, wait, write, read, and close operations. */

fSuccess = CallNamedPipe(

lpszPipename, /* pipe name */

lpszWrite, /* message to server */

strlen(lpszWrite)+1, /* message length */

chReadBuf, /* buffer to receive reply */

512, /* size of read buffer */

&cbRead, /* number of bytes read */

20000); /* waits for 20 seconds */

if (fSuccess || GetLastError() == ERROR_MORE_DATA) {

/* Data from the pipe is written to STDOUT. */

WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),

chReadBuf, cbRead, &cbWritten, NULL);

/*

* The pipe is closed, so no more bytes can be read from the

* message.

*/

if (! fSuccess)

printf("\n...extra data in message was lost\n");

}

+$#A Pipe Functions

Following are the functions used with pipes.

CallNamedPipe
ConnectNamedPipe
CreateNamedPipe
CreatePipe
DisconnectNamedPipe
GetNamedPipeHandleState
GetNamedPipeInfo
PeekNamedPipe
SetNamedPipeHandleState
TransactNamedPipe
WaitNamedPipe

Créé avec HelpNDoc Personal Edition: Générateur gratuit de livres électroniques et documentation


Power Management.rtf

+$#AK Power Management

This overview describes the power management functions and messages, and explains how to use them in applications and installable drivers.

+$#A About Power Management

The Windows operating system uses power management to reduce the power consumption of a computer and the attached devices. Windows works in conjunction with the computer and its devices to monitor system operation and shut down devices or the computer itself when idle. To help applications and installable drivers take advantage of power management and to ensure that no data is lost when a device or the computer is shut down, Windows provides functions and messages that reveal the system power status and notify of power management events.

+$#A About System Power Status

System power status indicates the source of power for a computer, that is, whether it uses a system battery or AC power. For computers that use batteries, the status also indicates how much battery life remains and whether the battery is currently charging.

You can retrieve the system power status by using the GetSystemPowerStatus function. This copies information about the power supply and battery status to a SYSTEM_POWER_STATUS structure. This structure identifies whether the system has a battery, and if it does, whether the battery is being used and the percentage of charge remaining.

Applications and installable drivers typically use the system power status to make decisions about whether to continue operation. For example, an application that is beginning a lengthy operation may check the status to determine whether enough battery power exists to complete the operation.

If power is low, an application can request user intervention or request that the system to shut itself down. You can suspend the operation of the system by using the SetSystemPowerState function. This generates a power management event that, if approved by all applications and drivers in the system, shuts the system down until the user restores power. Even if a computer uses AC power, an application that determines that the system is idle and can request that the system shut itself down to save power.

+$#A Power Management Events

A power management event is a change in either the system power status or the operational mode of a device or the computer. Because these events can affect the operation of applications and installable drivers and can lead to loss of data, Windows notifies all applications and installable drivers by broadcasting a WM_POWERBROADCAST message for each event.

A system power status event occurs when there is a change in the power supply or in the system battery status. For example, the system broadcasts a message whenever the user switches from battery to AC power or vice versa. The system also broadcasts a message when remaining battery power slips below 10%.

An operational mode event occurs when there is a change in power consumption, such as the system shutting itself down or the user turning the system back on. Some changes in operational mode can cause loss of data if applications and drivers do not prepare for them. Therefore, the system broadcasts messages about these changes before they actually occur. For example, if the system determines that it is idle, it broadcasts a message notifying applications and drivers that it is about to suspend operation and shut itself down to save power. The applications and drivers can prepare for this suspension by closing files and saving data that may otherwise be lost when power is shut off.

The system has three ways to shut itself down. It can suspend operation, place operation on standby, or carry out critical suspension. An application can also shut the system down by using the SetSystemPowerState function to suspend operation.

When system operation is suspended, power to all devices is lost. The WM_POWERBROADCAST messages, sent immediately before suspension, allow device drivers to save the state of the device. For many devices (but not all), saving the state means the driver can restore the state when power is eventually restored, allowing the device to continue operation as if power was never lost.

When the system goes into standby, power to devices may be reduced but no state is lost and interrupts continue to be processed. For example, a hard disk drive on standby may stop spinning but state information is preserved and the drive responds to requests for access. Standby operation is transparent to applications because the corresponding WM_POWERBROADCAST messages are never sent to them. Instead only device drivers receive and process these messages.

When the system carries out a critical suspension, battery power is too low to let the computer continue to run. Unlike for suspended and standby operations, the system does not notify applications and drivers before carrying out a critical suspension. This means data may be lost.

When system operation is restored after having been suspended or placed on standby, the system notifies all applications and drivers. It also identifies how the system was previously shut down so that the application or driver can take appropriate steps to restore its data and continue its own operation.

+$#A Power Messages

The system sends power messages to all applications and installable drivers whenever a power management event occurs or whenever an application calls the SetSystemPowerState function to suspend operation. The system sends these messages through the WM_POWERBROADCAST message, setting the wParam parameter to the message type. For example, the message type, PBT_APMPOWERSTATUSCHANGE, indicates a system power status change message.

The system sends a PBT_APMQUERYSUSPEND or PBT_APMQUERYSTANDBY message to request permission to suspend system operation or place it on stanmdby. The system expects each application and driver to determine whether the requested event should occur and to return TRUE or FALSE indicating this decision. Any application or driver can deny the request and prevent it from occurring.

The system sends a PBT_APMSUSPEND or PBT_APMSTANDBY message immediately before suspending operation or placing it on standby. This gives applications and drivers one last chance to prepare for the event before it occurs. In many cases, the system sends these messages without requesting permission to do so. This happens, for example, if an application forces suspension with the SetSystemPowerState function.

The standby messages (PBT_APMQUERYSTANDBY, PBT_APMQUERYSTANDBYFAILED, PBT_APMRESUMESTANDBY, and PBT_APMSTANDBY) are not set to applications. these are intended to be sent to low-level drivers and system components.

The system sends the PBT_APMQUERYSUSPENDFAILED or PBT_APMQUERYSTANDBYFAILED message whenever a requested event is denied. These messages are intended to notify applications and drivers to continue operation as usual.

The system sends the PBT_APMRESUMESUSPEND, PBT_APMRESUMESTANDBY, or PBT_APMRESUMECRITICAL message when system operation has been restored.

Note The WM_POWER message, previously available to applications and drivers for use with power management, is maintained for backward compatibility. All current applications and installable drivers should use and process the WM_POWERBROADCAST message instead.

+$#A Power Management Reference

This section describes the power management functions, messages, and structures.

+$#A Power Management Functions

This section describes the power management functions.

GetSystemPowerStatus
SetSystemPowerState

+$#A Power Management Messages

This section descibes the power management messages.

PBT_APMPOWERSTATUSCHANGE
PBT_APMQUERYSTANDBY
PBT_APMQUERYSTANDBYFAILED
PBT_APMQUERYSUSPEND
PBT_APMQUERYSUSPENDFAILED
PBT_APMRESUMECRITICAL
PBT_APMRESUMESTANDBY
PBT_APMRESUMESUSPEND
PBT_APMSTANDBY
PBT_APMSUSPEND
WM_POWERBROADCAST

+$#A Power Management Structure

This section describes the power management structure.

SYSTEM_POWER_STATUS

Créé avec HelpNDoc Personal Edition: Générateur gratuit de livres électroniques et documentation


Printing and Print Spooler.rtf

+$#AK Printing and Print Spooler

Microsoft® Windows® provides a complete set of functions that allow applications to print on a variety of devices: laser printers, vector plotters, raster printers, and fax machines.

+$#A About Printing

One of the chief features of Windows printing functions is their support of device independence; instead of issuing device-specific commands to draw output on a particular printer or plotter, an application calls high-level functions from graphics device interface (GDI). For example, to print a bitmapped image, an application can call the BitBlt function, supplying the coordinates for the bitmap as well as handles identifying the source and destination device contexts (DCs). The call to BitBlt is then converted to raw device commands by a printer device driver. A device driver is a Windows dynamic-link library (DLL) that supports the Windows device driver interface (DDI). A device driver generates raw device commands when it processes calls to DDI functions made by the graphics engine. The commands are processed by the printer when it prints the image. The syntax, number, and type of these commands vary from device to device.

+$#A Default Printing Interface

In addition to GDI and the device driver, the default printing interface consists of several other components that process output before it arrives at the printer. These components are the print spooler, the print processor, the graphics engine, and the monitor.

+$#A Print Spooler

The primary component of the printing interface is the print spooler. The print spooler is a Windows executable file that manages the printing process. Management of printing involves retrieving the location of the correct printer driver, loading that driver, converting high-level function calls to journal records, storing those journal records on disk as a print job, and so on.

The spooler is loaded at startup and continues to run until the operating system is shut down. Windows Print Manager provides a graphical interface that the user or system administrator can use to access and configure the spooler; however, if Print Manager is disabled or shut down, the spooler continues to run.

Applications that print create a printer DC. When an application creates a printer DC, the spooler performs necessary tasks such as determining the location of the required printer driver and then loading the appropriate printer driver. It also determines the data type used to record the print job. The supported data types include journal records, ASCII text, and PostScript®. A print job is a document stored internally (by using one of the supported data types) that may contain one or more pages of output. It may consist of multiple forms; for example, a job may consist of one envelope and three pages of A4 paper. A print job is defined (or bracketed) by the StartDoc and EndDoc functions.

The default data type for a print job is the journal record. A journal record is a compact structure used to store text output commands, raster graphics commands, and so on. When an application calls StartDoc, the spooler creates a journal file and a data file and begins storing journal records in the journal file. Each time the application calls one of the GDI drawing functions, one or more new journal records are created and stored in the journal file. The journal and data files are created in an operating system directory. The spooler uses the journal file to store journal records and uses the data file to record the type of form, the data type for the print job, the target printer, and so on. These files are deleted by the spooler when the job has successfully printed.

+$#A Print Processor

The spooler monitors the current print jobs and the target printer to determine an appropriate time to print a job. Once the spooler determines that a job should be printed, it calls the print processor. The print processor is a Windows DLL that reads and converts journal records into DDI calls.

+$#A Graphics Engine

The graphics engine is a Windows DLL that converts print processor output into calls to device driver functions. The device driver, in turn, processes these calls and converts them into raw device commands that the device can process.

+$#A Monitor

Once a device driver has converted an entire journal file into raw device commands, the file of converted commands is passed back to the spooler. The spooler sends these low-level commands to a monitor. A monitor is a Windows DLL that passes the raw device commands over the network, through a parallel port, or through a serial port to the device.

+$#A Printer Device Contexts Overview

Just as an application requires a display DC before it can begin drawing in the client area of a window, it needs a special printer DC before it can begin sending output to a printer. A printer DC is similar to a display DC in that it is an internal data structure that defines a set of graphic objects and their associated attributes and specifies the graphic modes that affect output. The graphic objects include a pen (for line drawing), a brush (for painting and filling), and a font (for text output).

Unlike a display DC, printer DCs are not owned by the window management component, and they cannot be obtained by calling the GetDC function. Instead, an application must call the CreateDC or PrintDlg function. When an application calls CreateDC, it must supply a driver and port name. This data is stored in one of the operating system initialization (.INI) files. It can be retrieved by calling the EnumPrinters function.

When an application calls PrintDlg and specifies the PD_RETURNDC value in the Flags member of the PRINTDLG structure, Windows automatically returns a handle identifying the user's selected printer DC. For an example that demonstrates this task, see Using a Print Dialog Box and Retrieving a Printer Device Context.

+$#A Printer Escapes

Windows version 3.x supported as many as 64 special functions called printer escapes that applications used to access special device features. An application called these functions by specifying one of 64 predefined values as the second parameter to the Escape function. For example, one of these applications printing on a PostScript printer could draw a PostScript graphic object called a path by calling the Escape function and supplying the BEGIN_PATH and END_PATH values. Most of these escapes are obsolete in the Microsoft® Win32® application programming interface (API) but are provided to simplify the porting of 16-bit Windows-based applications. The Win32 version of GDI supports a complete set of path functions that applications can use instead of the escapes to draw paths on any device. For a list of the Win32 functions that replace some of the escapes, see the Escape function.

Of the 64 original printer escapes, only the QUERYESCSUPPORT and the PASSTHROUGH escapes can be used by Win32-based applications:

Applications written for Windows 3.x can also use QUERYESCSUPPORT and PASSTHROUGH as well as the following 10 escapes. Note that the following escapes are only supported for backwards compatibility with Windows 3.x-based applications. The Escape function may fail if a Win32-based application uses any of these.

• ABORTDOC

• ENDDOC

• GETPHYSPAGESIZE

• GETPRINTINGOFFSET

• GETSCALINGFACTOR

• NEWFRAME

• NEXTBAND

• SETABORTPROC

• SETCOPYCOUNT

• STARTDOC

In addition to supporting Escape, the Win32 API provides a new extended escape function called ExtEscape. This function allows applications to access capabilities of a particular device not directly available through GDI.

+$#A WYSIWYG Display and Output

Most applications attempt to support WYSIWYG (what you see is what you get) output. This means that text drawn with a 10-point Helvetica bold font in the application window should have a similar appearance when it is printed. Obtaining true WYSIWYG output is virtually impossible and even undesirable in most cases. This is due, in part, to the differences in video and printer technologies; a pixel on a screen is generally larger than a dot on a common laser printer. Viewing distances are different as well; a computer user typically sits about two feet away from the screen, but a reader's eyes are usually one foot or less from the printed page.

To compensate for legibility differences between screens and the printed page, Windows supports a unit called the logical inch that is always specified in pixels. For a video display, the logical inch is always greater than the physical inch to compensate for the longer viewing distance and the (generally) coarser resolution. For printers, the logical inch is always equal to the physical inch.

To obtain a WYSIWYG effect when drawing text, an application can use the CreateFont function to specify the typeface name and point size of an ideal (or logical) font and then call the SelectObject function to identify the display or printer DC. When the application calls SelectObject, Windows selects a physical font that is the closest possible match to the specified logical font. When Windows selects the display font, it chooses a physical font that is larger than the actual point size. From the user's perspective, however, it appears to be very close to the correct height. When Windows selects the font for the printer, it chooses a physical font that is actually the requested point size. For more information about fonts and text output, see Fonts and Text.

To obtain a WYSIWYG effect when drawing bitmapped graphics, an application can retrieve the width and height, in logical inches, of the screen and the printed page. Using these values, the application can create horizontal and vertical scaling factors to maintain the proportion of bitmapped images when they are drawn on a printer. For an example that demonstrates this process, see WYSIWYG Display and Output. For more information about bitmaps and bitmap output, see Bitmaps.

+$#A Using the Printing Functions

This section contains examples that demonstrate how to print text and graphics. The code was taken from an application that allows a user to open and display a 16-color bitmapped image that is stored as a .BMP file.

In addition to displaying the bitmapped image, the application allows the user to configure the printer and then print the image and a string of text specifying the location of the file that contained the image. The application prints the text at the top of the page and the image in the center of the page.

• Opening and displaying a .BMP file

• Using a print dialog box and retrieving a printer device context

• Preparing to print

• Printing a document

+$#A Opening and Displaying a .BMP File

In the sample application, a user is able to open a .BMP file that contains a bitmapped image and display that image in the client area of the application's window. The user selects the file to be opened when the application displays the Open dialog box. (For more information about the Open dialog box, see Common Dialog Box Library.)

After the user selects a file and closes the dialog box, the file and path names are stored in members of the OPENFILENAME structure. The application uses this data to open the appropriate file and retrieve the bitmap header and data. The following code sample can be used to retrieve this data.

/* Retrieve a handle identifying the file. */

hfbm = CreateFile(ofn.lpstrFile, GENERIC_READ,

FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,

(HANDLE) NULL);

/* Retrieve the BITMAPFILEHEADER structure. */

ReadFile(hfbm, &bmfh, sizeof(BITMAPFILEHEADER),

&dwRead, (LPOVERLAPPED)NULL);

/* Retrieve the BITMAPFILEHEADER structure. */

ReadFile(hfbm, &bmih, sizeof(BITMAPINFOHEADER),

&dwRead, (LPOVERLAPPED)NULL);

/* Allocate memory for the BITMAPINFO structure. */

hmem1 = GlobalAlloc(GHND,

sizeof(BITMAPINFOHEADER) +

((1<<bmih.biBitCount) * sizeof(RGBQUAD)));

lpbmi = GlobalLock(hmem1);

/*

* Load BITMAPINFOHEADER into the BITMAPINFO

* structure.

*/

lpbmi->bmiHeader.biSize = bmih.biSize;

lpbmi->bmiHeader.biWidth = bmih.biWidth;

lpbmi->bmiHeader.biHeight = bmih.biHeight;

lpbmi->bmiHeader.biPlanes = bmih.biPlanes;

lpbmi->bmiHeader.biBitCount = bmih.biBitCount;

lpbmi->bmiHeader.biCompression = bmih.biCompression;

lpbmi->bmiHeader.biSizeImage = bmih.biSizeImage;

lpbmi->bmiHeader.biXPelsPerMeter = bmih.biXPelsPerMeter;

lpbmi->bmiHeader.biYPelsPerMeter = bmih.biYPelsPerMeter;

lpbmi->bmiHeader.biClrUsed = bmih.biClrUsed;

lpbmi->bmiHeader.biClrImportant = bmih.biClrImportant;

/*

* Retrieve the color table.

* 1 << bmih.biBitCount == 2 ^ bmih.biBitCount

*/

ReadFile(hfbm, lpbmi->bmiColors,

((1<<bmih.biBitCount) * sizeof(RGBQUAD)),

&dwRead, (LPOVERLAPPED) NULL);

/*

* Allocate memory for the required number of

* bytes.

*/

hmem2 = GlobalAlloc(GHND,

(bmfh.bfSize - bmfh.bfOffBits));

lpvBits = GlobalLock(hmem2);

/* Retrieve the bitmap data. */

ReadFile(hfbm, lpvBits,

(bmfh.bfSize - bmfh.bfOffBits),

&dwRead, (LPOVERLAPPED) NULL);

/*

* Create a bitmap from the data stored in the

* .BMP file.

*/

hbm = CreateDIBitmap(hdc, &bmih,

CBM_INIT, lpvBits,

lpbmi, DIB_RGB_COLORS);

/*

* Unlock the global memory objects and

* close the .BMP file.

*/

GlobalUnlock(hmem1);

GlobalUnlock(hmem2);

CloseHandle(hfbm);

/* Set the fDisplayBitmap flag. */

if (hbm)

fDisplayBitmap = TRUE;

else

TextOut(hdc, 100, 100, "LoadBitmap Failed", 17);

/* Paint the window (and draw the bitmap). */

GetClientRect(hwnd, &rect);

InvalidateRect(hwnd, &rect, TRUE);

UpdateWindow(hwnd);

Once the bitmap data is retrieved, the bitmapped image can be drawn in the application's client area. The following code sample is used to draw the bitmap.

case WM_PAINT:

BeginPaint(hwnd, &ps);

if (fDisplayBitmap) {

hdcMem = CreateCompatibleDC(ps.hdc);

SelectObject(hdcMem, hbm);

GetObject(hbm, sizeof(BITMAP), (LPSTR) &bm);

BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight,

hdcMem, 0, 0, SRCCOPY);

DeleteDC(hdcMem);

}

EndPaint(hwnd, &ps);

break;

+$# Using a Print Dialog Box and Retrieving a Printer Device Context

The first step in printing involves setting up the printer and obtaining a printer DC. In the sample application, the File menu contains two options, Print and Print Setup. By selecting either option, the user can configure the printer. When the user selects the Print Setup option, the Print Setup dialog box is displayed and the user can select a printer, a page orientation, a paper size, and so on. When the user selects the Print option, the Print dialog box is displayed and the user can select a range of pages, a print quality, a number of copies, and so on. The user can also display the Print Setup dialog box by clicking the Setup push button.

The Print and Print Setup dialog boxes are both displayed by initializing the members of a PRINTDLG structure and calling the PrintDlg function. (For more information about displaying the Print Setup dialog box, see Common Dialog Box Library). In addition to retrieving user-specified data, PrintDlg can be used to obtain a printer DC by specifying the PD_RETURNDC value in the Flags member of the PRINTDLG structure. The following code sample shows how to intialize the members of the structure and to display the Print dialog box.

/* Initialize the PRINTDLG members. */

pd.lStructSize = sizeof(PRINTDLG);

pd.hDevMode = (HANDLE) NULL;

pd.hDevNames = (HANDLE) NULL;

pd.Flags = PD_RETURNDC;

pd.hwndOwner = hwnd;

pd.hDC = (HDC) NULL;

pd.nFromPage = 1;

pd.nToPage = 1;

pd.nMinPage = 0;

pd.nMaxPage = 0;

pd.nCopies = 1;

pd.hInstance = (HANDLE) NULL;

pd.lCustData = 0L;

pd.lpfnPrintHook = (LPPRINTHOOKPROC) NULL;

pd.lpfnSetupHook = (LPSETUPHOOKPROC) NULL;

pd.lpPrintTemplateName = (LPSTR) NULL;

pd.lpSetupTemplateName = (LPSTR) NULL;

pd.hPrintTemplate = (HANDLE) NULL;

pd.hSetupTemplate = (HANDLE) NULL;

/* Display the PRINT dialog box. */

PrintDlg(&pd);

+$#A Preparing to Print

The following code sample determines whether the selected printer is capable of printing bitmaps by calling the GetDeviceCaps function, supplying the RASTERCAPS value. By examining this function's return value, the application can determine whether it should print a document or inform the user that the device does not support raster output.

/*

* Examine the raster capabilities of the device

* identified by pd.hDC to verify that it supports

* the BitBlt function.

*/

if (!(GetDeviceCaps(pd.hDC, RASTERCAPS)

& RC_BITBLT)) {

DeleteDC(pd.hDC);

MessageBox(hwnd,

"Printer cannot display bitmaps.",

"Device Error",

MB_OK);

break;

}

After the sample application determines that the selected printer is capable of printing bitmaps, it follows these steps:

1. Sets a Boolean flag that the application's abort procedure can examine to determine whether to allow printing to continue.

2. Registers the application's AbortProc function, which is used to cancel a print job.

3. Displays a modeless Cancel dialog box.

4. Disables the application's window while the dialog box is displayed.

The steps are illustrated in the following code sample.

/*

* Set the flag used by the AbortPrintJob

* dialog procedure.

*/

bPrint = TRUE;

/*

* Register the application's AbortProc

* function with GDI.

*/

SetAbortProc(pd.hDC, AbortProc);

/* Display the modeless Cancel dialog box. */

hdlgCancel = CreateDialog(hinst, (LPTSTR) "AbortDlg",

hwnd, (DLGPROC) AbortPrintJob);

/* Disable the application's window. */

EnableWindow(hwnd, FALSE);

Once the application registers AbortProc with Windows, GDI calls the function repeatedly during the printing process to determine whether to cancel a job. In the current version of the Win32 API, GDI calls this function approximately every two seconds until the entire job has been spooled.

If the user chooses to cancel the job, GDI notifies the spooler that it should delete the corresponding journal file from the print queue and reset the printer to its default state.

+$#A Abort Procedure

Any application written for Windows that supports printing should provide an abort procedure and a modeless dialog box that allow a user to cancel a print job. The abort procedure for the sample application contains a message loop that retrieves messages for the modeless dialog box.

BOOL CALLBACK AbortProc(HDC hdc, int nCode)

{

MSG msg;

/*

* Retrieve and remove messages from the thread's message

* queue.

*/

while (PeekMessage((LPMSG) &msg, (HWND) NULL,

0, 0, PM_REMOVE)) {

/* Process any messages for the Cancel dialog box. */

if (!IsDialogMessage(hdlgCancel, (LPMSG) &msg)) {

TranslateMessage((LPMSG) &msg);

DispatchMessage((LPMSG) &msg);

}

}

/*

* Return the global bPrint flag (which is set to FALSE

* if the user presses the Cancel button).

*/

return bPrint;

}

For Win16-based applications, this procedure must be exported in the application's module-definition (.DEF) file.

+$#A Cancel Dialog Box

The Cancel dialog box typically contains a single push button that allows the user to cancel a print job. The following template for the Cancel dialog box was taken from the sample application's resource (.RES) file.

AbortDlg DIALOG LOADONCALL MOVEABLE DISCARDABLE 33, 32, 160, 70

CAPTION "Sample Printing App"

STYLE WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_VISIBLE | WS_POPUP | WS_SYSMENU

BEGIN

CONTROL "Now Printing: ", IDD_TEXT, "static", SS_CENTER | WS_CHILD, 0, 10, 160, 8

CONTROL "", IDD_FILE, "static", SS_CENTER | WS_CHILD, 0, 25, 160, 8

CONTROL "Cancel", IDD_CANCEL, "button", BS_DEFPUSHBUTTON | WS_TABSTOP | WS_CHILD, 60, 45, 45, 15

END

The code sample that follows shows the dialog box procedure for the application.

LRESULT CALLBACK AbortPrintJob(

HWND hwndDlg, /* window handle of dialog box */

UINT message, /* type of message */

WPARAM wParam, /* message-specific information */

LPARAM lParam) /* message-specific information */

{

switch (message) {

case WM_INITDIALOG: /* message: initialize dialog box */

/* Initialize the static text control. */

SetDlgItemText(hwndDlg, IDD_FILE, ofn.lpstrFile);

return TRUE;

case WM_COMMAND: /* message: received a command */

/* User pressed "Cancel" button--stop print job. */

MessageBox(hwndDlg, "Incoming", "WM_COMMAND", MB_OK);

bPrint = FALSE;

return TRUE;

default:

return FALSE; /* didn't process a message */

}

UNREFERENCED_PARAMETER(lParam);

UNREFERENCED_PARAMETER(wParam);

UNREFERENCED_PARAMETER(message);

}

+$#A Printing a Document

Once the application initializes the necessary variables, registers its AbortProc function, and displays its modeless Cancel dialog box, it can start the print job by calling the StartDoc function.

After the application begins a print job, it can define individual pages in the document by calling the StartPage and EndPage functions and embedding the appropriate calls to GDI drawing functions within this bracket. After the application has defined the last page, it can close the document and end the print job by calling the EndDoc function.

The following example shows the code required to print a string of text and a bitmapped image. The string of text, centered at the top of the page, identifies the path and filename for the file that contains the bitmapped image. The bitmapped image, centered vertically and horizontally on the page, is drawn so that the same proportions used to draw the image in the application's window are maintained.

/*

* Initialize the members of a DOCINFO

* structure.

*/

di.cbSize = sizeof(DOCINFO);

di.lpszDocName = "Bitmap Printing Test";

di.lpszOutput = (LPTSTR) NULL;

di.lpszDataType = (LPTSTR) NULL;

di.fwType = 0;

/*

* Begin a print job by calling the StartDoc

* function.

*/

nError = StartDoc(pd.hDC, &di);

if (nError == SP_ERROR) {

errhandler("StartDoc", hwnd);

goto Error;

}

/*

* Inform the driver that the application is

* about to begin sending data.

*/

nError = StartPage(pd.hDC);

if (nError <= 0) {

errhandler("StartPage", hwnd);

goto Error;

}

/*

* Retrieve the number of pixels-per-logical-inch

* in the horizontal and vertical directions

* for the display upon which the bitmap

* was created.

*/

fLogPelsX1 = (float) GetDeviceCaps(pd.hDC, LOGPIXELSX);

fLogPelsY1 = (float) GetDeviceCaps(pd.hDC, LOGPIXELSY);

/*

* Retrieve the number of pixels-per-logical-inch

* in the horizontal and vertical directions

* for the printer upon which the bitmap

* will be printed.

*/

fLogPelsX2 = (float) GetDeviceCaps(pd.hDC,

LOGPIXELSX);

fLogPelsY2 = (float) GetDeviceCaps(pd.hDC,

LOGPIXELSY);

/*

* Determine the scaling factors required to

* print the bitmap and retain its original

* proportions.

*/

if (fLogPelsX1 > fLogPelsX2)

fScaleX = (fLogPelsX1 / fLogPelsX2);

else

fScaleX = (fLogPelsX2 / fLogPelsX1);

if (fLogPelsY1 > fLogPelsY2)

fScaleY = (fLogPelsY1 / fLogPelsY2);

else

fScaleY = (fLogPelsY2 / fLogPelsY1);

/*

* Compute the coordinate of the upper left

* corner of the centered bitmap.

*/

cWidthPels = GetDeviceCaps(pd.hDC, HORZRES);

xLeft = ((cWidthPels / 2) -

((int) (((float) bmih.biWidth)

* fScaleX)) / 2);

cHeightPels = GetDeviceCaps(pd.hDC, VERTRES);

yTop = ((cHeightPels / 2) -

((int) (((float) bmih.biHeight)

* fScaleY)) / 2);

/*

* Create a memory DC that is compatible with

* the printer and select the bitmap (which

* the user requested) into this DC.

*/

hdcMem = CreateCompatibleDC(pd.hDC);

if (!SelectObject(hdcMem, hbm))

errhandler("SelectObject Failed", hwnd);

/*

* Use the StretchBlt function to scale the

* bitmap and maintain its original proportions

* (that is, if the bitmap was square when it

* appeared in the application's client area,

* it should also appear square on the page).

*/

if (!StretchBlt(pd.hDC, xLeft, yTop,

(int) ((float) bmih.biWidth * fScaleX),

(int) ((float) bmih.biHeight * fScaleY),

hdcMem, 0, 0,

bmih.biWidth, bmih.biHeight,

SRCCOPY))

errhandler("StretchBlt Failed", hwnd);

/* Delete the memory DC. */

DeleteDC(hdcMem);

/*

* Retrieve the width of the string that

* specifies the full path and filename for the

* file that contains the bitmap.

*/

GetTextExtentPoint32(pd.hDC, ofn.lpstrFile,

ofn.nFileExtension + 3,

&szMetric);

/*

* Compute the starting point for the

* text-output operation. The string will

* be centered horizontally and positioned

* three-lines down from the top of the page.

*/

xLeft = ((cWidthPels / 2) - (szMetric.cx / 2));

yTop = (szMetric.cy * 3);

/*

* Print the path and filename for the bitmap,

* centered at the top of the page.

*/

TextOut(pd.hDC, xLeft, yTop, ofn.lpstrFile,

ofn.nFileExtension + 3);

/*

* Determine whether the user has pressed

* the Cancel button in the AbortPrintJob

* dialog box; if the button has been pressed,

* call the AbortDoc function. Otherwise, inform

* the spooler that the page is complete.

*/

nError = EndPage(pd.hDC);

if (nError <= 0) {

errhandler("EndPage", hwnd);

goto Error;

}

/* Inform the driver that document has ended. */

nError = EndDoc(pd.hDC);

if (nError <= 0)

errhandler("EndDoc", hwnd);

Error:

/* Enable the application's window. */

EnableWindow(hwnd, TRUE);

/* Remove the AbortPrintJob dialog box. */

DestroyWindow(hdlgCancel);

/* Delete the printer DC. */

DeleteDC(pd.hDC);

Because the pixels on a screen typically have different dimensions than the dots on a printer, it is necessary to scale bitmapped images to obtain a WYSIWYG effect. This is done by obtaining horizontal and vertical scaling factors and then applying those factors to the width and height values passed to the StretchBlt function. In the sample application, the scaling factors were obtained by retrieving the horizontal and vertical logical-pixel count for the two devices. Once the scaling factors were obtained, they were used to adjust the bitmap width and height.

To center the bitmap on the page, the application first computed the width and height of the scaled bitmap. (The bitmap was scaled to maintain the original proportions of the image.) These values were divided by two and then subtracted from half of the width and height of the page. The result defines the coordinates of the upper left corner of the bitmap.

To center the text at the top of the page, the application called the GetTextExtentPoint32 function to retrieve the width and height of the string specifying the path names and filenames. Once these values were obtained, the application used the height to position the string three lines down the page and the width to position the string horizontally centered on the page.

The following illustration shows a representation of the page that appeared when the application printed the bitmapped image in the WINLOGO.BMP file. This illustration also depicts the variables used to position the text and to position and scale the bitmap.

{ewc msdncd, EWGraphic, group10664 0 /a "SDK_01.bmp"}

+$#AK Printing and Print Spooler Reference

The following functions, structures, and messages are associated with printing and the print spooler.

+$#A Printing and Print Spooler Functions

Following are the functions used to print.

AbortDoc
DeviceCapabilities
EndDoc
EndPage
Escape
ExtEscape
SetAbortProc
StartDoc
StartPage

Following are the functions used to access the print spooler.

AbortPrinter
AbortProc
AddForm
AddJob
AddMonitor
AddPort
AddPrinter
AddPrinterConnection
AddPrinterDriver
AddPrintProcessor
AddPrintProvidor
AdvancedDocumentProperties
ClosePrinter
ConfigurePort
ConnectToPrinterDlg
DeleteForm
DeleteMonitor
DeletePort
DeletePrinter
DeletePrinterConnection
DeletePrinterDriver
DeletePrintProcessor
DeletePrintProvidor
DocumentProperties
EndDocPrinter
EndPagePrinter
EnumForms
EnumJobs
EnumMonitors
EnumPorts
EnumPrinterDrivers
EnumPrinters
EnumPrintProcessorDataTypes
EnumPrintProcessors
FindClosePrinterChangeNotification
FindFirstPrinterChangeNotification
FindNextPrinterChangeNotification
FreePrinterNotifyInfo
GetForm
GetJob
GetPrinter
GetPrinterData
GetPrinterDriver
GetPrinterDriverDirectory
GetPrintProcessorDirectory
OpenPrinter
PrinterMessageBox
PrinterProperties
ReadPrinter
ResetPrinter
ScheduleJob
SetForm
SetJob
SetPrinter
SetPrinterData
StartDocPrinter
StartPagePrinter
WaitForPrinterChange
WritePrinter

+$#A Printing and Print Spooler Structures

The following structures are used with the print spooler:

DATATYPES_INFO_1
PRINTER_NOTIFY_OPTIONS
PRINTER_NOTIFY_OPTIONS_TYPE
PRINTER_NOTIFY_INFO
PRINTER_NOTIFY_INFO_DATA

+$#A Printing and Print Spooler Messages

Following is the message used with the print spooler.

WM_SPOOLERSTATUS

Créé avec HelpNDoc Personal Edition: Générateur complet d'aides multi-formats


Processes and Threads.rtf

+$#AK Processes and Threads

A process is an executing application that consists of a private virtual address space, code, data, and other operating system resources, such as files, pipes, and synchronization objects that are visible to the process. A process also contains one or more threads that run in the context of the process. A thread is the basic entity to which the operating system allocates CPU time. A thread can execute any part of the application's code, including a part currently being executed by another thread. All threads of a process share the virtual address space, global variables, and operating system resources of the process.

This overview describes processes and threads, and explains how to create and use them.

+$#A About Processes and Threads

An application written for Microsoft® Windows® can consist of more than one process, and a process can consist of more than one thread. The Microsoft® Win32® application program interface (API) supports multitasking, which creates the effect of simultaneous execution of multiple processes and threads.

+$#A Multitasking

A process is a program that is loaded into memory and prepared for execution. Each process has a private virtual address space. A process consists of the code, data, and other system resources ⎯ such as files, pipes, and synchronization objects ⎯ that are accessible to the threads of the process. Each process is started with a single thread, but additional independently executing threads can be created.

A thread can execute any part of the program's code, including a part executed by another thread. Threads are the basic entity to which the operating system allocates CPU time. Each thread maintains a set of structures for saving its context while waiting to be scheduled for processing time. The context includes the thread's set of machine registers, the kernel stack, a thread environment block, and a user stack in the address space of the thread's process. All threads of a process share the virtual address space and can access the global variables and system resources of the process.

A multitasking operating system divides the available CPU time among the threads that need it. In Windows, the Win32 API is designed for preemptive multitasking; this means that the system allocates small slices of CPU time among the competing threads. The currently executing thread is suspended when its time slice elapses, allowing another thread to run. When the system switches from one thread to another, it saves the context of the suspended thread and restores the saved context of the next thread in the queue.

Because each time slice is small (approximately 20 milliseconds), it appears that multiple threads are executing at the same time. This is actually the case on multiprocessor systems, where the executable threads are distributed among the available processors. On a single processor system, however, using multiple threads does not result in more instructions being executed. In fact, the system can slow down if it is forced to keep track of too many threads.

To the user, the advantage of multitasking is the ability to have several applications open and working at the same time. For example, a user can edit a file with one application while another application is printing or recalculating a spreadsheet.

To the application developer, the advantage of multitasking is the ability to create applications that use more than one process and to create processes that use more than one thread of execution. For example, you can have one thread that handles interactions with the user (keyboard and mouse input), while other threads with lower priority perform the other work of the process. The higher priority of the input thread makes the program responsive to the user, while the other threads ensure that the processor can be used efficiently during the slices of time between keystrokes.

+$#A When to Use Multitasking

Any thread of any process can use the CreateThread and CreateProcess functions to create additional threads or processes. An application might use multiple processes for functions that require a private address space and private resources, to protect them from the activities of other threads. Functions that are to be distributed as separate executable modules but that can be integrated to form a single application, can benefit from the multiprocess approach.

It is useful for a process to have multiple threads in situations where the application has several tasks to run concurrently. For example, an application that opens more than one window can have a separate thread perform the work of the application in each window. Multiple threads can be a convenient way to structure a program that performs several similar or identical tasks. For example, a named pipe server can create a thread to handle communications with each client process that attaches to the pipe. Other examples of using multiple threads are processes that do the following:

• Handle input for multiple windows.

• Manage input from several communications devices.

• Need to distinguish among tasks of varying priority. For example, a high-priority thread handles time-critical tasks, and a low-priority thread performs other tasks.

It is typically more efficient for an application to implement multitasking by distributing tasks among the threads of a single process, rather than by creating multiple processes, for the following reasons:

• The system can create and execute threads more quickly than it can create processes, primarily because the code for threads has already been mapped into the address space of the process, while the code for a new process must be loaded.

• All threads of a process share the same address space and can access the process's global variables, which can simplify communications between threads.

• All threads of a process can use open handles to resources such as files and pipes.

For both multiple threads and multiple processes, there are costs and dangers to be considered. The system consumes memory for the structures required by both processes and threads. Keeping track of a large number of threads consumes CPU time. With multiple threads accessing the same resources, you must synchronize the threads to avoid conflicts. This is true for system resources (such as communications ports or disk drives), handles to resources shared by multiple processes (such as file or pipe handles), or the resources of a single process (such as global variables accessed by multiple threads). Failure to synchronize multiple threads properly (in the same or in different processes) can lead to program problems such as deadlock and race conditions. The Win32 API provides a set of synchronization objects and functions you can use to coordinate multiple threads. For more information about synchronization, see Synchronizing Execution of Multiple Threads and Synchronization.

The Win32 API also provides alternative ways to execute multiple tasks that, in some cases, make multithreaded processes unnecessary. The most significant of these are overlapped I/O and the ability to wait for multiple events.

Overlapped I/O enables a single thread to initiate multiple time consuming I/O requests that can run concurrently. This asynchronous I/O can be performed on files, pipes, serial communications devices, or tape devices. For more information about files, see Files. For more information about pipes, see Pipes. For more information about serial communication devices, see Communications. For more information about tape devices, see Tape Backup.

Waiting for multiple events enables a single thread to block its execution while waiting for any one of several events to occur. This is much more efficient than using multiple threads, each waiting for a single event, or using a single thread that consumes CPU time by continually checking for an event of interest. For more information about waiting for multiple events, see Synchronization.

+$#A Scheduling

The system scheduler controls multitasking by determining which of the competing threads receives the next slice of the processor's time. Each time slice consists of a fixed number of clock ticks. The Windows scheduler is preemptive, gaining control whenever a clock interrupt occurs and decrementing the count of clock ticks remaining in the time slice of the currently executing thread. If the count goes to zero, the scheduler determines which thread runs next. The scheduler maintains a queue of executable threads at each priority level. When the highest priority is shared by more than one executable thread, the next time slice goes to the thread at the head of the priority queue. The thread that just finished executing is placed at the end of the queue of threads with its priority. Threads that are suspended or blocked (for example, waiting for a semaphore object or waiting for input) are not executable, so until they become unblocked, the scheduler does not allocate any processor time to them, regardless of their priority. When the scheduler awards the next time slice to a different thread, the context of the currently executing thread is saved, and the machine state is set according to the context of the next executable thread.

+$#A Scheduling Priorities

The scheduling priority of each thread is determined by the following criteria:

• The priority class of its process (high, normal, or idle)

• The priority level of the thread within the priority class of its process (lowest, below normal, normal, above normal, highest)

• The dynamic priority boost, if any, the system applies to the thread's base priority

By default, the priority class of a process is set to normal. The CreateProcess function allows a parent process to specify the priority class of its child processes. Use SetPriorityClass to change the priority class of a process and GetPriorityClass to determine the current priority class of a process.

There are five priority levels within each priority class. The levels range from lowest (−2) to highest (2). All threads are started with normal (0) priority. The SetThreadPriority function enables you to change the priority level of a thread to any of the five levels. Use the GetThreadPriority function to determine the current priority level of a thread.

The base priority of each thread is the thread's priority level within the priority class of its process. The scheduler uses this base priority to determine a thread's dynamic priority, which it uses to make scheduling decisions. A thread's dynamic priority is never less than its base priority. The scheduler raises and lowers the dynamic priority of a thread to enhance its responsiveness when significant things happen to the thread. For example, whenever a window receives input (such as timer messages, mouse move messages, keyboard input), the scheduler boosts the priorities of all threads within the process that owns the window. In some cases, the dynamic priority of a waiting thread is raised when a wait is satisfied. For example, a wait associated with disk or keyboard I/O results in a dynamic boost when the input occurs.

After raising a thread's dynamic priority, the scheduler reduces that priority by one level each time the thread completes a time slice, until the thread drops back to its base priority.

In addition to these dynamic priority boosts, the scheduler raises the priority class of the process associated with the foreground window, so it is greater than or equal to the priority class of any background processes. The process's priority class returns to its original setting when it is no longer in the foreground. A user can always make a given window lose the foreground priority boost by clicking on another window or by using the ALT+TAB or CTRL+ESC key combination.

The priority classes are intended to be used as follows.

Priority | Meaning
HIGH_PRIORITY_CLASS | Indicates a process that performs time-critical tasks that must be executed immediately for it to run correctly. The threads of a high-priority class process preempt the threads of normal or idle priority class processes. An example is Windows Task List, which must respond quickly when called by the user, regardless of the load on the operating system. Use extreme care when using the high-priority class, because a high-priority class CPU-bound application can use nearly all available cycles.
IDLE_PRIORITY_CLASS | Indicates a process whose threads run only when the system is idle and are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle priority class is inherited by child processes.
NORMAL_PRIORITY_CLASS | Indicates a normal process with no special scheduling needs.
REALTIME_PRIORITY_CLASS | Indicates a process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.

Use the high-priority class with extreme care. If a thread runs at the highest priority level for extended periods, other threads in the system will be starved for CPU time. If several threads are set at high priority at the same time, the effect of the high priority is neutralized. The high-priority class should be reserved for threads that must respond to time critical events. If your application has a task that requires the high-priority class while most of its functions are normal priority, use SetPriorityClass to raise the priority class temporarily; then reduce it once the time-critical task has been completed. Another strategy is to create a high-priority process that is blocked most of the time, awakening only when the critical task is needed. The important point is that a thread should be high priority and executable only for a brief period, when it has time-critical work to perform.

Having selected a priority class for a multithreaded process, use SetThreadPriority to adjust the relative priority of its threads. A typical strategy is to use a higher priority level for the process's input thread, to ensure responsiveness to the user. Other threads, particularly those that are CPU intensive, can be set to a lower relative priority, to ensure that they can be preempted when necessary. However, if you have a higher priority thread waiting for a lower priority thread to complete some task, be sure to block the execution of the waiting thread (using a wait function, critical section, or the Sleep function) rather than having it iterate in a loop. Otherwise, the process will deadlock, because the lower priority thread will never get scheduled.

+$#A Creating Threads

The CreateThread function creates a new thread for a process. The creating thread must specify the starting address of the code that the new thread is to execute. Typically, the starting address is the name of a function defined in the program's code. A process can have multiple threads simultaneously executing the same function. The function called by the thread takes a single 32-bit parameter and returns a doubleword result.

The following example illustrates the creation of a new thread that executes the locally defined ThreadFunc function.

DWORD ThreadFunc(LPDWORD lpdwParam) {

printf("ThreadFunc: thread parameter=%d\n", *lpdwParam);

return 0;

}

DWORD main(VOID) {

DWORD dwThreadId, dwThrdParam = 1;

HANDLE hThread;

hThread = CreateThread(

NULL, /* no security attributes */

0, /* use default stack size */

(LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */

&dwThrdParam, /* argument to thread function */

0, /* use default creation flags */

&dwThreadId); /* returns the thread identifier */

/* Check the return value for success. */

if (hThread == NULL)

ErrorExit("CreateThread error\n");

For simplicity, this example passes a pointer to dwThrdParam as an argument to the thread function. This could be a pointer to any type of data or structure, or it could be omitted altogether by passing a NULL pointer and deleting the references to the parameter in ThreadFunc. It is risky to pass the address of the local variable if the creating thread exits before the new thread, because the pointer becomes invalid. Instead, either pass dynamically allocated parameters or make the creating thread wait for the new thread to terminate. Data can also be passed from the creating thread to the new thread using global variables. With global variables, it is usually necessary to synchronize access by multiple threads. For more information about synchronizing, see Synchronizing Execution of Multiple Threads.

Using the thread function's argument to pass data to the new thread is particularly useful in processes where the main thread might create multiple threads to execute the same code. In this case, it is inconvenient to use global variables. For example, a process that enables the user to open several files at the same time can create a new thread for each file, with each of the threads executing the same thread function. The creating thread can pass as an argument the unique information (such as the filename) required by each instance of the thread function.

The creating thread can use the arguments to CreateThread to specify the following:

• The security attributes for the handle of the new thread. These security attributes include an inheritance flag that determines whether the handle can be inherited by child processes. The security attributes also include a security descriptor, which the system uses to perform access checks on all subsequent uses of the thread's handle before access is granted. For more information about security attributes, see Security.

• The initial stack size of the new thread. The thread's stack is allocated automatically in the memory space of the process; it can grow as needed and it is freed when the thread terminates.

• A creation flag that enables you to create the thread in a suspended state. When suspended, the thread does not run until the ResumeThread function is called.

The handle returned by CreateThread has full-access rights to the new thread. Subject to security checks, the handle can be used in any of the Win32 API functions that accepts a handle to a thread object. The handle can be duplicated by DuplicateHandle, which enables you to create a thread handle with a subset of the access rights.

A thread can also be created with the CreateRemoteThread function. This is used by debugger processes to create a thread that runs in the address space of another process.

+$#A Suspending Thread Execution

A thread can suspend and resume the execution of another thread using the SuspendThread and ResumeThread functions. While a thread is suspended, it is not scheduled for time on the processor.

SuspendThread is not particularly useful for synchronization because it does not control the point in the code at which the thread's execution is suspended. However, you might want to suspend a thread in a situation where you are confirming a user's input that would terminate the work of the thread. If confirmed, the thread is terminated; otherwise, it resumes.

If a thread is created in a suspended state, it does not begin to run until ResumeThread is called with a handle to the suspended thread. This can be useful for initializing the thread's state before it begins to run. See Using a Multithreaded Multiple Document Interface Application for an example that uses this method to modify the thread's priority before it can run. Suspending a thread at creation can be useful for one-time synchronization, because ResumeThread ensures that the suspended thread will resume running at the starting point of its code.

A thread can temporarily suspend its own execution for a specified interval by calling the Sleep or SleepEx functions. This is useful particularly in cases where the thread responds to user interaction, as it can delay execution long enough to enable the users to observe the results of their actions. During the sleep interval, the thread is not scheduled for time on the processor.

+$#A Synchronizing Execution of Multiple Threads

To avoid conflicts, it is necessary to synchronize access by multiple threads to shared resources. Synchronization is also necessary to ensure that interdependent code is executed in the proper sequence.

The Win32 API provides a number of objects whose handles can be used to synchronize multiple threads. These objects include:

• Synchronization objects: mutex, semaphore, and event handles

• File handles

• Named pipe handles

• Console input buffer handles

• Communication device handles

• Process handles

• Thread handles

The state of each of these objects is either signaled or not signaled. When a handle to any of these objects is specified by a call to one of the wait functions ⎯ WaitForSingleObject, WaitForMultipleObjects, WaitForSingleObjectEx, or WaitForMultipleObjectsEx ⎯ the execution of the calling thread is blocked until the state of the specified object is signaled. As with a suspended thread, a waiting thread consumes very little processor time.

Some of these objects are useful in blocking a thread until some event occurs. For example, a console input buffer handle is signaled when there is unread input, such as a keystroke or mouse button press. Process and thread handles are signaled when the process or thread terminates. This allows a process, for example, to create a child process and then block its own execution until the new process has terminated.

Other objects are useful in protecting shared resources from simultaneous access. For example, multiple threads can each have a handle to a mutex object. Before accessing a shared resource, the threads must call one of the wait functions to wait for the state of the mutex to be signaled. When the mutex becomes signaled, only one waiting thread is released to access the resource. The state of the mutex is immediately reset to not signaled so any other waiting threads remain blocked. When the thread is finished with the resource, it must set the state of the mutex to signaled to free other threads to access the resource.

For the threads of a single process, critical-section objects provide another (and slightly more efficient) means of synchronization. A critical section is used like a mutex to enable only one thread at a time to use the protected resource. A thread uses the EnterCriticalSection function to request ownership of a critical section. If it is already owned by another thread, the requesting thread is blocked. After it receives ownership, the thread is free to use the protected resource. The execution of the other threads of the process is not affected unless they attempt to enter the same critical section.

The WaitForInputIdle function makes a thread wait until a specified process is initialized and waiting for user input with no input pending. This can be useful for synchronizing parent and child processes, because CreateProcess returns without waiting for the child process to complete its initialization.

For more about critical sections and synchronization, see Synchronization.

+$#A Thread Local Storage

All threads of a process share the virtual address space and the global variables of that process. Sometimes it is desirable to have static storage that is local to each thread. Of course, the local variables of a thread function are local to each thread that runs the function. But when a thread invokes another function, the static or global variables used by that function store the same value for all threads. With thread local storage (TLS), one thread allocates an index that can be used by any thread of the process to store and retrieve a different value for each thread.

The following is a typical TLS scenario:

1. The TlsAlloc function is used during process or dynamic-link library (DLL) initialization to allocate a TLS index.

2. Each thread that needs to use the TLS index allocates dynamic storage, then uses the TlsSetValue function to associate the index with a pointer to the dynamic storage.

3. When a thread needs to access its storage, it specifies the TLS index in a call to the TlsGetValue function to retrieve the pointer.

4. Each thread frees the dynamic storage that it has associated with a TLS index when it is no longer needed. When all threads have finished using a TLS index, the TlsFree function frees the index.

The constant TLS_MINIMUM_AVAILABLE defines the minimum number of TLS indexes available in each process. This minimum is guaranteed to be at least 64 for all systems.

TLS can be useful with DLLs. The DllEntryPoint function performs the initial TLS operations in the context of the process or thread the DLL is attaching to or detaching from. When the DLL attaches to a new process, the entry-point function calls TlsAlloc to allocate a TLS index for that process. When the DLL attaches to a new thread of that process, the entry-point function allocates dynamic memory for that thread and uses TlsSetValue to save private data to the index. The DLL stores the TLS index in a global variable that is private to each attached process. The functions of the DLL can use the TLS index in a call to TlsGetValue to access the private data for the calling thread.

For an example illustrating the use of thread local storage, see Using Thread Local Storage.

+$#A Terminating a Thread

A thread runs until one of the following events occurs:

• It calls the ExitThread function.

• The thread function returns. This results in an implicit call to the ExitProcess function for a primary thread, or to the ExitThread function for other threads.

• ExitProcess is invoked explicitly or implicitly by any thread of the process.

• The TerminateThread function is called with a handle to the thread.

• The TerminateProcess function is called with a handle to the thread's process.

When a thread terminates, the state of the thread object changes to signaled, releasing any other threads that had been waiting for the thread to terminate. Then its termination status changes from STILL_ACTIVE to the exit value of the thread.

If a thread is terminated by ExitThread, the entry-point function of all attached DLLs is invoked with a value indicating that the thread is detaching from the DLL. If a thread is terminated by ExitProcess, the DLL entry-point functions are invoked once, to indicate that the process is detaching. DLLs are not notified when a thread is terminated by TerminateThread or TerminateProcess. For more information about DLLs, see Dynamic-Link Libraries.

The GetExitCodeThread function returns the termination status of a thread. While a thread is executing, its termination status is STILL_ACTIVE. When a thread terminates, its termination status is the exit value of the thread, which is the value specified in the call to ExitThread, ExitProcess, or TerminateThread, or the value returned by the thread function.

+$#A Multiple Threads and Graphics Device Interface Objects

To enhance performance, access to graphics device interface (GDI) objects (such as palettes, device contexts (DC), regions, and the like) is not serialized. This creates a potential danger for processes that have multiple threads sharing these objects. For example, if one thread deletes a GDI object while another thread is using it, the results are unpredictable. This danger can be avoided simply by not sharing GDI objects. If sharing is unavoidable (or desirable), the application must provide its own mechanisms for synchronizing access. For more information about synchronizing access, see Synchronizing Execution of Multiple Threads.

+$#A Creating Processes

The CreateProcess function creates a new process, which runs independently of the creating process. The function provides two methods for identifying the program to be executed. The most common method is to specify the image file's path and filename. If the lpszImageName parameter is NULL, the program is identified in the lpszCommandLine parameter.

If CreateProcess succeeds, it returns a PROCESS_INFORMATION structure containing handles and identification numbers for the new process and its primary thread. The thread and process handles are created with full access rights, although access can be restricted if a security descriptor is specified.

+$#A Inheritance

A child process can inherit a number of properties and resources from its parent process. You can also prevent a child process from inheriting anything from its parent process. The following can be inherited:

• Any inheritable open handle returned by the CreateFile function. This includes handles to files, console input buffers, console screen buffers, named pipes, serial communication devices, and mailslots.

• Inheritable open handles to process, thread, mutex, event, semaphore, named pipe, anonymous pipe, and file mapping objects.

• Environment variables.

• The current directory.

• The console of parent process, unless the process is detached or a new console is created. A child console process also inherits the parent's standard handles, as well as access to the input buffer and the active screen buffer. For more about console processes, see Consoles and Character-Mode Support.

The fInheritHandles parameter of CreateProcess determines whether the inheritable handles of the parent process are inherited by the child process. The inheritability of the handles is specified in the functions used to create, open, or duplicate the handles. This allows a child process to inherit some of its parent's handles, while not inheriting others. For each of these object types there is a creation function (such as CreateProcess or CreateFile) that takes a security attributes argument via a flag determines whether the handle can be inherited. If the security attributes argument is NULL, the handle is not inherited.

Some of these objects have an open function (such as OpenMutex or OpenEvent) with an inherit flag parameter that determines inheritability.

Any of these handles can be duplicated using the DuplicateHandle function by which you can specify whether the handle can be inherited. This can be useful if a process has an inheritable open handle that you do not want to be inherited by the child process. In this case, use DuplicateHandle to open a noninheritable duplicate of the handle, then use the CloseHandle function to close the inheritable handle. DuplicateHandle can also open an inheritable duplicate of a noninheritable handle.

An inherited handle has the same value and access privileges as the parent process's handle. To use a handle, the child process must retrieve the handle and know the object to which it refers. For most handles, the parent process must communicate this information to the child process. This can be done using the command line or any of the other methods for interprocess communication (such as pipes or shared memory).

An inherited handle refers to the same object as the parent process's handle does. When one process changes the state of the object, the change affects both processes. With file handles, for example, if one process uses a handle to read from a file, the current file position is moved for all processes using the same handle.

A child process inherits the environment variables and current directory of its parent process by default. However, CreateProcess enables the parent process to specify a different current directory or block of environment variables. For more information about environment variables, see Environment Variables

The child process does not inherit the following:

• Priority class.

• Memory handles.

• Pseudohandles, as in the handles returned by the GetCurrentProcess or GetCurrentThread function. These handles are valid only for the calling process. For more information about these functions, see Process and Thread Handles and Identifiers.

• DLL module handles returned by the LoadLibrary function.

• GDI or USER handles, such as HBITMAP or HMENU. Refer to WINDEF.H for a complete list of these handles.

+$#A Environment Variables

Every process has an environment block that contains a set of environment variables and their values. The command processor for Microsoft® Windows NT™ (called CMD.EXE) provides the set command to display its environment block or to create new environment variables. Programs started by the command processor inherit the command processor's environment variables.

By default, a child process inherits the environment variables of its parent process. However, you can specify a different environment for the child process by creating a new environment block and passing a pointer to it as a parameter to the CreateProcess function.

The GetEnvironmentStrings function returns a pointer to the environment block of the calling process. This should be treated as a read-only block; do not modify it directly. Instead, use the SetEnvironmentVariable function to change an environment variable. The GetEnvironmentVariable function determines whether a specified variable is defined in the environment of the calling process, and, if so, what its value is. For more information about environments, see the examples in Changing Environment Variables.

+$#A Setting Window Properties Using STARTUPINFO

A parent process can specify properties associated with the main window of its child process. A pointer to a STARTUPINFO structure is passed as a parameter to the CreateProcess function. The members of this structure can be used to specify more than a dozen different characteristics of the child's main window. The dwFlags member contains a bit field that determines whether the other members of the structure are used. This allows the creating process to specify values for any subset of the window properties. Default values are used for the properties not specified by dwFlags. The dwFlags member can also force a feedback cursor to be displayed during the initialization of the new process. A process can use the GetStartupInfo function to retrieve the STARTUPINFO structure specified when it was created.

For GUI processes, the STARTUPINFO structure specifies the default values to be used the first time the new process calls the CreateWindow and ShowWindow functions to create and display an overlapped window. The following default values can be specified:

• The width and height, in pixels, of the window created by CreateWindow.

• The location in screen coordinates of the window created by CreateWindow.

• The nCmdShow parameter of ShowWindow.

For console processes, use the STARTUPINFO specifications only when a new console is created either by CreateProcess with CREATE_NEW_CONSOLE specified, or by the AllocConsole function. For more about console processes, see Consoles and Character-Mode Support. STARTUPINFO can specify the following console window properties:

• The size, in character cells, and the location in screen coordinates of the new console window.

• The size, in character cells, of the new console's screen buffer.

• The text and background color attributes of the new console's screen buffer.

• The title of the new console's window.

+$#A Terminating a Process

A process terminates when one of the following occurs:

• Any thread of the process calls the ExitProcess function.

• The primary thread of the process returns. This results in an implicit call to ExitProcess.

• The last thread of the process terminates.

• The TerminateProcess function is called with a handle to the process.

When a process terminates, the following events occur:

• Open handles to files or other resources are closed automatically. Note that the objects referenced by these handles exist until all open handles are closed. This means that an object remains valid after a process closes, if another process has a handle to it.

• The state of the process object becomes signaled, satisfying any threads that are waiting for the process to terminate. For more about synchronization, see Synchronizing Execution of Multiple Threads.

• The termination status of the process changes from STILL_ACTIVE to the exit value of the process.

Child processes are not automatically terminated when a parent process terminates.

ExitProcess is the preferred way to terminate a process, because it notifies all attached DLLs of the termination, and it ensures that all threads of the process are terminated. If a process is terminated by ExitProcess, the entry-point function of all attached DLLs is invoked with a value indicating that the process is detaching from the DLL. DLLs are not notified when a process is terminated by TerminateProcess. For more information about DLLs, see Dynamic-Link Libraries.

When a process is terminated by ExitProcess or TerminateProcess, all threads of the process are terminated immediately with no chance to run additional code (except for the DLL process detach code if ExitProcess is called). This means that the process does not execute code in termination handler blocks such as the finally block of the try-finally termination handler supported by Microsoft compilers. For more information about handlers, see Structured Exception Handling.

The primary thread of a process differs from the other threads of a process in that ExitProcess (rather than the ExitThread function) is invoked implicitly when a primary thread returns. Alternatively, the primary thread can avoid terminating other threads by explicitly calling ExitThread before it exits. In this case, one of the remaining threads explicitly calls ExitProcess, to ensure that all threads of the process are terminated. For example, it is possible for an attached DLL to create a thread that does not terminates unless ExitProcess is invoked.

ExitProcess can be invoked implicitly by the system. For most unhandled exceptions, the system's default action is to call ExitProcess. For console processes, ExitProcess is called by the default handler function that can be invoked when a CTRL+C or CTRL+BREAK signal is received. For more about console application, see Consoles and Character-Mode Support. Note that this default handling of console interrupt signals can terminate parent and child processes, because all processes attached to a console receive the signal. An interrupt signal received by a console window is propagated only to attached console processes. Detached processes and processes that have created new console windows are not affected. GUI processes are not affected by CTRL+C or CTRL+BREAK signals.

The GetExitCodeProcess function returns the termination status of a process. While a process is running, its termination status is STILL_ACTIVE. When a process terminates, its termination status is the exit value of the process, which is the value specified in the call to ExitProcess or TerminateProcess, or the value returned by the main or WinMain function of the process. If a process is terminated in some other way (for example, by a fatal, unhandled exception or a CTRL+C signal) the process's termination value is the exception value that caused the termination. The process's exit value is also used as the exit value of all of the threads that had not previously terminated.

+$#A Process and Thread Handles and Identifiers

When a new thread is created by the CreateThread or CreateRemoteThread function, a handle of the thread is returned. Similarly, when a new process is created by the CreateProcess function, handles of the new process and its primary thread are returned. These handles are created with full access rights, and ⎯ subject to security access checking ⎯ can be used in any of the functions that accepts a thread or process handle. These handles can be inherited by child processes, depending on the inheritance flag specified when they are created. The handles are valid until closed, even after the process or thread they represent is terminated.

The OpenProcess function opens a handle to a process specified by its process identifier. OpenProcess enables you to specify the handle's access rights and inheritability.

A process can use the GetCurrentProcess function to retrieve a pseudohandle of its own process object; a thread can use the GetCurrentThread function to retrieve a pseudohandle to its own thread object. These pseudohandles are valid only for the calling process; they cannot be inherited or duplicated for use by other processes.

The functions that create a process or thread also return an identifier that uniquely identifies the process or thread throughout the system. A process can use the GetCurrentProcessId function to get its own process identifier. Similarly, a thread can use the GetCurrentThreadId function to get its own thread identifier. The identifiers are valid from the time the thread or process is created until the thread or process terminates.

+$#A Using Processes and Threads

• Using a multithreaded multiple document interface application

• Creating a child process with redirected input and output

• Changing environment variables

• Using thread local storage

+$#A Using a Multithreaded Multiple Document Interface Application

The following example shows how to use multiple threads in a multiple document interface (MDI) process. The process has a single main window, but can have any number of child windows. The primary thread of the process performs initialization and also handles messages to all windows through the application-defined MainWndProc and ChildWndProc functions.

Each time a child window is created, a new thread is also created. In this example, the new thread only checks continually to see if it is time to terminate. In a real application, the new thread could be used to draw the contents of the window or to carry out the functions associated with the window.

The ThreadFunc function is specified in the CreateThread function as the code for the new thread to execute. The handle of the child window associated with the thread is passed as a parameter to ThreadFunc. The child window's handle is also a parameter to ChildWndProc when a message is dispatched to the child window. A handle is necessary for any communication between a child window and its corresponding thread. Both ThreadFunc and ChildWndProc use the window handle in the SetWindowLong function to access the 32-bit value that is reserved for application use in each window structure. In this example, the 32-bit value is a termination flag. When ChildWndProc gets the WM_CLOSE message, it sets the flag; ThreadFunc checks the flag each time through its loop.

This example shows how to use normal priority for the primary thread and below-normal priority for the other threads. Because the primary thread passes all messages for both the main window and the child windows, its higher relative priority ensures responsiveness to user input. (Even when the child threads are left at normal priority, the scheduler automatically boosts the priority of the input thread when necessary.)

When the user terminates the process by closing the main window, a global Boolean parameter is set to terminate the child threads. At this point, the primary thread waits for each child thread to terminate before proceeding. This is necessary only if you want the threads to clean up, save changes to a file, or detach from DLLs before closing. Without waiting, the primary thread terminates the process by returning before the lower priority threads were able to run any additional code.

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#define MM_NEWWIN 8001

typedef struct _PTHREADLIST {

HANDLE hThread;

LPVOID lpvNext;

} THREADLIST, *PTHREADLIST;

/* Globals */

HANDLE hModule; /* handle to .EXE file for this process */

HWND hwndMain = NULL; /* handle to main window */

BOOL fKillAll = FALSE; /* sets TRUE to terminate all threads */

PTHREADLIST pHead = NULL; /* head of linked list of thread info. */

/* Forward declarations */

BOOL InitializeApp(VOID);

LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);

DWORD ThreadFunc(HWND);

VOID AddThreadToList(HANDLE);

VOID ErrorExit(LPSTR);

/* Primary thread: Initialize the application and dispatch messages. */

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,

LPSTR lpszCmdLn, int nShowCmd) {

MSG msg;

hModule = GetModuleHandle(NULL);

if (! InitializeApp())

ErrorExit("InitializeApp failure!");

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return 1;

UNREFERENCED_PARAMETER(hInst);

UNREFERENCED_PARAMETER(hPrevInst);

UNREFERENCED_PARAMETER(lpszCmdLn);

UNREFERENCED_PARAMETER(nShowCmd);

}

/* Register window classes and create the main window. */

BOOL InitializeApp(VOID) {

HMENU hmenuMain, hmenuPopup;

WNDCLASS wc;

/* Register a window class for the main window. */

wc.style = CS_OWNDC;

wc.lpfnWndProc = MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hModule;

wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);

wc.lpszMenuName = NULL;

wc.lpszClassName = "MainWindowClass";

if (! RegisterClass(&wc))

return FALSE;

/* Register a window class for child windows. */

wc.lpfnWndProc = ChildWndProc;

wc.lpszClassName = "ThreadWindowClass";

if (! RegisterClass(&wc))

return FALSE;

/* Create a menu for the main window. */

hmenuMain = CreateMenu();

hmenuPopup = CreateMenu();

if (! AppendMenu(hmenuPopup, MF_STRING, MM_NEWWIN, "&New Window"))

return FALSE;

if (! AppendMenu(hmenuMain, MF_POPUP, (UINT) hmenuPopup, "&Threads"))

return FALSE;

/* Create the main window. */

hwndMain = CreateWindow("MainWindowClass", "Primary Window",

WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_THICKFRAME |

WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN |

WS_VISIBLE | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, NULL, hmenuMain, hModule,

NULL);

if (hwndMain == NULL)

return FALSE;

/* Set the initial focus. */

SetFocus(hwndMain);

return TRUE;

}

/* Main window procedure: Handle messages for the main window. */

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uiMessage,

WPARAM wParam, LPARAM lParam) {

static HWND hwndClient;

static DWORD dwCount = 1;

CLIENTCREATESTRUCT ccsClientCreate;

HWND hwndChildWnd;

DWORD IDThread;

PTHREADLIST pNode;

switch (uiMessage) {

/* Create a client window to receive child window messages. */

case WM_CREATE:

ccsClientCreate.hWindowMenu = NULL;

ccsClientCreate.idFirstChild = 1;

hwndClient = CreateWindow("MDICLIENT", NULL,

WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 0, 0,

hwnd, NULL, hModule, (LPVOID)&ccsClientCreate);

return 0L;

/*

* Close the main window. First set fKillAll to TRUE to

* terminate all threads. Then wait for the threads to exit

* before passing a close message to a default handler. If you

* don't wait for threads to terminate, process terminates

* with no chance for thread cleanup.

*/

case WM_CLOSE:

fKillAll = TRUE;

pNode = pHead;

while (pNode) {

DWORD dwRes;

SetThreadPriority(pNode->hThread,

THREAD_PRIORITY_HIGHEST);

dwRes = WaitForSingleObject(pNode->hThread,

INFINITE);

pNode = (PTHREADLIST) pNode->lpvNext;

}

return DefFrameProc(hwnd, hwndClient, uiMessage,

wParam, lParam);

/* Terminate the process. */

case WM_DESTROY:

PostQuitMessage(0);

return 0L;

/* Handle the menu commands. */

case WM_COMMAND:

switch (LOWORD(wParam)) {

/* Create a child window and start a thread for it. */

case MM_NEWWIN: {

HANDLE hThrd;

MDICREATESTRUCT mdicCreate;

TCHAR tchTitleBarText[32];

LONG lPrev;

sprintf(tchTitleBarText, "Thread Window %d", dwCount);

mdicCreate.szClass = "ThreadWindowClass";

mdicCreate.szTitle = tchTitleBarText;

mdicCreate.hOwner = hModule;

mdicCreate.x = mdicCreate.y =

mdicCreate.cx = mdicCreate.cy = CW_USEDEFAULT;

mdicCreate.style = mdicCreate.lParam = 0L;

/*

* Send a "create child window" message to the

* client window.

*/

hwndChildWnd = (HWND) SendMessage(hwndClient,

WM_MDICREATE, 0L, (LONG)&mdicCreate);

if (hwndChildWnd == NULL)

ErrorExit("Failed in Creating Thread Window!");

/*

* Window structure used to pass a quit message to

* the thread.

*/

lPrev = SetWindowLong(hwndChildWnd, GWL_USERDATA, 0);

/* Create suspended, alter priority before resuming. */

hThrd = CreateThread(NULL, /* no security attributes */

0, /* use default stack size */

(LPTHREAD_START_ROUTINE) ThreadFunc,

(LPVOID)hwndChildWnd, /* param to thrd func */

CREATE_SUSPENDED, /* creation flag */

&IDThread); /* returns thread id */

if (hThrd == NULL)

ErrorExit("CreateThread Failed!");

AddThreadToList(hThrd);

dwCount++;

/*

* Set the priority lower than the primary (input)

* thread, so that the process is responsive to user

* input. Then resume the thread.

*/

if (! SetThreadPriority(hThrd,

THREAD_PRIORITY_BELOW_NORMAL))

ErrorExit("SetThreadPriority failed!");

if ((ResumeThread(hThrd)) == -1)

ErrorExit("ResumeThread failed!");

return 0L;

}

default:

return DefFrameProc(hwnd, hwndClient, uiMessage,

wParam, lParam);

}

default:

return DefFrameProc(hwnd, hwndClient, uiMessage,

wParam, lParam);

}

}

/* Handle messages for the child windows. */

LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT uiMessage, WPARAM wParam,

LPARAM lParam) {

LONG lPrevLong;

switch (uiMessage) {

/* Use a window structure to pass "close" message to thread. */

case WM_CLOSE:

lPrevLong = SetWindowLong(hwnd, GWL_USERDATA, 1);

return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);

case WM_DESTROY:

return 0L;

default:

return DefMDIChildProc(hwnd, uiMessage, wParam, lParam);

}

}

/*

* Each child window has a thread that can be used to perform tasks

* associated with that window--for example, drawing its contents.

*/

DWORD ThreadFunc(HWND hwnd) {

LONG lKillMe = 0L;

while (TRUE) {

lKillMe = GetWindowLong(hwnd, GWL_USERDATA);

if (fKillAll || lKillMe) break;

/* Perform tasks. */

}

/* Perform actions needed before thread termination. */

return 0;

}

VOID AddThreadToList(HANDLE hThread) {

PTHREADLIST pNode;

pNode = (PTHREADLIST) LocalAlloc(LPTR, sizeof(PTHREADLIST));

if (pNode == NULL)

ErrorExit("malloc Failed!");

pNode->hThread = hThread;

pNode->lpvNext = (LPVOID) pHead;

pHead = pNode;

}

VOID ErrorExit(LPSTR lpszMessage) {

MessageBox(hwndMain, lpszMessage, "Error", MB_OK);

ExitProcess(0);

}

+$#A Creating a Child Process with Redirected Input and Output

The following example shows the creation of a child process by a console process and illustrates a technique for using anonymous pipes to redirect the child process's standard input and output. If you have a program that can read its input from the standard input (STDIN) or write its results to the standard output (STDOUT), redirected I/O enables you to execute the program as a child process while controlling its input and receiving its output.

The example shows how to use a SECURITY_ATTRIBUTES structure to create handles that can be inherited by the child process. The structure is used in the CreatePipe function to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the child's standard output. These pipe handles are specified in the SetStdHandle function, which makes them the standard handles inherited by the child process. After the child process is created, SetStdHandle is used again to restore the original standard handles for the parent process.

The parent process uses the other ends of the pipes to write to the child's input and read the child's output. The handles to these ends of the pipe are also inheritable. However, the handle must not be inherited. Before creating the child process, the parent process must use DuplicateHandle to create a noninheritable duplicate of the application-defined hChildStdinWr global variable. It then uses CloseHandle to close the inheritable handle. This leaves the parent process with a pipe handle it can use, but which the child process does not inherit. For more information about pipes, see Pipes.

The following is the parent process.

#include <stdio.h>

#include <windows.h>

#define BUFSIZE 4096

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,

hChildStdoutRd, hChildStdoutWr,

hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID);

VOID WriteToPipe(VOID);

VOID ReadFromPipe(VOID);

VOID ErrorExit(LPTSTR);

VOID ErrMsg(LPTSTR, BOOL);

DWORD main(int argc, char *argv[]) {

SECURITY_ATTRIBUTES saAttr;

BOOL fSuccess;

/* Set the bInheritHandle flag so pipe handles are inherited. */

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

saAttr.bInheritHandle = TRUE;

saAttr.lpSecurityDescriptor = NULL;

/*

* The steps for redirecting child's STDOUT:

* 1. Save current STDOUT, to be restored later.

* 2. Create anonymous pipe to be STDOUT for child.

* 3. Set STDOUT of parent to be write handle of pipe, so

* it is inherited by child.

*/

/* Save the handle to the current STDOUT. */

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/* Create a pipe for the child's STDOUT. */

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))

ErrorExit("Stdout pipe creation failed\n");

/* Set a write handle to the pipe to be STDOUT. */

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))

ErrorExit("Redirecting STDOUT failed");

/*

* The steps for redirecting child's STDIN:

* 1. Save current STDIN, to be restored later.

* 2. Create anonymous pipe to be STDIN for child.

* 3. Set STDIN of parent to be read handle of pipe, so

* it is inherited by child.

* 4. Create a noninheritable duplicate of write handle,

* and close the inheritable write handle.

*/

/* Save the handle to the current STDIN. */

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

/* Create a pipe for the child's STDIN. */

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))

ErrorExit("Stdin pipe creation failed\n");

/* Set a read handle to the pipe to be STDIN. */

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))

ErrorExit("Redirecting Stdin failed");

/* Duplicate the write handle to the pipe so it is not inherited. */

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,

GetCurrentProcess(), &hChildStdinWrDup, 0,

FALSE, /* not inherited */

DUPLICATE_SAME_ACCESS);

if (! fSuccess)

ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdinWr);

/* Now create the child process. */

if (! CreateChildProcess())

ErrorExit("Create process failed");

/* After process creation, restore the saved STDIN and STDOUT. */

if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))

ErrorExit("Re-redirecting Stdin failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))

ErrorExit("Re-redirecting Stdout failed\n");

/* Get a handle to the parent's input file. */

if (argc > 1)

hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

else

hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)

ErrorExit("no input file\n");

/* Write to pipe that is the standard input for a child process. */

WriteToPipe();

/* Read from pipe that is the standard output for child process. */

ReadFromPipe();

return 0;

}

BOOL CreateChildProcess() {

PROCESS_INFORMATION piProcInfo;

STARTUPINFO siStartInfo;

/* Set up members of STARTUPINFO structure. */

siStartInfo.cb = sizeof(STARTUPINFO);

siStartInfo.lpReserved = NULL;

siStartInfo.lpReserved2 = NULL;

siStartInfo.cbReserved2 = 0;

siStartInfo.lpDesktop = NULL;

siStartInfo.dwFlags = 0;

/* Create the child process. */

return CreateProcess(NULL,

"child", /* command line */

NULL, /* process security attributes */

NULL, /* primary thread security attributes */

TRUE, /* handles are inherited */

0, /* creation flags */

NULL, /* use parent's environment */

NULL, /* use parent's current directory */

&siStartInfo, /* STARTUPINFO pointer */

&piProcInfo); /* receives PROCESS_INFORMATION */

VOID WriteToPipe(VOID) {

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

/* Read from a file and write its contents to a pipe. */

for (;;) {

if (! ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ||

dwRead == 0) break;

if (! WriteFile(hChildStdinWrDup, chBuf, dwRead,

&dwWritten, NULL)) break;

}

/* Close the pipe handle so the child stops reading. */

if (! CloseHandle(hChildStdinWrDup))

ErrorExit("Close pipe failed\n");

}

VOID ReadFromPipe(VOID) {

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

/*

* Close the write end of the pipe before reading from the

* read end of the pipe.

*/

if (! CloseHandle(hChildStdoutWr))

ErrorExit("Closing handle failed");

/* Read output from child, and write it to parent's STDOUT. */

for (;;) {

if (! ReadFile(hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) ||

dwRead == 0) break;

if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))

break;

}

}

VOID ErrorExit (LPTSTR lpszMessage) {

fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);

}

Here's the child process:

#include <windows.h>

#define BUFSIZE 4096

VOID main(VOID) {

CHAR chBuf[BUFSIZE];

DWORD dwRead, dwWritten;

HANDLE hStdin, hStdout;

BOOL fSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

hStdin = GetStdHandle(STD_INPUT_HANDLE);

if ((hStdout == INVALID_HANDLE_VALUE) ||

(hStdin == INVALID_HANDLE_VALUE))

ExitProcess(1);

for (;;) {

/* Read from standard input. */

fSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

if (! fSuccess || dwRead == 0)

break;

/* Write to standard output. */

fSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

if (! fSuccess)

break;

}

}

+$#A Changing Environment Variables

The environment block consists of a null-terminated block of null-terminated strings (that is, two null bytes at the end of the block), where each string is in the form:

name=value

Because the equal sign is a separator, it must not be used in the name of an environment variable. The following example creates a new environment block to pass to a child process.

TCHAR tchNewEnv[BUFSIZE];

LPTSTR lpszCurrentVariable;

BOOL fSuccess;

/* Copy name=value environment variable pairs into environment block. */

lpszCurrentVariable = tchNewEnv;

if (lstrcpy(lpszCurrentVariable, "OperatingSystem=Windows") == NULL)

ErrorExit("lstrcpy failed");

lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;

if (lstrcpy(lpszCurrentVariable, "API=Win32") == NULL)

ErrorExit("lstrcpy failed");

/* Terminate the block with a NULL byte. */

lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;

*lpszCurrentVariable = '\0';

/* Create the child process, specifying a new environment block. */

fSuccess = CreateProcess(NULL, "childenv", NULL, NULL, TRUE, 0,

(LPVOID) tchNewEnv, /* new environment block */

NULL, &siStartInfo, &piProcInfo);

if (! fSuccess)

ErrorExit("CreateProcess failed");

If you want the child process to inherit most of the parent's environment with only a few changes, save the current values, make changes for the child to inherit, and then restore the saved values for example.

LPTSTR lpszOldValue;

TCHAR tchBuf[BUFSIZE];

BOOL fSuccess;

/*

* lpszOldValue gets current value of "varname", or NULL if "varname"

* environment variable does not exist. Set "varname" to new value,

* create child process, then use SetEnvironmentVariable to restore

* original value of "varname". If lpszOldValue is NULL, the "varname"

* variable will be deleted.

*/

lpszOldValue = ((GetEnvironmentVariable("varname",

tchBuf, BUFSIZE) > 0) ? tchBuf : NULL);

/* Set a value for the child to inherit. */

if (! SetEnvironmentVariable("varname", "newvalue"))

ErrorExit("SetEnvironmentVariable failed");

/* Create a child process. */

fSuccess = CreateProcess(NULL, "childenv", NULL, NULL, TRUE, 0,

NULL, /* inherit parent's environment */

NULL, &siStartInfo, &piProcInfo);

if (! fSuccess)

ErrorExit("CreateProcess failed");

/* Restore the parent's environment. */

if (! SetEnvironmentVariable("varname", lpszOldValue))

ErrorExit("SetEnvironmentVariable failed");

The following example, taken from a console process, prints the contents of the process's environment block.

LPTSTR lpszVariable;

LPVOID lpvEnv;

/* Get a pointer to the environment block. */

lpvEnv = GetEnvironmentStrings();

/*

* Variable strings are separated by NULL byte, and the block is

* terminated by a NULL byte.

*/

for (lpszVariable = (LPTSTR) lpvEnv; *lpszVariable; lpszVariable++) {

while (*lpszVariable)

putchar(*lpszVariable++);

putchar('\n');

}

+$#A Using Thread Local Storage

Thread local storage (TLS) enables multiple threads of the same process to use an index allocated by the TlsAlloc function to store and retrieve a value that is local to the thread. In this example, an index is allocated when the process starts. When each thread starts, it allocates a block of dynamic memory and stores a pointer to this memory by using the TLS index. The TLS index is used by the locally defined CommonFunc function to access the data local to the calling thread. Before each thread terminates, it releases its dynamic memory.

#include <stdio.h>

#include <windows.h>

#define THREADCOUNT 4

DWORD dwTlsIndex;

VOID ErrorExit(LPTSTR);

VOID CommonFunc(VOID) {

LPVOID lpvData;

/* Retrieve a data pointer for the current thread. */

lpvData = TlsGetValue(dwTlsIndex);

if ((lpvData == 0) && (GetLastError() != 0))

ErrorExit("TlsGetValue error");

/*

* Use the data stored for the current thread.

*/

printf("common: thread %d: lpvData=%lx\n",

GetCurrentThreadId(), lpvData);

Sleep(5000);

}

DWORD ThreadFunc(VOID) {

LPVOID lpvData;

/* Initialize the TLS index for this thread. */

lpvData = (LPVOID) LocalAlloc(LPTR, 256);

if (! TlsSetValue(dwTlsIndex, lpvData))

ErrorExit("TlsSetValue error");

printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);

CommonFunc();

/* Release the dynamic memory before the thread returns. */

lpvData = TlsGetValue(dwTlsIndex);

if (lpvData != 0)

LocalFree((HLOCAL) lpvData);

return 0;

}

DWORD main(VOID) {

DWORD IDThread;

HANDLE hThread[THREADCOUNT];

int i;

/* Allocate a TLS index. */

if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF)

ErrorExit("TlsAlloc failed");

/* Create multiple threads. */

for (i = 0; i < THREADCOUNT; i++) {

hThread[i] = CreateThread(NULL,/* no security attributes */

0, /* use default stack size */

(LPTHREAD_START_ROUTINE) ThreadFunc, /* thread function */

NULL, /* no thread function argument */

0, /* use default creation flags */

&IDThread); /* returns thread identifier */

/* Check the return value for success. */

if (hThread[i] == NULL)

ErrorExit("CreateThread error\n");

}

for (i = 0; i < THREADCOUNT; i++)

WaitForSingleObject(hThread[i], INFINITE);

return 0;

}

VOID ErrorExit (LPTSTR lpszMessage) {

fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);

}

+$#A Process and Thread Functions

Following are the functions used with processes and threads.

AttachThreadInput
CommandLineToArgvW
CreateProcess
CreateRemoteThread
CreateThread
ExitProcess
ExitThread
FreeEnvironmentStrings
GetCommandLine
GetCurrentProcess
GetCurrentProcessId
GetCurrentThread
GetCurrentThreadId
GetEnvironmentStrings
GetEnvironmentVariable
GetExitCodeProcess
GetExitCodeThread
GetPriorityClass
GetProcessAffinityMask
GetProcessShutdownParameters
GetProcessTimes
GetProcessVersion
GetProcessWorkingSetSize
GetStartupInfo
GetThreadPriority
GetThreadTimes
OpenProcess
ResumeThread
SetEnvironmentVariable
SetPriorityClass
SetProcessShutdownParameters
SetThreadAffinityMask
SetThreadPriority
Sleep
SleepEx
SetProcessWorkingSetSize
SuspendThread
TerminateProcess
TerminateThread
TlsAlloc
TlsFree
TlsGetValue
TlsSetValue
WaitForInputIdle
WinExec

Obsolete or Deleted Functions

Yield

Créé avec HelpNDoc Personal Edition: Éditeur de documentation CHM facile


Progress Bars.rtf

+$#AK Progress Bars

A progress bar is a window that an application can use to indicate the progress of a lengthy operation. It consists of a rectangle that is gradually filled, from left to right, with the system highlight color as an operation progresses. The following illustration shows a progress bar positioned along the bottom of a window.

{ewc msdncd, EWGraphic, group10601 0 /a "SDK_01.bmp"}

+$#A About Progress Bars

You create a progress bar by using the CreateWindowEx function, specifying the PROGRESS_CLASS window class. This window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls function first.

+$#A Range and Current Position

A progress bar has a range and a current position. The range represents the entire duration of the operation, and the current position represents the progress that the application has made toward completing the operation. The window procedure uses the range and the current position to determine the percentage of the progress bar to fill with the highlight color and to determine the text, if any, to display within the progress bar. Because the range and current position values are expressed as unsigned integers, the highest possible range or current position value is 65,535.

The minimum value in the range can be from 0 to 65,535. Likewise, the maximum value can be from 0 to 65,535. If you do not set the range values, the system sets the minimum value to 0 and the maximum value to 100. You can adjust the range to convenient integers by using the PBM_SETRANGE message.

A progress bar provides several messages that you can use to set the current position. The PBM_SETPOS message sets the position to a given value. The PBM_DELTAPOS message advances the position by adding a specified value to the current position.

The PBM_SETSTEP message allows you to specify a step increment for a progress bar. Subsequently, whenever you send the PBM_STEPIT message to the progress bar, the current position advances by the specified increment. By default, the step increment is set to 10.

+$#A Default Progress Bar Message Processing

This section describes the window messages handled by the window procedure for the PROGRESS_CLASS window class.

Message | Processing performed
WM_CREATE | Allocates and initializes an initial structure.
WM_DESTROY | Frees all resources associated with the progress bar.
WM_ERASEBKGND | Draws the background and borders of the progress bar.
WM_GETFONT | Returns the handle of the font that the progress bar is currently using to draw text.
WM_PAINT | Draws the progress bar.
WM_SETFONT | Saves the handle of the new font and returns the handle of the previous font.

+$#A Using Progress Bars

The following example shows how to use a progress bar to indicate the progress of a lengthy file-parsing operation. The example creates a progress bar and positions it along the bottom of the parent window's client area. The height of the progress bar is based on the height of the arrow bitmap used in a scroll bar. The range is based on the size of the file divided by 2048, which is the size of each "chunk" of data read from the file. The example also sets an increment and advances the current position of the progress bar by the increment after parsing each chunk.

// ParseALargeFile - parses a large file and uses a progress bar to

// indicate the progress of the parsing operation.

// Returns TRUE if successful or FALSE otherwise.

// hwndParent - parent window of the progress bar

// lpszFileName - name of the file to parse

//

// Global variable

// g_hinst - instance handle

extern HINSTANCE g_hinst;

BOOL ParseALargeFile(HWND hwndParent, LPSTR lpszFileName)

{

RECT rcClient; // client area of parent window

int cyVScroll; // height of scroll bar arrow

HWND hwndPB; // handle of progress bar

HANDLE hFile; // handle of file

DWORD cb; // size of file and count of bytes read

LPCH pch; // address of data read from file

LPCH pchTmp; // temporary pointer

// Ensure that the common control DLL is loaded and create a

// progress bar along the bottom of the client area of the

// parent window. Base the height of the progress bar on

// the height of a scroll bar arrow.

InitCommonControls();

GetClientRect(hwndParent, &rcClient);

cyVScroll = GetSystemMetrics(SM_CYVSCROLL);

hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPSTR) NULL,

WS_CHILD | WS_VISIBLE, rcClient.left,

rcClient.bottom - cyVScroll,

rcClient.right, cyVScroll,

hwndParent, (HMENU) 0, g_hinst, NULL);

// Open the file for reading, and retrieve the size of the file.

hFile = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ,

(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);

if (hFile == (HANDLE) INVALID_HANDLE_VALUE)

return FALSE;

cb = GetFileSize(hFile, (LPDWORD) NULL);

// Set the range and increment of the progress bar.

SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, cb / 2048));

SendMessage(hwndPB, PBM_SETSTEP, (WPARAM) 1, 0);

// Parse the file.

pch = (LPCH) LocalAlloc(LPTR, sizeof(char) * 2048);

pchTmp = pch;

do {

ReadFile(hFile, pchTmp, sizeof(char) * 2048, &cb,

(LPOVERLAPPED) NULL);

.

. // Include here any code that parses the file.

.

// Advance the current position of the progress bar

// by the increment.

SendMessage(hwndPB, PBM_STEPIT, 0, 0);

} while (cb);

CloseHandle((HANDLE) hFile);

DestroyWindow(hwndPB);

return TRUE;

}

+$#A Progress Bar Messages

The following messages are associated with progress bars.

PBM_DELTAPOS
PBM_SETPOS
PBM_SETRANGE
PBM_SETSTEP
PBM_STEPIT

+$#AK Property Sheets

A property sheet is a window that allows the user to view and edit the properties of an item. For example, a spreadsheet application can use a property sheet to allow the user to set the font and border properties of a cell or to view and set the properties of a device, such as a disk drive, printer, or mouse.

This overview assumes that you have a thorough understanding of dialog box templates and dialog box procedures. If you do not, you should read Dialog Boxes before continuing with this overview.

+$#A About Property Sheets

A property sheet contains one or more overlapping child windows called pages, each containing control windows for setting a group of related properties. For example, a page can contain the controls for setting the font properties of an item, including the type style, point size, color, and so on. Each page has a tab that the user can select to bring the page to the foreground of the property sheet. The following illustration shows a property sheet for viewing and setting the properties of a floppy disk drive.

{ewc msdncd, EWGraphic, group10602 0 /a "SDK_01.bmp"}

+$#A Property Sheet Dialog Boxes

A property sheet and the pages it contains are actually dialog boxes. The property sheet is a system-defined dialog box that manages the pages and provides a common container for them. The property sheet dialog box can be modal or modeless. It includes a frame, a title bar, and four buttons: OK, Cancel, Apply Now, and Help. (The Help button may be hidden as in the preceding illustration.) The dialog box procedures for the pages receive notification messages when the user selects the buttons.

Each page in a property sheet is an application-defined modeless dialog box that manages the control windows used to view and edit the properties of an item. You provide the dialog box template used to create each page as well as the dialog box procedure that manages the controls and sets the properties of the corresponding item.

A property sheet sends notification messages to the dialog box procedure for a page when the page is gaining or losing the activation and when the user chooses the OK, Cancel, Apply Now, or Help button. The notifications are sent in the form of WM_NOTIFY messages. The lParam parameter points to an NMHDR structure, which includes the window handle of the property sheet dialog box.

Some notification messages require a page to return either TRUE or FALSE in response to the WM_NOTIFY message. A page cannot simply return TRUE or FALSE; instead, it must use the SetWindowLong function to set the DWL_MSGRESULT value for the page dialog box to either TRUE or FALSE.

+$#A Pages

A property sheet must contain at least one page, but cannot contain more than the value of MAXPROPPAGES as defined in the Win32 header files. Each page has a zero-based index that the property sheet assigns according to the order in which the page is added to the property sheet. The indexes are used in messages that you send to the property sheet.

A property page can contain a nested dialog box. If it does, you must include the WS_EX_CONTROLPARENT style for the top-level dialog box and call the IsDialogMessage function with the handle of the parent dialog box. This ensures that the user can use mnemonics and the dialog box navigation keys to move the focus to controls in the nested dialog box.

Each page has a corresponding icon and label. The property sheet creates a tab for each page and displays the icon and label in the tab. All property sheet pages are expected to use a nonbold font. To ensure that the font is not bold, specify the DS_3DLOOK style in the dialog box template.

The dialog box procedure for a page must not call the EndDialog function. Doing so will destroy the entire property sheet, not just the page.

+$#A Property Sheet Creation

Before creating a property sheet, you must define one or more pages. This involves filling a PROPSHEETPAGE structure with information about the page ⎯ its icon, label, dialog box template, dialog box procedure, and so on ⎯ and then specifying the address of the structure in a call to the CreatePropertySheetPage function. The function returns a handle of the HPROPSHEETPAGE type that uniquely identifies the page.

To create a property sheet, you specify the address of a PROPSHEETHEADER structure in a call to the PropertySheet function. The structure defines the icon and title for the property sheet and also includes a pointer to an array of HPROPSHEETPAGE handles. When PropertySheet creates the property sheet, it includes the pages identified in the array. The order of the array determines the order of the pages in the property sheet.

Another way to create a property sheet is to specify an array of PROPSHEETPAGE structures instead of an array of HPROPSHEETPAGE handles. In this case, PropertySheet creates handles for the pages before adding them to the property sheet.

When a page is created, the dialog box procedure for the page receives a WM_INITDIALOG message. The message's lParam parameter points to the PROPSHEETPAGE structure used to create the page. The dialog box can save the information in the structure and use it later to modify the page.

PropertySheet automatically sets the size and initial position of a property sheet. The position is based on the position of the owner window, and the size is based on the largest page specified in the array of pages when the property sheet was created. If you want the pages to match the width of the four buttons at the bottom of the property sheet, set the width of the widest page to 190 dialog units.

+$#A Adding and Removing Pages

After creating a property sheet, an application can add a page by using the PSM_ADDPAGE message. Note that the size of the property sheet cannot change after it has been created, so the new page must be no larger than the largest page currently in the property sheet.

An application removes a page by using the PSM_REMOVEPAGE message. When you define a page, you can specify the address of a PropSheetPageProc callback function that the property sheet calls when it is creating or removing the page. Using PropSheetPageProc gives you an opportunity to perform initialization and cleanup operations for individual pages.

When a property sheet is destroyed, it automatically destroys all of the pages that have been added to it. The pages are destroyed in reverse order from that specified in the array used to create the pages. To destroy a page that was created by the CreatePropertySheetPage function but was not added to the property sheet, use the DestroyPropertySheetPage function.

+$#A Property Sheet Title and Page Labels

You specify the title of a property sheet in the PROPSHEETHEADER structure used to create the property sheet. If the dwFlags member includes the PSH_PROPTITLE value, the property sheet adds the "Properties for" prefix to the specified title string. An application can change the title after a property sheet is created by using the PSM_SETTITLE message.

By default, a property sheet uses the name string specified in the dialog box template as the label for a page. You can override the name string by including the PSP_USETITLE value in the dwFlags member of the PROPSHEETPAGE structure that defines the page. When PSP_USETITLE is specified, the pszTitle member must contain the address of the label string for the page.

+$#A Page Activation

A property sheet can have only one active page at a time. The page that has the activation is at the foreground of the overlapping stack of pages. The user activates a page by selecting its tab; an application activates a page by using the PSM_SETCURSEL message.

The property sheet sends the PSN_KILLACTIVE notification message to the page that is about to lose the activation. In response, the page should validate any changes that the user has made to the page. If the page requires additional user input before losing the activation, it should use the SetWindowLong function to set the DWL_MSGRESULT value of the page to TRUE. Also, the page should display a message box that describes the problem and provides the recommended action. The page should set DWL_MSGRESULT to FALSE when it is okay to lose the activation.

Before the page that is gaining the activation is visible, the property sheet sends the PSN_SETACTIVE notification message to the page. The page should respond by initializing its control windows.

+$#A Help Button

When a page is activated, the property sheet determines whether to enable or disable the Help button for the page by checking for the PSP_HASHELP style. If the page has this style, it supports the Help button. If the PSP_HASHELP style is not present, the button is disabled.

When the user chooses the Help button, the active page receives the PSN_HELP notification message. The page should respond by displaying help information, typically by calling the WinHelp function.

+$#A OK, Cancel, and Apply Now Buttons

The OK and Apply Now buttons are similar; both direct a property sheet's pages to validate and apply the property changes that the user has made. The only difference is that choosing the OK button causes the property sheet to be destroyed after the changes are applied, but choosing the Apply Now button does not.

When the user chooses the OK or Apply Now button, the property sheet sends the PSN_KILLACTIVE notification message to the active page, giving it an opportunity to validate the user's changes. If the page determines that the changes are valid, it should call the SetWindowLong function to set the DWL_MSGRESULT value for the page to FALSE. In this case, the property sheet sends the PSN_APPLY notification message to each page, directing them to apply the new properties to the corresponding item. If the page determines that the user's changes are not valid, it should set DWL_MSGRESULT to TRUE and display a dialog box informing the user of the problem. The page remains active until it sets DWL_MSGRESULT to FALSE in response to a PSN_KILLACTIVE message. An application can use the PSM_APPLY message to simulate the choice of the Apply Now button.

The Apply Now button is initially disabled when a page becomes active, indicating that there are not yet any property changes to apply. When the page receives user input through one of its controls indicating that the user has edited a property, the page should send the PSM_CHANGED message to the property sheet. The message causes the property sheet to enable the Apply Now button. If the user subsequently chooses the Apply Now or Cancel button, the page should reinitialize its controls and then send the PSM_UNCHANGED message to disable again the Apply Now button.

Sometimes the Apply Now button causes a page to make a change to a property sheet, and the change cannot be undone. When this happens, the page should send the PSM_CANCELTOCLOSE message to the property sheet. The message causes the property sheet to change the text of the OK button to "Close," indicating that the applied changes cannot be canceled.

Sometimes a page makes a change to the system configuration that requires Windows to be to restarted or the system rebooted before the change can take effect. After making such a change, a page should send either the PSM_RESTARTWINDOWS or PSM_REBOOTSYSTEM message to the property sheet. These messages cause the PropertySheet function to return the ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM value after the property sheet is destroyed.

The property sheet sends the the PSN_RESET notification message to all pages when the user chooses the Cancel button, indicating that the property sheet is about to be destroyed. A page should use the notification to perform cleanup operations.

+$#A Wizard Property Sheets

You can create a special type of property sheet called a wizard, which consists of a sequence of dialog boxes that guide the user through the steps of an operation, such as setting up a device or creating a birthday card. In a wizard property sheet, the pages do not have tabs, and only one page is visible at a time. Also, instead of having Ok and Apply Now buttons, a wizard property sheet has a Back button, a Next or Finish button, and a Cancel button. To tell the property sheet which buttons to enable, use the PSM_SETWIZBUTTONS message with the PSWIZB_BACK, PSWIZB_NEXT, and PSWIZB_FINISH values. You create and initialize a wizard property sheet just as you would a standard property sheet, except that you must include the PSH_WIZARD value in the dwFlags member of the PROPSHEETHEADER structure. The system ignores the pszCaption member; instead, it puts the label of the current page in the title bar of the property sheet. When the user switches from one page to the next, the system updates the title using the label of the current page.

You should use the following values to set the sizes of the elements in your wizard property sheet. Using these values ensures that your pages conform to the Window standards.

WIZ_BODYCX | Width of the body of a page in a wizard property sheet. The body does not include the bitmap area.
WIZ_BODYX | Horizontal coordinate of the upper left corner of the body of a page in a wizard property sheet. Use zero for the vertical coordinate of the body of a page.
WIZ_CXBMP | Width of the bitmap area in a page of a wizard property sheet. Use WIZ_CYDLG for the height of the bitmap area.
WIZ_CXDLG | Width of a page in a wizard property sheet.
WIZ_CYDLG | Height of a page in a wizard property sheet.

The dialog box procedure for a page in a wizard property sheet receives all of the same notification messages as a standard property sheet page. In addition, a wizard property sheet page receives three notification messages that a standard property sheet page does not receive: PSN_WIZBACK, PSN_WIZNEXT, and PSN_WIZFINISH. A wizard page receives these notifications when the user chooses the Back, Next, or Finish button.

When the user chooses the Back or Next button, the property sheet automatically advances to the previous or next page. An application can prevent the property sheet from advancing by setting the DWL_MSGRESULT value to −1 in response to the PSN_WIZBACK or PSN_WIZNEXT notification. To jump to a page other than the previous or next one, an application should set DWL_MSGRESULT to the identifier of the dialog box to be displayed.

The system automatically destroys the wizard property sheet when the user clicks the Finish button. An application can prevent the wizard from being destroyed by setting DWL_MSGRESULT to a nonzero value in response to the PSN_WIZFINISH notification message.

+$#A Property Sheet Extensions

A property sheet extension is a dynamic-link library (DLL) that adds one or more pages to a property sheet created by another module. The module that creates the property sheet includes an AddPropSheetPageProc callback function that the extension DLL calls to add a page. The function receives the handle of a page and an application-defined 32-bit value.

The extension DLL also contains a callback function called ExtensionPropSheetPageProc, which receives the address of AddPropSheetPageProc from the module that creates the property sheet. The extension DLL must export ExtensionPropSheetPageProc.

The Windows header files include two prototypes for defining property sheet callback functions. To define AddPropSheetPageProc, use the following prototype.

typedef BOOL (CALLBACK FAR * LPFNADDPROPSHEETPAGE)(HPROPSHEETPAGE,
LPARAM);

To define ExtensionPropSheetPageProc, use the following prototype.

typedef BOOL (CALLBACK FAR * LPFNADDPROPSHEETPAGES)(LPVOID, LPFNADDPROPSHEETPAGE, LPARAM);

+$#A Using Property Sheets

• Creating a property sheet

• Processing notification messages

+$#A Creating a Property Sheet

The example in this section creates a property sheet that contains two pages ⎯ one for setting the font properties of a cell in a spreadsheet and another for setting the border properties of the cell. The example defines the pages by filling a pair of PROPSHEETPAGE structures and specifying the address in the PROPSHEETHEADER structure that is passed to the PropertySheet function. The dialog box templates, icons, and labels for the pages are loaded from the resources contained in the application's executable file. The icon for the property sheet is also loaded from the application's resources.

// DoPropertySheet - creates a property sheet that contains two pages.
// hwndOwner - handle of the owner window of the property sheet
//
// Global variables
// g_hinst - instance handle
extern HINSTANCE g_hinst;

VOID DoPropertySheet(HWND hwndOwner)
{
PROPSHEETPAGE psp[2];
PROPSHEETHEADER psh;

psp[0].dwSize = sizeof(PROPSHEETPAGE);
psp[0].dwFlags = PSP_USEICONID | PSP_USETITLE;
psp[0].hInstance = g_hinst;
psp[0].pszTemplate = MAKEINTRESOURCE(DLG_FONT);
psp[0].pszIcon = MAKEINTRESOURCE(IDI_FONT);
psp[0].pfnDlgProc = FontDialogProc;
psp[0].pszTitle = MAKEINTRESOURCE(IDS_FONT)
psp[0].lParam = 0;
psp[0].pfnCallback = NULL;

psp[1].dwSize = sizeof(PROPSHEETPAGE);
psp[1].dwFlags = PSP_USEICONID | PSP_USETITLE;
psp[1].hInstance = g_hinst;
psp[1].pszTemplate = MAKEINTRESOURCE(DLG_BORDER);
psp[1].pszIcon = MAKEINTRESOURCE(IDI_BORDER);
psp[1].pfnDlgProc = BorderDialogProc;
psp[1].pszTitle = MAKEINTRESOURCE(IDS_BORDER);
psp[1].lParam = 0;
psp[1].pfnCallback = NULL;

psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_USEICONID | PSH_PROPSHEETPAGE;
psh.hwndParent = hwndOwner;
psh.hInstance = g_hinst;
psh.pszIcon = MAKEINTRESOURCE(IDI_CELL_PROPERTIES);
psh.pszCaption = (LPSTR) "Cell Properties";
psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
psh.nStartPage = 0;
psh.ppsp = (LPCPROPSHEETPAGE) &psp;
psh.pfnCallback = NULL;

PropertySheet(&psh);
return;
}

+$#A Processing Notification Messages

A property sheet sends WM_NOTIFY messages to retrieve information from the pages and to notify the pages of user actions. The lParam parameter of the message is the address of an NMHDR structure, which contains the handle of the property sheet dialog box, the handle of the page dialog box, and a notification code. The page must respond to some notification messages by setting the DWL_MSGRESULT value of the page to either TRUE or FALSE.

The following example is a code fragment from the dialog box procedure for a page. It shows how to process the PSN_HELP notification message.

case WM_NOTIFY:
switch (((NMHDR FAR *) lParam)->code) {

case PSN_HELP:
{
char szBuf[FILE_LEN]; // buffer for name of help file

// Display help for the font properties page.
LoadString(g_hinst, IDS_HELPFILE, &szBuf, FILE_LEN)
WinHelp(((NMHDR FAR *) lParam)->hwndFrom, &szBuf,
HELP_CONTEXT, IDH_FONT_PROPERTIES);
break;
}
.
. // Process other property sheet notifications here.
.
}

+$#A Property Sheet Reference

The following functions, messages, notification messages, and structures are associated with property sheets. These elements can be grouped as follows.

Property Sheet Creation

CreatePropertySheetPage
DestroyPropertySheetPage
PropertySheet
PROPSHEETHEADER
PROPSHEETPAGE
PropSheetPageProc
PropSheetProc
PSM_ADDPAGE
PSM_REMOVEPAGE

Property Sheet Extensions

AddPropSheetPageProc
ExtensionPropSheetPageProc

Page Activation

PSM_SETCURSEL
PSM_SETCURSELID
PSN_KILLACTIVE
PSN_SETACTIVE

Property Sheet Buttons

PSM_APPLY
PSM_CANCELTOCLOSE
PSM_CHANGED
PSM_PRESSBUTTON
PSM_REBOOTSYSTEM
PSM_RESTARTWINDOWS
PSM_UNCHANGED
PSN_APPLY
PSN_HELP
PSN_QUERYCANCEL
PSN_RESET

Wizard Property Sheets

PSM_SETFINISHTEXT
PSM_SETWIZBUTTONS
PSN_WIZBACK
PSN_WIZFINISH
PSN_WIZNEXT

Miscellaneous Messages

PSM_GETTABCONTROL
PSM_SETTITLE
PSM_QUERYSIBLINGS

+$#A Property Sheet Functions

The following functions are used with property sheets.

AddPropSheetPageProc
CreatePropertySheetPage
DestroyPropertySheetPage
ExtensionPropSheetPageProc
PropertySheet
PropSheetPageProc
PropSheetProc

+$#A Property Sheet Messages

An application sends messages to direct a property sheet to add and remove pages, enable and disable buttons, set the return value of the PropertySheet function, and so on. The following are the property sheet messages.

PSM_ADDPAGE
PSM_APPLY
PSM_CANCELTOCLOSE
PSM_CHANGED
PSM_GETCURRENTPAGEHWND
PSM_GETTABCONTROL
PSM_ISDIALOGMESSAGE
PSM_PRESSBUTTON
PSM_QUERYSIBLINGS
PSM_REBOOTSYSTEM
PSM_REMOVEPAGE
PSM_RESTARTWINDOWS
PSM_SETCURSEL
PSM_SETCURSELID
PSM_SETFINISHTEXT
PSM_SETTITLE
PSM_SETWIZBUTTONS
PSM_UNCHANGED

+$#A Property Sheet Notification Messages

A property sheet sends notification messages to the pages to notify them of events. The following are the property sheet notifications.

PSN_APPLY
PSN_HELP
PSN_KILLACTIVE
PSN_QUERYCANCEL
PSN_RESET
PSN_SETACTIVE
PSN_WIZBACK
PSN_WIZFINISH
PSN_WIZNEXT

+$#A Property Sheet Structures

The following structures are used with property sheets.

PROPSHEETHEADER
PROPSHEETPAGE

Créé avec HelpNDoc Personal Edition: Générateur d'aides CHM gratuit


Rectangles.rtf

+$#AK Rectangles

Applications written for Microsoft® Windows® use rectangles to specify rectangular areas on the screen or in a window.

+$#A About Rectangles

In Windows applications, rectangles are used for the cursor clipping region, the invalid portion of the client area, an area for displaying formatted text, or the scroll area. Your applications can also use rectangles to fill, frame, or invert a portion of the client area with a given brush, and to retrieve the coordinates of a window or a window's client area.

+$#A Rectangle Coordinates

An application must use a RECT structure to define a rectangle. The structure specifies the coordinates of two points: the upper left and lower right corners of the rectangle. The sides of the rectangle extend from these two points and are parallel to the x- and y-axes.

The coordinate values for a rectangle are expressed as signed integers. The coordinate value of a rectangle's right side must be greater than that of its left side. Likewise, the coordinate value of the bottom must be greater than that of the top.

Because applications can use rectangles for many different purposes, the Windows rectangle functions do not use an explicit unit of measure. Instead, all rectangle coordinates and dimensions are given in signed, logical values. The mapping mode and the function in which the rectangle is used determine the units of measure. For more information about coordinates and mapping modes, see Coordinate Spaces and Transformations.

+$#A Rectangle Operations

The Microsoft® Win32® application programming interface (API) provides several functions for working with rectangles.

The SetRect function creates a rectangle, the CopyRect function makes a copy of a given rectangle, and the SetRectEmpty function creates an empty rectangle. An empty rectangle is any rectangle that has zero width, zero height, or both. The IsRectEmpty function determines whether a given rectangle is empty. The EqualRect function determines whether two rectangles are identical ⎯ that is, whether they have the same coordinates.

The InflateRect function increases or decreases the width or height of a rectangle, or both. It can add or remove width from both ends of the rectangle; it can add or remove height from both the top and bottom of the rectangle.

The OffsetRect function moves a rectangle by a given amount. It moves the rectangle by adding the given x-amount, y-amount, or x- and y-amounts to the corner coordinates.

The PtInRect function determines whether a given point lies within a given rectangle. The point is in the rectangle if it lies on the left or top side or is completely within the rectangle. The point is not in the rectangle if it lies on the right or bottom side.

The IntersectRect function creates a new rectangle that is the intersection of two existing rectangles, as shown in the following figure.

{ewc msdncd, EWGraphic, group10811 0 /a "SDK_01.bmp"}

The UnionRect function creates a new rectangle that is the union of two existing rectangles, as shown in the following figure.

{ewc msdncd, EWGraphic, group10811 1 /a "SDK_02.bmp"}

For information about functions that draw ellipses and polygons, see Filled Shapes.

+$#A Using Rectangles

The example in this section illustrates how to use the rectangle functions. It consists of the main window procedure from an application that enables the user to move and size a bitmap.

When the application starts, it draws a 32-pixel by 32-pixel bitmap in the upper left corner of the screen. The user can move the bitmap by dragging it. To size the bitmap, the user creates a target rectangle by dragging the mouse, then drags the bitmap and "drops" it on the target rectangle. The application responds by copying the bitmap into the target rectangle.

The window procedure that allows the user to move and size the bitmap is given in the following example.

LRESULT CALLBACK MainWndProc(hwnd, uMsg, wParam, lParam)

HWND hwnd; /* handle of window */

UINT uMsg; /* message */

WPARAM wParam; /* first message parameter */

LPARAM lParam; /* second message parameter */

{

HDC hdc; /* device context (DC) for window */

RECT rcTmp; /* temporary rectangle */

PAINTSTRUCT ps; /* paint data for Begin/EndPaint */

POINT ptClientUL; /* client area upper left corner */

POINT ptClientLR; /* client area lower right corner */

static HDC hdcCompat; /* DC for copying bitmap */

static POINT pt; /* x and y coordinates of cursor */

static RECT rcBmp; /* rectangle that encloses bitmap */

static RECT rcTarget; /* rectangle to receive bitmap */

static RECT rcClient; /* client-area rectangle */

static BOOL fDragRect; /* TRUE if bitmap rect. is dragged */

static HBITMAP hbmp; /* handle of bitmap to display */

static HBRUSH hbrBkgnd; /* handle of background-color brush */

static COLORREF crBkgnd; /* color of client-area background */

static HPEN hpenDot; /* handle of dotted pen */

switch (uMsg) {

case WM_CREATE:

/* Load the bitmap resource. */

hbmp = LoadBitmap(hinst, MAKEINTRESOURCE(1));

/*

* Create a device context (DC) to hold the bitmap.

* The bitmap is copied from this DC to the window's DC

* whenever it must be drawn.

*/

hdc = GetDC(hwnd);

hdcCompat = CreateCompatibleDC(hdc);

SelectObject(hdcCompat, hbmp);

/*

* Create a brush of the same color as the background

* of the client area. The brush is used later to erase

* the old bitmap before copying the bitmap into the

* target rectangle.

*/

crBkgnd = GetBkColor(hdc);

hbrBkgnd = CreateSolidBrush(crBkgnd);

ReleaseDC(hwnd, hdc);

/*

* Create a dotted pen. The pen is used to draw the

* bitmap rectangle as the user drags it.

*/

hpenDot = CreatePen(PS_DOT, 1, RGB(0, 0, 0));

/*

* Set the initial rectangle for the bitmap. Note that

* this application supports only a 32- by 32-pixel

* bitmap. The rectangle is slightly larger than the

* bitmap.

*/

SetRect(&rcBmp, 1, 1, 34, 34);

return 0;

case WM_PAINT:

/*

* Draw the bitmap rectangle and copy the bitmap into

* it. The 32-pixel by 32-pixel bitmap is centered

* in the rectangle by adding 1 to the left and top

* coordinates of the bitmap rectangle, and subtracting

* 2 from the right and bottom coordinates.

*/

BeginPaint(hwnd, &ps);

Rectangle(ps.hdc, rcBmp.left, rcBmp.top,

rcBmp.right, rcBmp.bottom);

StretchBlt(ps.hdc, rcBmp.left + 1, rcBmp.top + 1,

(rcBmp.right - rcBmp.left) - 2,

(rcBmp.bottom - rcBmp.top) - 2, hdcCompat,

0, 0, 32, 32, SRCCOPY);

EndPaint(hwnd, &ps);

break;

case WM_MOVE:

case WM_SIZE:

/*

* Convert the client coordinates of the client-area

* rectangle to screen coordinates and save them in a

* rectangle. The rectangle is passed to the ClipCursor

* function during WM_LBUTTONDOWN processing.

*/

GetClientRect(hwnd, &rcClient);

ptClientUL.x = rcClient.left;

ptClientUL.y = rcClient.top;

ptClientLR.x = rcClient.right;

ptClientLR.y = rcClient.bottom;

ClientToScreen(hwnd, &ptClientUL);

ClientToScreen(hwnd, &ptClientLR);

SetRect(&rcClient, ptClientUL.x, ptClientUL.y,

ptClientLR.x, ptClientLR.y);

return 0;

case WM_LBUTTONDOWN:

/*

* Restrict the mouse cursor to the client area. This

* ensures that the window receives a matching

* WM_LBUTTONUP message.

*/

ClipCursor(&rcClient);

/* Save the coordinates of the mouse cursor. */

pt.x = (LONG) LOWORD(lParam);

pt.y = (LONG) HIWORD(lParam);

/*

* If the user has clicked the bitmap rectangle, redraw

* it using the dotted pen. Set the fDragRect flag to

* indicate that the user is about to drag the

* rectangle.

*/

if (PtInRect(&rcBmp, pt)) {

hdc = GetDC(hwnd);

SelectObject(hdc, hpenDot);

Rectangle(hdc, rcBmp.left, rcBmp.top, rcBmp.right,

rcBmp.bottom);

fDragRect = TRUE;

ReleaseDC(hwnd, hdc);

}

return 0;

case WM_MOUSEMOVE:

/*

* Draw a target rectangle or drag the bitmap

* rectangle, depending on the status of the fDragRect

* flag.

*/

if ((wParam && MK_LBUTTON)

&& !fDragRect) { /* draw a target rectangle */

/*

* Set the mix mode so that the pen color is the

* inverse of the background color. The previous

* rectangle can then be erased by drawing

* another rectangle on top of it.

*/

hdc = GetDC(hwnd);

SetROP2(hdc, R2_NOTXORPEN);

/*

* If a previous target rectangle exists, erase

* it by drawing another rectangle on top of it.

*/

if (!IsRectEmpty(&rcTarget))

Rectangle(hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

/*

* Save the coordinates of the target rectangle.

* Avoid invalid rectangles by ensuring that the

* value of the left coordinate is greater than

* that of the right coordinate, and that the

* value of the bottom coordinate is greater than

* that of the top.

*/

if ((pt.x < (LONG) LOWORD(lParam)) &&

(pt.y > (LONG) HIWORD(lParam)))

SetRect(&rcTarget, pt.x, HIWORD(lParam),

LOWORD(lParam), pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&

(pt.y > (LONG) HIWORD(lParam)))

SetRect(&rcTarget, LOWORD(lParam),

HIWORD(lParam), pt.x, pt.y);

else if ((pt.x > (LONG) LOWORD(lParam)) &&

(pt.y < (LONG) HIWORD(lParam)))

SetRect(&rcTarget, LOWORD(lParam), pt.y,

pt.x, HIWORD(lParam));

else

SetRect(&rcTarget, pt.x, pt.y, LOWORD(lParam),

HIWORD(lParam));

/* Draw the new target rectangle. */

Rectangle(hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

ReleaseDC(hwnd, hdc);

}

else if ((wParam && MK_LBUTTON)

&& fDragRect) { /* drag the bitmap rectangle */

/*

* Set the mix mode so that the pen color is the

* inverse of the background color.

*/

hdc = GetDC(hwnd);

SetROP2(hdc, R2_NOTXORPEN);

/*

* Select the dotted pen into the DC and erase

* the previous bitmap rectangle by drawing

* another rectangle on top of it.

*/

SelectObject(hdc, hpenDot);

Rectangle(hdc, rcBmp.left, rcBmp.top,

rcBmp.right, rcBmp.bottom);

/*

* Set the new coordinates of the bitmap

* rectangle, then redraw it.

*/

OffsetRect(&rcBmp, LOWORD(lParam) - pt.x,

HIWORD(lParam) - pt.y);

Rectangle(hdc, rcBmp.left, rcBmp.top,

rcBmp.right, rcBmp.bottom);

ReleaseDC(hwnd, hdc);

/* Save the coordinates of the mouse cursor. */

pt.x = (LONG) LOWORD(lParam);

pt.y = (LONG) HIWORD(lParam);

}

return 0;

case WM_LBUTTONUP:

/*

* If the bitmap rectangle and target rectangle

* intersect, copy the bitmap into the target

* rectangle. Otherwise, copy the bitmap into the

* rectangle bitmap at its new location.

*/

if (IntersectRect(&rcTmp, &rcBmp, &rcTarget)) {

/*

* Erase the bitmap rectangle by filling it with

* the background color.

*/

hdc = GetDC(hwnd);

FillRect(hdc, &rcBmp, hbrBkgnd);

/*

* Redraw the target rectangle because the part

* that intersected with the bitmap rectangle was

* erased by the call to FillRect.

*/

Rectangle(hdc, rcTarget.left, rcTarget.top,

rcTarget.right, rcTarget.bottom);

/* Copy the bitmap into the target rectangle. */

StretchBlt(hdc, rcTarget.left + 1, rcTarget.top + 1,

(rcTarget.right - rcTarget.left) - 2,

(rcTarget.bottom - rcTarget.top) - 2, hdcCompat,

0, 0, 32, 32, SRCCOPY);

/*

* Copy the target rectangle to the bitmap

* rectangle, set the coordinates of the target

* rectangle to 0, then reset the fDragRect flag.

*/

CopyRect(&rcBmp, &rcTarget);

SetRectEmpty(&rcTarget);

ReleaseDC(hwnd, hdc);

fDragRect = FALSE;

}

else if (fDragRect) {

/*

* Draw the bitmap rectangle, copy the bitmap into

* it, and reset the fDragRect flag.

*/

hdc = GetDC(hwnd);

Rectangle(hdc, rcBmp.left, rcBmp.top,

rcBmp.right, rcBmp.bottom);

StretchBlt(hdc, rcBmp.left + 1, rcBmp.top + 1,

(rcBmp.right - rcBmp.left) - 2,

(rcBmp.bottom - rcBmp.top) - 2, hdcCompat,

0, 0, 32, 32, SRCCOPY);

ReleaseDC(hwnd, hdc);

fDragRect = FALSE;

}

/* Release the mouse cursor. */

ClipCursor((LPRECT) NULL);

return 0;

case WM_DESTROY:

/*

* Destroy the background brush, compatible bitmap,

* and the bitmap.

*/

DeleteObject(hbrBkgnd);

DeleteDC(hdcCompat);

DeleteObject(hbmp);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return (LRESULT) NULL;

}

+$#A Rectangle Functions

Following are the functions used with rectangles.

CopyRect
EqualRect
InflateRect
IntersectRect
IsRectEmpty
OffsetRect
PtInRect
SetRect
SetRectEmpty
SubtractRect
UnionRect

Créé avec HelpNDoc Personal Edition: Création d'aide CHM, PDF, DOC et HTML d'une même source


Regions.rtf

+$#AK Regions

In Microsoft® Windows®, a region is a rectangle, polygon, or ellipse (or a combination of two or more of these shapes) that can be filled, painted, inverted, framed, and used to perform hit testing (testing for the cursor location).

+$#A About Regions

Following are three types of regions that have been filled and framed.

{ewc msdncd, EWGraphic, group10665 0 /a "SDK_01.bmp"}

+$#A Region Creation and Selection

An application creates a region by calling a function associated with a specific shape. The following table shows the function(s) associated with each of the standard shapes.

Shape | Function
Rectangular region | CreateRectRgn, CreateRectRgnIndirect
Rectangular region with rounded corners | CreateRoundRectRgn
Elliptical region | CreateEllipticRgn, CreateEllipticRgnIndirect
Polygonal region | CreatePolygonRgn, CreatePolyPolygonRgn

Each region creation function returns a handle that identifies the new region. An application can use this handle to select the region into a device context by calling the SelectObject function and supplying this handle as the second argument. After a region is selected into a device context, the application can perform various operations on it.

+$#A Region Operations

Applications can combine regions, compare them, paint or invert their interiors, draw a frame around them, retrieve their dimensions, and test whether the cursor lies within their boundaries.

+$#A Combining

An application combines two regions by calling the CombineRgn function. Using this function, an application can combine the intersecting parts of two regions, all but the intersecting parts of two regions, the two original regions in their entirety, and so on. Following are five values that define the region combinations.

Value | Meaning
RGN_AND | The intersecting parts of two original regions define a new region.
RGN_COPY | A copy of the first (of the two original regions) defines a new region.
RGN_DIFF | The part of the first region that does not intersect the second defines a new region.
RGN_OR | The two original regions define a new region.
RGN_XOR | Those parts of the two original regions that do not overlap define a new region.

The following illustration shows the five possible combinations of a square and a circular region resulting from a call to CombineRgn.

{ewc msdncd, EWGraphic, group10665 1 /a "SDK_02.bmp"}

+$#A Comparing

An application compares two regions to determine whether or not they are identical by calling the EqualRgn function. EqualRgn considers two regions identical if they are equal in size and shape.

+$#A Filling

An application fills the interior of a region by calling the FillRgn function and supplying a handle that identifies a specific brush. When an application calls FillRgn, Windows fills the region with the brush by using the current fill mode for the specified device context. There are two fill modes: alternate and winding. The application can set the fill mode for a device context by calling the SetPolyFillMode function. The application can retrieve the current fill mode for a device context by calling the GetPolyFillMode function.

The following illustration shows two identical regions: one filled using alternate mode and the other filled using winding mode.

{ewc msdncd, EWGraphic, group10665 2 /a "SDK_03.bmp"}

Alternate Mode

To determine which pixels Windows highlights when alternate mode is specified, perform the following test:

1. Select a pixel within the region's interior.

2. Draw an imaginary ray, in the positive x-direction, from that pixel towards infinity.

3. Each time the ray intersects a boundary line, increment a count value.

Windows highlights the pixel if the count value is an odd number.

Winding Mode

To determine which pixels Windows highlights when winding mode is specified, perform the following test:

1. Determine the direction in which each boundary line is drawn.

2. Select a pixel within the region's interior.

3. Draw an imaginary ray, in the positive x-direction, from the pixel toward infinity.

4. Each time the ray intersects a boundary line with a positive y-component, increment a count value. Each time the ray intersects a boundary line with a negative y-component, decrement the count value.

Windows highlights the pixel if the count value is nonzero.

+$#A Painting

An application fills the interior of a region by using the brush currently selected into a device context by the PaintRgn function. This function uses the current polygon fill modes (alternate and winding).

+$#A Inverting

An application inverts the colors that appear within a region by calling the InvertRgn function. On monochrome displays, InvertRgn makes white pixels black and black pixels white. On color screens, this inversion is dependent on the type of technology used to generate the colors for the screen.

+$#A Framing

An application draws a border around a region by calling the FrameRgn function and specifying the border width and brush pattern that Windows uses when drawing the frame.

+$#A Retrieving a Bounding Rectangle

An application retrieves the dimensions of a region's bounding rectangle by calling the GetRgnBox function. If the region is rectangular, GetRgnBox returns the dimensions of the region. If the region is elliptical, the function returns the dimensions of the smallest rectangle that can be drawn around the ellipse. The long sides of the rectangle are the same length as the ellipse's major axis, and the short sides of the rectangle are the same length as the ellipse's minor axis. If the region is polygonal, GetRgnBox returns the dimensions of the smallest rectangle that can be drawn around the entire polygon.

+$#A Moving

An application moves a region by calling the OffsetRgn function. The given offsets along the x-axis and y-axis determine the number of logical units to move left or right and up or down.

+$#A Hit Testing

An application performs hit testing on regions to determine the coordinates of the current cursor position. Then it passes these coordinates ⎯ as well as a handle identifying the region ⎯ to the PtInRegion function. The cursor coordinates can be retrieved by processing the various mouse messages, such as WM_LBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONDOWN, and WM_RBUTTONUP. The return value for PtInRegion indicates whether the cursor position is within the given region.

+$#A Using Regions

• Using regions to clip output

• Using regions to perform hit testing

+$#A Using Regions to Clip Output

This section contains a single example that demonstrates how you can use regions to enable the user to define how a part of client area output can appear. Regions used for this purpose are called clipping regions.

The example for this section is taken from a Windows-based application that enables a user to capture the entire desktop as a bitmap and then isolate and save a part of this image as a .BMP file.

By selecting the Define Clip Region option from the application's menu, the user is able to select a clipping region by clicking the left mouse button and dragging the mouse. As the user drags the mouse, the application draws a rectangle that corresponds to the new clipping region.

By selecting the Clip option, the user is able to redraw the isolated part of the image within the boundaries of the specified rectangle.

+$#A Defining the Clipping Region

When the user selects the Define Clip Region option, Windows issues a WM_COMMAND message. The wParam parameter of this message contains an application-defined constant, IDM_DEFINE, that indicates that the user selected this option from the menu. The application processes this input by setting a Boolean flag, fDefineRegion, as shown in the following code sample.

case WM_COMMAND:

switch (wParam) {

case IDM_DEFINE:

fDefineRegion = TRUE;

break;

After selecting the Define Clipping Region option, the user can begin drawing the rectangle by clicking and dragging the mouse while the cursor is in the application's client area.

When the user presses the left button, Windows issues a WM_LBUTTONDOWN message. The lParam parameter of this message contains the cursor coordinates, which correspond to the upper left corner of a rectangle used to define the clipping region. The application processes the WM_LBUTTONDOWN message, as follows.

/* These variables are required for clipping. */

static POINT ptUpperLeft;

static POINT ptLowerRight;

static POINT aptRect[5];

static POINT ptTmp;

static POINTS ptsTmp;

static BOOL fDefineRegion;

static BOOL fRegionExists;

static HRGN hrgn;

static RECT rctTmp;

int i;

switch (message) {

case WM_LBUTTONDOWN:

if (fDefineRegion) {

/* Retrieve the new upper left corner. */

ptsTmp = MAKEPOINTS(lParam);

ptUpperLeft.x = (LONG) ptsTmp.x;

ptUpperLeft.y = (LONG) ptsTmp.y;

}

if (fRegionExists) {

/* Erase the previous rectangle. */

hdc = GetDC(hwnd);

SetROP2(hdc, R2_NOTXORPEN);

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))

errhandler("Polyline Failed", hwnd);

ReleaseDC(hwnd, hdc);

/* Clear the rectangle coordinates. */

for (i = 0; i < 4; i++) {

aptRect[i].x = 0;

aptRect[i].y = 0;

}

/* Clear the temporary point structure. */

ptTmp.x = 0;

ptTmp.y = 0;

/* Clear the lower right coordinates. */

ptLowerRight.x = 0;

ptLowerRight.y = 0;

/* Reset the flag. */

fRegionExists = FALSE;

fDefineRegion = TRUE;

/* Retrieve the new upper left corner. */

ptsTmp = MAKEPOINTS(lParam);

ptUpperLeft.x = (LONG) ptsTmp.x;

ptUpperLeft.y = (LONG) ptsTmp.y;

}

break;

As the user drags the mouse, Windows issues WM_MOUSEMOVE messages and stores the new cursor coordinates in the lParam parameter. Each time the application receives a new WM_MOUSEMOVE message, it erases the previous rectangle (if one exists) and draws the new rectangle by calling the Polyline function, passing it the coordinates of the four corners of the rectangle. The application performs the following tasks.

/* These variables are required for clipping. */

static POINT ptUpperLeft;

static POINT ptLowerRight;

static POINT aptRect[5];

static POINT ptTmp;

static POINTS ptsTmp;

static BOOL fDefineRegion;

static BOOL fRegionExists;

static HRGN hrgn;

static RECT rctTmp;

int i;

switch (message) {

case WM_MOUSEMOVE:

if (wParam & MK_LBUTTON && fDefineRegion) {

/* Get a window DC. */

hdc = GetDC(hwnd);

if (!SetROP2(hdc, R2_NOTXORPEN))

errhandler("SetROP2 Failed", hwnd);

/*

* If previous mouse movement occurred, store the original

* lower right corner coordinates in a temporary structure.

*/

if (ptLowerRight.x) {

ptTmp.x = ptLowerRight.x;

ptTmp.y = ptLowerRight.y;

}

/*

* Get the new coordinates of the clipping region's lower

* right corner.

*/

ptsTmp = MAKEPOINTS(lParam);

ptLowerRight.x = (LONG) ptsTmp.x;

ptLowerRight.y = (LONG) ptsTmp.y;

/*

* If previous mouse movement occurred, erase the original

* rectangle.

*/

if (ptTmp.x) {

aptRect[0].x = ptUpperLeft.x;

aptRect[0].y = ptUpperLeft.y;

aptRect[1].x = ptTmp.x;

aptRect[1].y = ptUpperLeft.y;

aptRect[2].x = ptTmp.x;

aptRect[2].y = ptTmp.y;

aptRect[3].x = ptUpperLeft.x;

aptRect[3].y = ptTmp.y;

aptRect[4].x = aptRect[0].x;

aptRect[4].y = aptRect[0].y;

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))

errhandler("Polyline Failed", hwnd);

}

aptRect[0].x = ptUpperLeft.x;

aptRect[0].y = ptUpperLeft.y;

aptRect[1].x = ptLowerRight.x;

aptRect[1].y = ptUpperLeft.y;

aptRect[2].x = ptLowerRight.x;

aptRect[2].y = ptLowerRight.y;

aptRect[3].x = ptUpperLeft.x;

aptRect[3].y = ptLowerRight.y;

aptRect[4].x = aptRect[0].x;

aptRect[4].y = aptRect[0].y;

if (!Polyline(hdc, (CONST POINT *) aptRect, 5))

errhandler("Polyline Failed", hwnd);

ReleaseDC(hwnd, hdc);

}

break;

+$#A Clipping Output

After the user chooses the Clip option from the menu, the application uses the coordinates of the rectangle the user created to define a clipping region. After defining the clipping region and selecting it into the application's device context, the application redraws the bitmapped image. The application performs these tasks, as follows.

/* These variables are required for clipping. */

static POINT ptUpperLeft;

static POINT ptLowerRight;

static POINT aptRect[5];

static POINT ptTmp;

static POINTS ptsTmp;

static BOOL fDefineRegion;

static BOOL fRegionExists;

static HRGN hrgn;

static RECT rctTmp;

int i;

case WM_COMMAND:

switch (wParam) {

case IDM_CLIP:

hdc = GetDC(hwnd);

/*

* Retrieve the application's client rectangle and paint

* with the default (white) brush.

*/

GetClientRect(hwnd, &rctTmp);

FillRect(hdc, &rctTmp, GetStockObject(WHITE_BRUSH));

/* Use the rect coordinates to define a clipping region. */

hrgn = CreateRectRgn(aptRect[0].x, aptRect[0].y,

aptRect[2].x, aptRect[2].y);

SelectClipRgn(hdc, hrgn);

/* Transfer (draw) the bitmap into the clipped rectangle. */

BitBlt(hdc,

0, 0,

bmp.bmWidth, bmp.bmHeight,

hdcCompatible,

0, 0,

SRCCOPY);

ReleaseDC(hwnd, hdc);

break;

+$#A Using Regions to Perform Hit Testing

The example in Brushes uses regions to simulate a "zoomed" view of an 8- by 8-pixel monochrome bitmap. By clicking on the pixels in this bitmap, the user creates a custom brush suitable for drawing operations. The example shows how to use the PtInRegion function to perform hit testing and the InvertRgn function to invert the colors in a region.

+$#A Region Functions

Following are the functions used with regions.

CombineRgn
CreateEllipticRgn
CreateEllipticRgnIndirect
CreatePolygonRgn
CreatePolyPolygonRgn
CreateRectRgn
CreateRectRgnIndirect
CreateRoundRectRgn
EqualRgn
ExtCreateRegion
FillRgn
FrameRgn
GetPolyFillMode
GetRegionData
GetRgnBox
InvertRgn
OffsetRgn
PaintRgn
PtInRegion
RectInRegion
SetPolyFillMode

Créé avec HelpNDoc Personal Edition: Créer des aides HTML, DOC, PDF et des manuels depuis une même source


Registry.rtf

+$#AK Registry

The registry is a system-defined database that applications and Microsoft® Windows® system components use to store and retrieve configuration data.

+$#A About the Registry

The registry stores data in binary files. To manipulate registry data, an application must use the registry functions. This overview describes the registry and the functions that applications use to access and manipulate the data stored there. The data stored in the registry varies according to the Windows platform that is used. This overview contains a description of registry entries that can exist on any platform.

+$#A Structure of the Registry

The registry stores data in a hierarchically structured tree. Each node in the tree is called a key. Each key can contain both subkeys and data entries called values. Sometimes, the presence of a key is all the data that an application requires; other times, an application opens a key and uses the values associated with the key. A key can have any number of values, and the values can be in any form.

Each key has a name consisting of one or more printable ANSI characters ⎯ that is, characters ranging from values 32 through 127. Key names cannot include a space, a backslash (\), or a wildcard character (* or ?). Key names beginning with a period (.) are reserved. The name of each subkey is unique with respect to the key that is immediately above it in the hierarchy. Key names are not localized into other languages, although values may be.

+$#A Registry Storage Space

Although there are few technical limits to the type and size of data an application can store in the registry, certain practical guidelines exist to promote system efficiency. An application should store configuration and initialization data in the registry, but other kinds of data should be stored elsewhere.

Generally, data consisting of more than one or two kilobytes (K) should be stored as a file and referred to by using a key in the registry rather than being stored as a value. Instead of duplicating large pieces of data in the registry, an application should save the data as a file and refer to the file. Executable binary code should never be stored in the registry.

A value entry uses much less registry space than a key. To save space, an application should group similar data together as a structure and store the structure as a value rather than storing each of the structure members as a separate key. (Storing the data in binary form allows an application to store data in one value that would otherwise be made up of several incompatible types.)

+$#A Predefined Keys

An application must open a key before it can add data to the registry. To open a key, an application must supply the handle of another key in the registry that is already open. The system defines standard handles that are always open. An application can use these predefined handles as entry points to the registry.

The system provides two predefined keys at the root of the registry: HKEY_LOCAL_MACHINE and HKEY_USERS. In addition, the system defines two subkeys: HKEY_CLASSES_ROOT (a subkey of HKEY_LOCAL_MACHINE) and HKEY_CURRENT_USER (a subkey of HKEY_USERS). These registry handles are valid for all Win32 implementations of the registry, although the use of the handles may vary from platform to platform.

Predefined keys help an application navigate in the registry and make it possible to develop tools that allow a system administrator to manipulate categories of data. Applications that add data to the registry should always work within the framework of predefined keys, so administrative tools can find and use the new data.

These predefined keys are used as entry points to the registry.

Entry point | Use
HKEY_CLASSES_ROOT | Registry entries subordinate to this key define types (or classes) of documents and the properties associated with those types. Data stored under this key is used by Windows shell applications and by object linking and embedding (OLE) applications.
HKEY_CURRENT_USER | Registry entries subordinate to this key define the preferences of the current user. These preferences include the settings of environment variables, data about program groups, colors, printers, network connections, and application preferences.
HKEY_LOCAL_MACHINE | Registry entries subordinate to this key define the physical state of the computer, including data about the bus type, system memory, and installed hardware and software.
HKEY_USERS | Registry entries subordinate to this key define the default user configuration for new users on the local computer and the user configuration for the current user.

The use of HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and HKEY_USERS varies depending on the implementation of the registry. In addition, other predefined handles have been defined for specific Windows platforms.

+$#A Categories of Data

Before putting data into the registry, an application should divide the data into two categories: computer-specific data and user-specific data. By making this distinction, an application can support multiple users, and yet locate user-specific data over a network and use that data in different locations, allowing location-independent user profile data. (A user profile is a set of configuration data saved for every user.)

When the application is installed, it should record the computer-specific data under the HKEY_LOCAL_MACHINE key. In particular, it should create keys for the company name, product name, and version number, as shown in the following example:

HKEY_LOCAL_MACHINE\Software\MyCompany\MyProduct\1.0

If the application supports OLE, it should record that data under HKEY_LOCAL_MACHINE\Software\Classes.

An application should record user-specific data under the HKEY_CURRENT_USER key, as shown in the following example:

HKEY_CURRENT_USER\Software\MyCompany\MyProduct\1.0\...

+$#A Opening, Creating, and Closing Keys

Before an application can add data to the registry, it must create or open a key. To create or open a key, an application always refers to the key as a subkey of a currently open key. The four predefined keys (HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS, and HKEY_CURRENT_USER) are always open. An application uses the RegOpenKey or RegOpenKeyEx function to open a key and the RegCreateKey or RegCreateKeyEx function to create a key.

An application can use the RegCloseKey function to close a key and write the data it contains into the registry. RegCloseKey does not necessarily write the data to the registry before returning; it can take as much as several seconds for the cache to be flushed to the hard disk. If an application must explicitly write registry data to the hard disk, it can use the RegFlushKey function. RegFlushKey, however, uses many system resources and should be called only when absolutely necessary.

+$#A Writing and Deleting Registry Data

An application can use either the RegSetValue or RegSetValueEx function to associate a value and its data with a key. RegSetValue works only with strings (values having the REG_SZ type). RegSetValueEx, however, can write values with any type of data. Either of these functions can create a key and its value at the same time.

To delete a value from a key, an application can use the RegDeleteValue function. To delete a key, it can use the RegDeleteKey function. A deleted key is not removed until the last handle to it has been closed. Subkeys and values cannot be created under a deleted key.

To change a key's security information, an application can use the RegSetKeySecurity function.

+$#A Retrieving Data from the Registry

To retrieve data from the registry, an application typically enumerates the subkeys of a key until it finds a particular one and then retrieves data from the value or values associated with it. An application can call either the RegEnumKey or RegEnumKeyEx function to enumerate the subkeys of a given key. RegEnumKeyEx returns a subkey and its class, but RegEnumKey returns only the subkey, not the class.

To retrieve detailed data about a particular subkey, an application can call the RegQueryInfoKey function. The RegGetKeySecurity function retrieves a copy of the security descriptor protecting a key.

An application can use the RegEnumValue function to enumerate the values for a given key, and the RegQueryValue or RegQueryValueEx function to retrieve a particular value for a key. An application typically calls RegEnumValue to determine the value names and then RegQueryValueEx to retrieve the data for the names.

RegQueryValue and RegQueryValueEx differ in how they treat unexpanded references to environment variables. If an unnamed value contains an unexpanded environment variable (for example, %PATH%), RegQueryValue expands the variable into the storage buffer provided as one of its parameters. RegQueryValueEx, however, does not expand these references. (Applications can also use the ExpandEnvironmentStrings function to expand environment variables.)

+$#A Registry Files

Applications can save part of the registry in a file and then load the contents of the file back into the registry. A registry file is useful when a large amount of data is being manipulated, when many entries are being made in the registry, or when the data is transitory and must be loaded and then unloaded again. Applications that back up and restore parts of the registry are likely to use registry files.

To save a key and its subkeys and values to a registry file, an application can call the RegSaveKey function. To write the registry file back to the registry, an application can use the RegLoadKey, RegReplaceKey, or RegRestoreKey function.

RegLoadKey loads registry data from a specified file into a specified subkey under HKEY_USERS or HKEY_LOCAL_MACHINE on the calling application's computer or on a remote computer. The function creates the specified subkey if it does not already exist. After calling this function, an application can use the RegUnLoadKey function to restore the registry to its previous state.

RegReplaceKey replaces a key and all its subkeys and values in the registry with the data contained in a specified file. The new data takes effect the next time the system is started.

RegRestoreKey loads registry data from a specified file into a specified key on the calling application's computer or on a remote computer. This function replaces the subkeys and values below the specified key with the subkeys and values that follow the top-level key in the file.

+$#A Using the Registry

The following example demonstrates the use of the RegQueryInfoKey, RegEnumKey, and RegEnumValue functions. The hKey parameter passed to each function is the handle of an open key. This key must be opened before the function call and closed afterward.

// QueryKey - enumerates the subkeys of a given key and the associated

// values and then copies the information about the keys and values

// into a pair of edit controls and list boxes.

// hDlg - dialog box that contains the edit controls and list boxes

// hKey - key whose subkeys and values are to be enumerated

VOID QueryKey(HWND hDlg, HANDLE hKey)

{

CHAR achKey[MAX_PATH];

CHAR achClass[MAX_PATH] = ""; /* buffer for class name */

DWORD cchClassName = MAX_PATH; /* length of class string */

DWORD cSubKeys; /* number of subkeys */

DWORD cbMaxSubKey; /* longest subkey size */

DWORD cchMaxClass; /* longest class string */

DWORD cValues; /* number of values for key */

DWORD cchMaxValue; /* longest value name */

DWORD cbMaxValueData; /* longest value data */

DWORD cbSecurityDescriptor; /* size of security descriptor */

FILETIME ftLastWriteTime; /* last write time */

DWORD i, j;

DWORD retCode, retValue;

CHAR achValue[MAX_VALUE_NAME];

DWORD cchValue = MAX_VALUE_NAME;

CHAR achBuff[80];

// Get the class name and the value count.

RegQueryInfoKey(hKey, /* key handle */

achClass, /* buffer for class name */

&cchClassName, /* length of class string */

NULL, /* reserved */

&cSubKeys, /* number of subkeys */

&cbMaxSubKey, /* longest subkey size */

&cchMaxClass, /* longest class string */

&cValues, /* number of values for this key */

&cchMaxValue, /* longest value name */

&cbMaxValueData, /* longest value data */

&cbSecurityDescriptor, /* security descriptor */

&ftLastWriteTime); /* last write time */

SetDlgItemText(hDlg, IDE_CLASS, achClass);

SetDlgItemInt(hDlg, IDE_CVALUES, cValues, FALSE);

SendMessage(GetDlgItem(hDlg, IDL_LISTBOX),

LB_ADDSTRING, 0, (LONG) "..");

// Enumerate the child keys, looping until RegEnumKey fails. Then

// get the name of each child key and copy it into the list box.

SetCursor(LoadCursor(NULL, IDC_WAIT));

for (i = 0, retCode = ERROR_SUCCESS;

retCode == ERROR_SUCCESS; i++) {

retCode = RegEnumKey(hKey, i, achKey, MAX_PATH);

if (retCode == (DWORD)ERROR_SUCCESS)

SendMessage(GetDlgItem(hDlg, IDL_LISTBOX),

LB_ADDSTRING, 0, (LONG) achKey);

}

SetCursor(LoadCursor (NULL, IDC_ARROW));

// Enumerate the key values.

SetCursor(LoadCursor(NULL, IDC_WAIT));

if (cValues)

for (j = 0, retValue = ERROR_SUCCESS;

j < cValues; j++) {

cchValue = MAX_VALUE_NAME;

achValue[0] = '\0';

retValue = RegEnumValue(hKey, j, achValue,

&cchValue,

NULL,

NULL, /* &dwType, */

NULL, /* &bData, */

NULL); /* &bcData */

if (retValue != (DWORD) ERROR_SUCCESS &&

retValue != ERROR_INSUFFICIENT_BUFFER) {

wsprintf (achBuff,

"Line:%d 0 based index = %d, retValue = %d, "

"ValueLen = %d",

__LINE__, j, retValue, cchValue);

MessageBox (hDlg, achBuff, "Debug", MB_OK);

}

achBuff[0] = '\0';

// Add each value to a list box.

if (!lstrlen(achValue))

lstrcpy(achValue, "<NO NAME>");

wsprintf(achBuff, "%d) %s ", j, achValue);

SendMessage(GetDlgItem(hDlg,IDL_LISTBOX2),

LB_ADDSTRING, 0, (LONG) achBuff);

}

SetCursor(LoadCursor(NULL, IDC_ARROW));

}

+$#A Registry Functions

Following are the functions used with the registry:

RegCloseKey
RegConnectRegistry
RegCreateKey
RegCreateKeyEx
RegDeleteKey
RegDeleteValue
RegEnumKey
RegEnumKeyEx
RegEnumValue
RegFlushKey
RegGetKeySecurity
RegLoadKey
RegNotifyChangeKeyValue
RegOpenKey
RegOpenKeyEx
RegQueryInfoKey
RegQueryValue
RegQueryValueEx
RegReplaceKey
RegRestoreKey
RegSaveKey
RegSetKeySecurity
RegSetValue
RegSetValueEx
RegUnLoadKey

Créé avec HelpNDoc Personal Edition: Produire des livres électroniques facilement


Remote Access Service.rtf

+$#AK Remote Access Service

Remote Access Service (RAS) lets users at remote locations work as if connected directly to a computer network, accessing one or more RAS servers.

+$#A About Remote Access Service

Win32 RAS support enables a RAS client application to start a RAS connection operation, get information about existing RAS connections, and end a RAS connection.

Microsoft® Win32® application programming interface (API) does not currently provide support for applications to administer the Remote Access Server.

This overview discusses the following topics:

• Types of RAS connection operations

• Phonebook files and the information needed to establish a connection

• Connection states during a connection operation

• Notification handlers for tracking the progress of a connection operation

• Paused states during a connection operation that enable the RAS client to collect additional information from the user

• Support for connection operations that require a callback from the remote server

• Ending a RAS connection

+$#A RAS Connection Operations

A RAS client application uses the RasDial function to establish a connection to a RAS server. The RasDial function starts the connection operation, which is then carried out by the Remote Access Connection Manager.

The Remote Access Connection Manager is a service that handles the details of establishing the connection to the remote server. This service also provides the client with status information during the connection operation. The Remote Access Connection Manager starts automatically when an application loads the RASAPI32.DLL.

The RasDial call specifies the following information when it starts a connection operation:

• The connection information that the Remote Access Connection Manager needs to establish the connection.

• An optional notification handler that receives progress notifications during the connection operation. If the RasDial call specifies a notification handler, the call is asynchronous; otherwise, it is synchronous.

• An optional RASDIALEXTENSIONS structure to enable or disable extensions to the RasDial operation. The extensions permit a RAS client to directly enable some modem settings, to control whether RAS uses the prefixes and suffixes in a phonebook entry, and to support paused states during the connection operation.

+$#A Synchronous Operations

When RasDial is invoked as a synchronous operation, the function does not return until the connection has been established or an error occurs. Synchronous mode provides a simple way for a RAS client to establish a connection. The client can simply call RasDial, wait for the function to return, and then call the RasGetConnectStatus function to determine whether the connection operation was successful. Once the connection has been established, the client application can terminate without breaking the connection. If an error occurs, the client application must shut down the connection operation before terminating.

The disadvantage of synchronous mode is that the client does not receive progress notifications as the connection operation proceeds. As a workaround for this lack of progress notifications, a synchronous mode client can use a separate thread that calls RasGetConnectStatus to poll for and display the current state. However, for RAS clients that want to receive progress information, the preferred technique is to invoke RasDial asynchronously.

+$#A Asynchronous Operations

When RasDial is invoked as an asynchronous operation, the function returns immediately. In asynchronous mode, the RasDial call must specify a notification handler that the Remote Access Connection Manager uses to inform the client whenever the connection operation changes states or an error occurs.

The notification handler can be a window to receive messages, or a RasDialFunc or RasDialFunc1 callback function. The Remote Access Connection Manager makes its asynchronous notifications in the context of the thread that made the RasDial call. For this reason, the calling thread must not terminate until the connection operation has been successfully established or an error occurs. As in synchronous mode, the client application can safely terminate once the connection has been established, and it must shut down the connection operation if an error occurs.

+$#A Phonebook Files and Connection Information

A RasDial call must specify the information that the Remote Access Connection Manager needs to establish the connection. Typically, the RasDial call provides the connection information by specifying a phonebook entry in a phonebook file. The connection information in a phonebook entry includes phone numbers, bps rates, user authentication information, and other connection information.

A RAS client uses the parameters of the RasDial function to specify a phonebook file and an entry in that file. The lpszPhonebookPath parameter can specify the name of a phonebook file, or it can be NULL to indicate that the default phonebook file should be used. The lpRasDialParams parameter points to a RASDIALPARAMS structure that specifies the name of the phonebook entry to use.

To display a list of phonebook entries from which the user can select a connection, a RAS client can call the RasEnumEntries function to enumerate the entries in a phonebook file.

To make a connection without using a phonebook entry, the RasDial call can specify an empty string for the szEntryName member of the RASDIALPARAMS structure. The RASDIALPARAMS.szPhoneNumber member must contain the number to call. In this case, the Remote Access Connection Manager uses the first available modem port and default values for all other settings.

+$#A Phonebook Files

Phonebook files provide a standard way to collect and specify the information that the Remote Access Connection Manager needs to establish a remote connection. A phonebook file is a text file that associates entry names with information such as phone numbers, COM ports, and modem settings. Each phonebook entry contains the information needed to establish a RAS connection.

The Win32 API does not currently provide support for creating a phonebook file.

Windows 95: Dial-up networking stores phonebook entries in the registry rather than in a phonebook file. A user or administrator can use the Dial-Up Networking folder to create and edit phonebook entries. You can use the RasCreatePhonebookEntry and RasEditPhonebookEntry functions to create or edit a phonebook entry. These functions display a dialog box in which the user can specify information about the phonebook entry. You can use the RasGetEntryDialParams and RasSetEntryDialParams functions to set or retrieve the connection parameters for a phonebook entry.

Windows NT: The functions for creating and editing a phonebook entry are not supported. Microsoft's remote access application, RASPHONE.EXE, creates a default phonebook file called RASPHONE.PBK, and can also create personal phonebook files. A user or administrator can use RASPHONE.EXE to create and edit entries in a phonebook file. Using RASPHONE.EXE is currently the only safe way to create a phonebook file or to create or edit a phonebook entry.

+$#A User Authentication Information

The Remote Access Connection Manager service on the client computer sends a user name and password to the RAS server on the remote computer. Before it will establish a connection, the remote server uses this information to authenticate the user. By default, the Remote Access Connection Manager sends the user name and password of the currently logged-on user. The RAS client can use the RASDIALPARAMS structure specified in the RasDial call to specify a different user name and password.

If the remote server cannot authenticate the user with the specified information, it can allow the connection operation to enter a paused state to enable the RAS client to collect different authentication data from the user.

+$#A Other Connection Information

The members of the RASDIALPARAMS structure can also specify the following connection information:

• A phone number to override the number in the phonebook entry.

• A callback phone number that the remote server can call back to establish the connection.

• The name of the remote network domain on which the authentication is to occur.

For the callback number and the domain, the RASDIALPARAMS members can either indicate that RAS should use the information in the phonebook entry, or it can provide information that overrides the phonebook data.

A RAS client can use the lpRasDialExtensions parameter of the RasDial function to control whether RAS uses a phone number prefix or suffix specified in a phonebook entry.

+$#A Connection States

During the process of connecting to a remote server, the Remote Access Connection Manager and the RAS server on the remote computer perform several steps to establish the connection. Each of these steps is identified by a connection state. The RASCONNSTATE enumeration is a set of values that correspond to these connection states. The connection states can be divided into the following three groups:

Running states | The running states are the parts of the connection operation that RAS handles automatically, such as connecting to the necessary devices, authenticating the user, and waiting for a callback from the remote server. Unless an error occurs, the RAS client need take no action other than to pass the notification on to the user.
Paused states | The paused states occur when the remote server pauses the connection operation to get additional input from the user. During a paused state, the user can type a callback number, a different user name and password if the user authentication fails, or a new password if the old one has expired.
Terminal states | The terminal states occur when the connection has been successfully established, the connection operation has failed, or the connection has been broken by a RasHangUp call.

There are several mechanisms that a RAS client can use to determine the current state of a connection operation. When a RAS client executes the RasDial function asynchronously, the Remote Access Connection Manager sends progress notifications to the client's notification handler whenever the connection state changes. In addition, the client can use the RasGetConnectStatus function to get the current state of any RAS connection operation.

+$#A Notification Handlers

An asynchronous RasDial call must specify a notification handler. During an asynchronous connection operation, the Remote Access Connection Manager uses the notification handler to inform the RAS client whenever the connection state changes or an error occurs.

The actions performed by a notification handler can be divided into the following categories:

• Handling errors.

• Providing feedback to the user as the connection operation proceeds through the various connection states. See Informational Notifications.

• Handling paused states.

• Signaling the RAS client application when the connection operation has been completed. See Completion Notifications.

There are three types of notification handlers, each of which receives the same basic information: the current connection state and an error code that is nonzero only if an error has occurred.

RasDialFunc | A callback function prototype that receives only the current connection state and error code information.
RasDialFunc1 | A callback function prototype that receives the HRASCONN connection handle and extended error information in addition to the basic information. The connection handle parameter makes RasDialFunc1 useful for client applications that support multiple simultaneous connection operations. This allows the client to specify the same callback function for all operations, and enables the callback function to determine which connection is changing states.
Window handle | A window handle to which RAS sends WM_RASDIALEVENT messages containing the current connection state and error code information. Use this method if your source code must be compatible with 16-bit Windows, because 16-bit Windows does not support either of the callback functions.

The Remote Access Connection Manager suspends the connection operation until the notification handler returns. For this reason, the handler should return as soon as possible unless an error has occurred.

The RasDial function should not be called from within a notification handler. The other remote access functions (RasGetConnectStatus, RasEnumEntries, RasEnumConnections, RasGetErrorString, and RasHangUp) can be called from within a handler.

+$#A Handling RAS Errors

When an error occurs, the Remote Access Connection Manager invokes the client's notification handler. The notification indicates the connection state when the error occurred, and a code that identifies the error. In these cases, the notification handler should call RasHangUp to end the RAS connection.

The RAS client can use the RasGetErrorString function to get a display string describing the error.

+$#A Informational Notifications

For the connection states known as running states, no action is required of the notification handler unless an error occurs. Running states occur during the parts of the connection operation that RAS handles automatically, such as connecting to the necessary devices, authenticating the user, and waiting for a callback from the remote server. The notification is simply a progress report to the client.

The client can choose to pass these informational notifications on to the user. In some running states, the client may want to display additional information. For example, a notification handler that receives a RASCS_ConnectDevice notification can call the RasGetConnectStatus function to get the name and type of the device being connected to. Another example is when the client receives a RASCS_Projected notification. This occurs when the RAS projection phase of the connection operation has been completed. The client can call the RasGetProjectionInfo function to get additional information about the projection. The client can use this information to notify the user as to which network protocols can be used by this connection.

You should avoid writing code that depends on the order or occurrence of particular informational states, because this may vary between platforms.

+$#A Completion Notifications

The Remote Access Connection Manager continues progress notifications until the connection operation has been completed. This occurs in the following situations:

• The connection is established. The handler receives a RASCS_Connected notification. The RAS client application can exit without breaking the connection.

• An error occurs. The handler receives a notification indicating the error and the connection state when the error occurred.

• The connection operation is interrupted by a RasHangUp call.

+$#A Paused States

During a connection operation, there can be times when the remote server cannot proceed without additional information from the local user. Beginning with Microsoft® Windows NT™ version 3.5, the RasDial function supports paused states. A paused state allows the Remote Access Connection Manager to suspend a connection operation so the RAS client application can collect information from the user.

Paused states are useful in the following situations:

• When the user needs to provide a callback number.

• When the user authentication fails, the user can type in a different user name and password.

• When the user's password has expired, the user can provide a new password.

By default, paused state support is disabled. RAS clients that want to support paused states must set the RDEOPTS_PausedStates flag in the RASDIALEXTENSIONS structure passed as a parameter to RasDial.

When a paused state occurs, the Remote Access Connection Manager invokes the client's notification handler. If paused state support is disabled, the notification message indicates an error, and the connection operation fails. If it is enabled, the Connection Manager pauses the connection operation to wait for the RAS client's response. The RAS client can resume the connection operation by a second RasDial call, or terminate it by calling the RasHangUp function.

After getting the user's input, the RAS client restarts the connection operation by calling RasDial again. This second RasDial call must specify the following information:

• The connection handle that was returned by the original RasDial call.

• The same notification handler as the original RasDial call.

• The user's input in the appropriate members of the RASDIALPARAMS structure. Other members of the RASDIALPARAMS structure should have the same information as specified in the original RasDial call.

The second RasDial call cannot be made from within the notification handler.

+$#A Callback Connections

RAS supports connections in which the remote server hangs up and then calls back to the client to establish the connection.

For each user that can connect to a RAS server, the server stores a callback attribute that controls how the connection is made. The default attribute is No Callback, which means that the user can connect to the RAS server without a callback. Alternatively, the administrator of the RAS server can assign to a user either the Preset or Set By Caller callback attribute.

+$#A Preset

For a user assigned the Preset restriction, the administrator specifies a phone number that the RAS server must call back to establish a connection. The user cannot specify a different number, and the connection cannot be made without a callback.

A Preset callback operation is handled automatically by the Remote Access Connection Manager and the remote server. The RAS client application does not need to do anything other than provide feedback to the user when the notification handler is called during the various states of the callback operation.

+$#A Set by Caller

A user assigned the Set By Caller privilege can choose to connect either with or without a callback. The RasDial call uses the szCallBackNumber member of the RASDIALPARAMS structure to indicate the choice.

The szCallBackNumber member can simply specify the callback number; or, to establish the connection without a callback, szCallBackNumber can point to an empty string, "". In either of these cases, the Remote Access Connection Manager handles the connection operation automatically. As with a Preset callback operation, the RAS client does not need to perform any action other than to provide feedback to the user.

If the RasDial call enables paused states, szCallBackNumber can point to an asterisk string, "*", to indicate that the connection operation should enter a paused state to allow the user to type in the callback number. In this case, the connection operation for a Set By Caller user enters a paused state after the remote server has authenticated the user. During the paused state, the RAS client gets the callback number input from the user. The client then resumes the connection operation by making a second RasDial call in which szCallBackNumber specifies the number supplied by the user.

Note that if paused states are not enabled there is a different meaning when szCallBackNumber points to an asterisk string, "*". In this case, the asterisk indicates that the callback number is stored in the phonebook file specified by the RasDial call.

+$#A Disconnecting

When a RAS client application starts a connection operation, the RasDial call receives an HRASCONN connection handle to identify the connection. If the returned handle is not NULL, the client must eventually call the RasHangUp function to end the connection. If an error occurs during the connection operation, the client must call RasHangUp even though the connection was never established.

The application that calls RasHangUp should not exit immediately, because the Remote Access Connection Manager needs time to properly terminate the connection. Instead, the application should wait until the RasGetConnectStatus function returns ERROR_INVALID_HANDLE, indicating that the connection has been deleted.

A RAS client application might need to end a connection even though it does not have the handle returned by RasDial. For example, the application that called RasDial might have exited once the connection was successfully established. In this case, the disconnecting application can use the RasEnumConnections function to get all the current connections. For each connection, RasEnumConnections returns a RASCONN structure containing the HRASCONN connection handle and the phonebook entry name or phone number specified when the connection operation was started. This information can be used to display a list of connections from which the user can select the connection to end.

+$#A Using Remote Access Service

The following topic explains how to use Remote Access Service features in an application.

• Linking to the remote access DLL

+$#A Linking to the Remote Access DLL

If an application links statically to the RASAPI32 DLL, the application will fail to load if Remote Access Service is not installed. A RAS application can load when RAS is not installed by using LoadLibrary to load the DLL, and GetProcAddress to obtain pointers to the RAS functions.

The Win32 RAS functions are in RASAPI32.DLL. The import library for these functions is RASAPI32.LIB. To use the RAS functions, your programs must include the following files:

File | Description
RAS.H | Contains the RAS function prototypes, constants, and structure definitions.
RASERROR.H | Contains the RAS error codes.

+$#AK Remote Access Service Reference

The following functions, strucures, messages, and enumeration types are associated with remote access service.

+$#A Remote Access Service Functions

Use these functions to implement Remote Access Service functionality:

RasCreatePhonebookEntry
RasDial
RasDialFunc
RasDialFunc1
RasEditPhonebookEntry
RasEnumConnections
RasEnumEntries
RasGetConnectStatus
RasGetEntryDialParams
RasGetErrorString
RasGetProjectionInfo
RasHangUp
RasSetEntryDialParams

+$#AK Remote Access Service Structures

Use these structures to implement Remote Access Service functionality:

RASAMB
RASCONN
RASCONNSTATUS
RASDIALEXTENSIONS
RASDIALPARAMS
RASENTRYNAME
RASPPPIP
RASPPPIPX
RASPPPNBF

+$#A Remote Access Service Messages

Use this message to implement Remote Access Service functionality:

WM_RASDIALEVENT

+$#A Remote Access Service Enumeration Types

Use these enumeration types to implement Remote Access Service functionality:

RASCONNSTATE
RASPROJECTION

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


Resources.rtf

+$#AK Resources

A resource is binary data that a resource compiler or developer adds to an application's executable file. A resource can be either standard or defined. The data in a standard resource describes an icon, cursor, menu, dialog box, bitmap, enhanced metafile, font, accelerator table, message-table entry, string-table entry, or version. An application-defined resource, also called a custom resource, contains any data required by a specific application.

+$#A About Resources

This overview describes the functions that enable applications to find a resource in a module; load a resource into memory; add, delete or replace a resource in an executable file; and generate a list of the resources contained in a module. For specific information about the organization of resource data within executable files, refer to the resource formats documentation.

For information about how to create standard resources, refer to the following table.

Resource |  | Topic
Accelerator table |  | Keyboard Accelerators
Bitmap | Bitmaps
Cursor | Cursors
Dialog box | Dialog Boxes
Enhanced metafile | Metafiles
Font | Fonts and Text
Icon | Icons
Menu | Menus (Windows NT)
Message-table entry | Your message-compiler documentation
String-table entry | String Manipulation
Version information |  | File Installation Library

For information about how to include resource data in a Microsoft® Windows® executable file, refer to the documentation for your resource compiler.

+$#A Finding and Loading Resources

Before using a resource, an application must load it into memory. The FindResource and FindResourceEx functions find a resource in a module and return a handle to the binary resource data. FindResource locates a resource by type and name. FindResourceEx locates the resource by type, name, and language. Information about FindResource in this topic also applies to FindResourceEx.

The LoadResource function uses the resource handle returned by FindResource to load the resource into memory. After an application loads a resource by using LoadResource, Windows automatically unloads and reloads the resource as memory conditions and application execution require. Thus, an application need not explicitly unload a resource it no longer needs.

An application can use FindResource and LoadResource to find and load any type of resource, but these functions should be used only if the application must access the binary resource data for subsequent function calls. To use a resource immediately, an application should use one of the following resource-specific functions to find and load resources in one call.

Function |  | Action
FormatMessage |  | Loads and formats a message-table entry.
LoadAccelerators | Loads an accelerator table.
LoadBitmap | Loads a bitmap resource.
LoadCursor | Loads a cursor resource.
LoadIcon | Loads an icon resource.
LoadImage | Loads an icon, cursor, bitmap, or enhanced metafile resource.
LoadMenu | Loads a menu resource.
LoadString |  | Loads a string-table entry.

Before terminating, an application should release the memory occupied by accelerator tables, bitmaps, cursors, icons, and menus by using one of the functions in the following table.

Resource |  | Release function
Accelerator table |  | DestroyAcceleratorTable
Bitmap | DeleteObject
Cursor | DestroyCursor
Icon | DestroyIcon
Menu |  | DestroyMenu

When the application terminates, Windows automatically releases the memory occupied by the other types of resources.

+$#A Adding, Deleting, and Replacing Resources

Applications must frequently add, delete, or replace resources in executable files. Two methods can be used to accomplish these tasks. The first is to edit the resource-definition file, recompile the resources, and add the recompiled resources to the application's executable file. The second method is to copy the resource data directly into the application's executable file.

For example, to localize an English-language application for use in Norway, it may be necessary to replace the English dialog box with one using Norwegian. A developer creates an appropriate dialog box by using a dialog box editor or by writing a template in the resource-definition file. The developer then recompiles the resources and adds the new resources to the application's executable file.

If an appropriate dialog box exists in binary form, however, the developer can copy the data directly to the executable file being localized by using three Windows functions. The BeginUpdateResource function creates an update handle for the executable file whose resources are to be changed. The UpdateResource function uses this handle to add, delete, or replace a resource in the executable file. The EndUpdateResource function closes the handle.

After an update handle to an executable file is created by BeginUpdateResource, an application can use UpdateResource repeatedly to make changes to the resource data. Each call to UpdateResource contributes to an internal list of additions, deletions, and replacements but does not actually write the data to the executable file. Immediately before closing the update handle, EndUpdateResource writes the accumulated changes to the executable file.

Sometimes, an application must copy resources or find resource sizes. The LoadLibrary function provides a module handle to an executable file whose resources are to be copied, and the LockResource function provides a pointer to the resource data in the specified module. The SizeofResource function returns the size, in bytes, of a specified resource.

+$#A Enumerating Resources

Three Windows functions enable an application to obtain lists of resource types, names, and languages in a specified module. The EnumResourceTypes function provides a list of resource types found in the module, the EnumResourceNames function provides the name of each resource within a given type, and the EnumResourceLanguages function provides the language of each resource of a given name and type. These functions and their associated callback functions enable applications to create a list of all resources in a module. This process is described in Creating a Resource List.

+$#A Using Resources

• Updating resources

• Creating a resource list

+$#A Updating Resources

The following example copies a dialog box resource from one executable file, HAND.EXE, to another, FOOT.EXE, by following these steps:

1. The LoadLibrary function loads the executable file HAND.EXE.

2. The FindResource and LoadResource functions locate and load the dialog box resource named If from the file.

3. The LockResource function returns a pointer to the dialog box resource data.

4. The BeginUpdateResource function opens an update handle FOOT.EXE.

5. The UpdateResource function copies the dialog box resource from HAND.EXE to FOOT.EXE.

6. The EndUpdateResource function completes the update.

The following code implements these steps.

HRSRC hResLoad; // handle to loaded resource

HANDLE hExe; // handle to existing .EXE file

HRSRC hRes; // handle/ptr. to res. info. in hExe

HANDLE hUpdateRes; // update resource handle

char *lpResLock; // pointer to resource data

BOOL result;

// Load the .EXE file that contains the dialog box you want to copy.

hExe = LoadLibrary("hand.exe");

if (hExe == NULL) {

ErrorHandler("Could not load exe.");

}

// Locate the dialog box resource in the .EXE file.

hRes = FindResource(hExe, "AboutBox", RT_DIALOG);

if (hRes == NULL) {

ErrorHandler("Could not locate dialog box.");

}

// Load the dialog box into global memory.

hResLoad = LoadResource(hExe, hRes);

if (hResLoad == NULL) {

ErrorHandler("Could not load dialog box.");

}

// Lock the dialog box into global memory.

lpResLock = LockResource(hRes);

if (lpResLock == NULL) {

ErrorHandler("Could not lock dialog box.");

}

// Open the file to which you want to add the dialog box resource.

hUpdateRes = BeginUpdateResource("foot.exe", FALSE);

if (hUpdateRes == NULL) {

ErrorHandler("Could not open file for writing.");

}

// Add the dialog box resource to the update list.

result = UpdateResource(hUpdateRes, // update resource handle

RT_DIALOG, // change dialog box resource

"AboutBox", // dialog box name

MAKELANGID(LANG_NEUTRAL,

SUBLANG_NEUTRAL), // neutral language ID

lpResLock, // ptr to resource info

SizeofResource(hExe, hRes)); // size of resource info.

if (result == FALSE) {

ErrorHandler("Could not add resource.");

}

// Write changes to FOOT.EXE and then close it.

if (!EndUpdateResource(hUpdateRes, FALSE)) {

ErrorHandler("Could not write changes to file.");

}

// Clean up.

if (!FreeLibrary(hExe)) {

ErrorHandler("Could not free executable.");

}

+$#A Creating a Resource List

The following example creates a list of every resource in the HAND.EXE file. The list is written to the RESINFO.TXT file.

The code demonstrates how to load the executable file, create a file in which to write resource information, and call the EnumResourceTypes function to send each resource type found in the module to the application-defined callback function EnumTypesFunc. See EnumResTypeProc for information on callback functions of this type. This callback function uses the EnumResourceNames function to pass the name of every resource within the specified type to another application-defined callback function, EnumNamesFunc. See EnumResNameProc for information on callback functions of this type. EnumNamesFunc uses the EnumResourceLanguages function to pass the language of every resource of the specified type and name to a third callback function, EnumLangsFunc. See EnumResLangProc for information on callback functions of this type. EnumLangsFunc writes information about the resource of the specified type, name, and language to the RESINFO.TXT file.

char szBuffer[80]; // print buffer for EnumResourceTypes

DWORD cbWritten; // number of bytes written to res. info. file

int cbString; // length of string in sprintf

// Declare callback functions.

BOOL EnumTypesFunc(

HANDLE hModule,

LPTSTR lpType,

LONG lParam);

BOOL EnumNamesFunc(

HANDLE hModule,

LPCTSTR lpType,

LPTSTR lpName,

LONG lParam);

BOOL EnumLangsFunc(

HANDLE hModule,

LPCTSTR lpType,

LPCTSTR lpName,

WORD wLang,

LONG lParam);

// Load the .EXE whose resources you want to list.

hExe = LoadLibrary("hand.exe");

if (hExe == NULL) {

ErrorHandler("Could not load .EXE.");

}

// Create a file to contain the resource info.

hFile = CreateFile("resinfo.txt", // name of file

GENERIC_READ | GENERIC_WRITE, // access mode

0, // share mode

(LPSECURITY_ATTRIBUTES) NULL, // no security

CREATE_ALWAYS, // create flags

FILE_ATTRIBUTE_NORMAL, // file attributes

(HANDLE) NULL); // no template

if (hFile == INVALID_HANDLE_VALUE) {

ErrorHandler("Could not open file.");

}

// Find all of the loaded file's resources.

cbString = sprintf(szBuffer,

"The file contains the following resources:\n\n");

WriteFile(hFile, // file to hold resource info.

szBuffer, // what to write to the file

(DWORD) cbString, // number of bytes in szBuffer

&cbWritten, // number of bytes written

NULL); // no overlapped I/O

EnumResourceTypes(hExe, // module handle

(ENUMRESTYPEPROC)EnumTypesFunc, // callback function

0); // extra parameter

// Unload the executable file whose resources were

// enumerated and close the file created to contain

// the resource information.

FreeLibrary(hExe);

CloseHandle(hFile);

// FUNCTION: EnumTypesFunc(HANDLE, LPSTR, LONG)

//

// PURPOSE: Resource type callback

BOOL EnumTypesFunc(

HANDLE hModule, // module handle

LPTSTR lpType, // address of resource type

LONG lParam) // extra parameter, could be

// used for error checking

{

int cbString;

// Write the resource type to a resource information file.

// The type may be a string or an unsigned decimal

// integer, so test before printing.

if ((ULONG)lpType & 0xFFFF0000) {

cbString = sprintf(szBuffer, "Type: %s\n", lpType);

}

else {

cbString = sprintf(szBuffer, "Type: %u\n", (USHORT)lpType);

}

WriteFile(hFile, szBuffer, (DWORD) cbString,

&cbWritten, NULL);

// Find the names of all resources of type lpType.

EnumResourceNames(hModule,

lpType,

(ENUMRESNAMEPROC)EnumNamesFunc,

0);

return TRUE;

}

// FUNCTION: EnumNamesFunc(HANDLE, LPSTR, LPSTR, LONG)

//

// PURPOSE: Resource name callback

BOOL EnumNamesFunc(

HANDLE hModule, // module handle

LPCTSTR lpType, // address of resource type

LPTSTR lpName, // address of resource name

LONG lParam) // extra parameter, could be

// used for error checking

{

int cbString;

// Write the resource name to a resource information file.

// The name may be a string or an unsigned decimal

// integer, so test before printing.

if ((ULONG)lpName & 0xFFFF0000) {

cbString = sprintf(szBuffer, "\tName: %s\n", lpName);

}

else {

cbString = sprintf(szBuffer, "\tName: %u\n",

(USHORT)lpName);

}

WriteFile(hFile, szBuffer, (DWORD) cbString,

&cbWritten, NULL);

// Find the languages of all resources of type

// lpType and name lpName.

EnumResourceLanguages(hModule,

lpType,

lpName,

(ENUMRESLANGPROC)EnumLangsFunc,

0);

return TRUE;

}

// FUNCTION: EnumLangsFunc(HANDLE, LPSTR, LPSTR, WORD, LONG)

//

// PURPOSE: Resource language callback

BOOL EnumLangsFunc(

HANDLE hModule, // module handle

LPCTSTR lpType, // address of resource type

LPCTSTR lpName, // address of resource name

WORD wLang, // resource language

LONG lParam) // extra parameter, could be

used for error checking

{

HANDLE hResInfo;

char szBuffer[80];

int cbString = 0;

hResInfo = FindResourceEx(hModule, lpType, lpName, wLang);

// Write the resource language to the resource information file.

cbString = sprintf(szBuffer, "\t\tLanguage: %u\n",

(USHORT)wLang);

WriteFile(hFile, szBuffer, (DWORD) cbString,

&cbWritten, NULL);

// Write the resource handle and size to buffer.

cbString = sprintf(szBuffer,

"\t\thResInfo == %lx, Size == %lu\n\n",

hResInfo,

SizeofResource(hModule, hResInfo));

WriteFile(hFile, szBuffer, (DWORD) cbString,

&cbWritten, NULL);

return TRUE;

}

+$#A Resource Functions

Following are the functions used with resources.

BeginUpdateResource
CopyImage
EndUpdateResource
EnumResLangProc
EnumResNameProc
EnumResourceLanguages
EnumResourceNames
EnumResourceTypes
EnumResTypeProc
FindResource
FindResourceEx
LoadImage
LoadResource
LockResource
SizeofResource
UpdateResource

Obsolete or Deleted Functions

FreeResource
SetResourceHandler
UnlockResource

Créé avec HelpNDoc Personal Edition: Créer des livres électroniques facilement


Rich Edit Controls.rtf

+$#AK Rich Edit Controls

A rich edit control is a window in which the user can enter and edit text. The text can be assigned character and paragraph formatting, and can include embedded OLE objects. Rich edit controls provide a programming interface for formatting text. However, an application must implement any user interface components necessary to make formatting operations available to the user.

+$#A About Rich Edit Controls

Rich edit controls support almost all of the messages and notification messages used with multiline edit controls. Thus, applications that already use edit controls can be easily changed to use rich edit controls. Additional messages and notifications enable applications to access the functionality unique to rich edit controls. For information about edit controls, see Edit Controls.

An application can send messages to a rich edit control to perform such operations as formatting text, printing, and saving. An application can process notification messages to monitor events in a rich edit control. For example, an application can process notifications to filter keyboard and mouse input, to permit or deny changes to protected text, or to resize the control as needed to fit its content.

You create a rich edit control by using the CreateWindowEx function, specifying the "RichEdit" window class. Because the common control library registers this window class, you must call the LoadLibrary function to ensure that the RICHED32.DLL library is loaded before the rich edit control is created.

Rich edit controls support most of the window styles used with edit controls as well as additional styles. You should specify the ES_MULTILINE window style if you want to allow more than one line of text in the control.

+$#A Text Formatting

An application can send messages to a rich edit control to format characters and paragraphs and to retrieve formatting information. Paragraph formatting attributes include alignment, tabs, indents, and numbering. For characters, you can specify typeface, size, color, and effects such as bold, italic, and protected.

You can apply paragraph formatting by using the EM_SETPARAFORMAT message. To determine the current paragraph formatting for the selected text, use the EM_GETPARAFORMAT message. The PARAFORMAT structure is used with both messages to specify paragraph formatting attributes.

You can apply character formatting by using the EM_SETCHARFORMAT message. To determine the current character formatting for the selected text, you can use the EM_GETCHARFORMAT message. The CHARFORMAT structure is used with both messages to specify character attributes.

You can also use EM_SETCHARFORMAT and EM_GETCHARFORMAT messages to set and retrieve the default character formatting, which is the formatting applied to any subsequently inserted characters. For example, if an application sets the default character formatting to bold and the user then types a character, that character is bold.

The default character formatting is applied to newly inserted text only if the current selection is empty. Otherwise, the new text assumes the character formatting of the text it replaces. If the selection changes, the default character formatting changes to match the first character in the new selection.

The protected character effect is unique in that it does not change the appearance of text. If the user attempts to modify protected text, a rich edit control sends its parent window an EN_PROTECTED notification message, allowing the parent window to allow or prevent the change. To receive this notification message, you must enable it by using the EM_SETEVENTMASK message.

Foreground color is a character attribute, but background color is a property of the rich edit control. To set the background color, use the EM_SETBKGNDCOLOR message.

+$#A Current Selection in a Rich Edit Control

The user can select text in a rich edit control by using the mouse or the keyboard. The current selection is the range of selected characters, or the position of the insertion point if no characters are selected. An application can get information about the current selection, set the current selection, determine when the current selection changes, and show or hide the selection highlight.

To determine the current selection in a rich edit control, use the EM_EXGETSEL message. To set the current selection, use the EM_EXSETSEL message. The CHARRANGE structure is used with both messages and specifies a range of characters. To retrieve information about the contents of the current selection, you can use the EM_SELECTIONTYPE message.

An application can detect when the current selection changes by processing the EN_SELCHANGE notification message. The notification message specifies a SELCHANGE structure containing information about the new selection. A rich edit control sends this notification message only if you enable it by using the EM_SETEVENTMASK message.

By default, a rich edit control shows and hides the selection highlight when it gains and loses the focus. You can show or hide the selection highlight at any time by using the EM_HIDESELECTION message. For example, an application might provide a Search dialog box to find text in a rich edit control. The application might select matching text without closing the dialog box, in which case it must use the EM_HIDESELECTION message to highlight the selection.

As with edit controls, you can specify the ES_NOHIDESEL window style to prevent a rich edit control from hiding the selection highlight when it loses the focus. You can also use the EM_HIDESELECTION message to change the ES_NOHIDESEL window style after a rich edit control is created.

Instead of using the EM_EXGETSEL and EM_EXSETSEL messages, you retrieve and set the current selection by using the EM_GETSEL and EM_SETSEL edit control messages. The EM_GETSEL message packs two 16-bit character indexes into its 32-bit return value and, therefore, works only for selections that fall entirely within the first 64K. However, a rich edit control will never contain more than 32K of text, unless you extend this limit by using the EM_EXLIMITTEXT message. For selections that extend beyond the first 64K of text, the EM_GETSEL message returns − 1.

+$#A Rich Edit Text Operations

An application can send messages to retrieve or find text in a rich edit control. You can retrieve either the selected text or a specified range of text.

To get the selected text in a rich edit control, use the EM_GETSELTEXT message. The text is copied to the specified character array. You must ensure that the array is large enough to hold the selected text plus a terminating null character.

To retrieve a specified range of text, use the EM_GETTEXTRANGE message. The TEXTRANGE structure used with this message specifies the text range to retrieve and points to a character array that receives the text. Here again, the application must ensure that the array is large enough for the specified text plus a terminating null character.

You can search for a string in a rich edit control by using the EM_FINDTEXT message. The FINDTEXT structure used with this message specifies the text range to search and the string to search for. You can also specify such options as whether the search is case-sensitive.

+$#A Word and Line Breaks

A rich edit control calls a function called a word break procedure to find breaks between words and to determine where it can break lines. The control uses this information when performing word wrap operations and when processing the CTRL+LEFT and CTRL+RIGHT key combinations. An application can send messages to a rich edit control to replace the default word break procedure, to retrieve word break information, and to determine what line a given character falls on.

Word break procedures for rich edit controls are similar to those for edit controls, but they have additional capabilities. Word break procedures for both kinds of controls can determine whether a character is a delimiter and can find the nearest word break before or after the specified position. A delimiter is a character that marks the end of a word, such as a space. In an edit control, word breaks occur only after delimiters.

Word break procedures for rich edit controls also group characters into character classes, each identified by a value in the range 0x00 through 0x0F. Word breaks occur either after delimiters or between characters of different classes. Thus, a word break procedure with different classes for alphanumeric and punctuation characters would find two word breaks in the string "WIN.COM".

A character's class can be combined with zero or more word break flags to form an eight-bit value. When performing word wrap operations, a rich edit control uses word break flags to determine where it can break lines. There are the following word break flags.

WBF_BREAKAFTER | Lines may be broken after the character.
WBF_BREAKLINE | The character is a delimiter. Delimiters mark the ends of words. Lines may be broken after delimiters.
WBF_ISWHITE | The character is a white space character. Trailing white space characters are not included in the length of a line when wrapping.

The WBF_BREAKAFTER value is used to allow wrapping after a character that does not mark the end of a word, such as a hyphen.

You can replace a rich edit control's default word break procedure with your own by using the EM_SETWORDBREAKPROC message. For more information about word break procedures, see the description of the EditWordBreakProc function.

Alternately, you can use the EM_SETWORDBREAKPROCEX message to replace the default extended word break procedure with an EditWordBreakProcEx function. This function provides additional information about the text, such as the character set. You can use the EM_GETWORDBREAKPROCEX message to retrieve the address of the current extended word break procedure.

You can use the EM_FINDWORDBREAK message to find word breaks or to determine a character's class and word break flags. In turn, the control calls its word break procedure to get the requested information.

To determine which line a given character falls on, you can use the EM_EXLINEFROMCHAR message.

+$#A Rich Edit Clipboard Operations

An application can paste the contents of the clipboard into a rich edit control using either the best available clipboard format or a specific clipboard format. You can also determine whether a rich edit control is capable of pasting a clipboard format.

As with an edit control, you can copy or cut the contents of the current selection by using the WM_COPY or WM_CUT message. Similarly, you can paste the contents of the clipboard into a rich edit control by using the WM_PASTE message. The control pastes the first available format that it recognizes, which presumably is the most descriptive format.

To paste a specific clipboard format, you can use the EM_PASTESPECIAL message. This message is useful for applications with a Paste Special command that enables the user to select the clipboard format. You can use the EM_CANPASTE message to determine whether a given format is recognized by the control.

You can also use the EM_CANPASTE message to determine whether any available clipboard format is recognized by a rich edit control. This message is useful when processing the WM_INITMENUPOPUP message. An application might enable or gray its Paste command depending on whether the control can paste any available format.

Rich edit controls register two clipboard formats: rich-text format and a format called RichEdit Text and Objects. An application can register these formats by using the RegisterClipboardFormat function, specifying the CF_RTF and CF_RETEXTOBJ values.

+$#A Streams

You can use streams to transfer data into or out of a rich edit control. A stream is defined by an EDITSTREAM structure, which specifies a buffer and an application-defined callback function.

To read data into a rich edit control (that is, stream the data in), use the EM_STREAMIN message. The control repeatedly calls the application's callback function, which transfers a portion of the data into the buffer each time.

To save the contents of a rich edit control (that is, stream the data out), you can use the EM_STREAMOUT message. The control repeatedly writes to the buffer and then calls the application's callback function. For each call, the callback function saves the contents of the buffer.

+$#A Printing

You can send messages to a rich edit control to render its output for a specified device, such as a printer. You can also specify the output device for which a rich edit control formats its text.

To format part of a rich edit control's contents for a specific device, you can use the EM_FORMATRANGE message. The FORMATRANGE structure used with this message specifies the range of text to format as well as the device context (DC) for the target device.

After formatting text for an output device, you can send the output to the device by using the EM_DISPLAYBAND message. By repeatedly using the EM_FORMATRANGE and EM_DISPLAYBAND messages, an application that prints the contents of a rich edit control can implement banding. (Banding is division of output into smaller parts for pringing purposes).

You can use the EM_SETTARGETDEVICE message to specify the target device for which a rich edit control formats its text. This message is useful for WYSIWYG (what you see is what you get) formatting, in which an application positions text using the default printer's font metrics instead of the screen's.

+$#A Bottomless Rich Edit Controls

An application can resize a rich edit control as needed so that it is always the same size as its contents. A rich edit control supports this so-called "bottomless" functionality by sending its parent window an EN_REQUESTRESIZE notification message whenever the size of its contents changes.

When processing the EN_REQUESTRESIZE notification message, an application should resize the control to the dimensions in the specified REQRESIZE structure. An application might also move any information near the control to accommodate the control's change in height. To resize the control, you can use the SetWindowPos function.

You can force a bottomless rich edit control to send an EN_REQUESTRESIZE notification message by using the EM_REQUESTRESIZE message. This message can be useful when processing the WM_SIZE message.

To receive EN_REQUESTRESIZE notification messages, you must enable the notification by using the EM_SETEVENTMASK message.

+$#A Miscellaneous Notification Messages

A rich edit control's parent window can process notification messages to monitor events affecting the control. Rich edit controls support all of the notification messages used with edit controls as well as several additional ones. You can determine which notification messages a rich edit control sends its parent window by setting its event mask.

To set the event mask for a rich edit control, use the EM_SETEVENTMASK message. You can retrieve the current event mask for a rich edit control by using the EM_GETEVENTMASK message.

A rich edit control's parent window can filter all keyboard and mouse input to the control by processing the EN_MSGFILTER notification message. The parent window can prevent the keyboard or mouse message from being processed or can change the message by modifying the specified MSGFILTER structure.

An application can process the EN_PROTECTED notification message to detect when the users attempts to modify protected text. To mark a range of text as protected, you can set the protected character effect. For more information, see Text Formatting.

You can enable the user to drop files in a rich edit control by processing the EN_DROPFILES notification message. The specified ENDROPFILES structure contains information about the files being dropped.

+$#A Unsupported Edit Control Functionality

Rich edit controls support most but not all of the functionality of multiline edit controls. This section lists the edit control messages and window styles that are not supported by rich edit controls.

The following messages are processed by edit controls but not by rich edit controls.

Unsupported message | Comments
EM_FMTLINES | Not supported.
EM_GETHANDLE | Rich edit controls do not store text as a simple array of characters.
EM_GETMARGINS | Not supported.
EM_GETPASSWORDCHAR | The ES_PASSWORD style is not supported.
EM_SETHANDLE | Rich edit controls do not store text as a simple array of characters.
EM_SETMARGINS | Not supported.
EM_SETPASSWORDCHAR | The ES_PASSWORD style is not supported.
EM_SETRECTNP | Not supported.
EM_SETTABSTOPS | The EM_SETPARAFORMAT message is used instead.
WM_CTLCOLOR | The EM_SETBKGNDCOLOR message is used instead.
WM_GETFONT | The EM_GETCHARFORMAT message is used instead.

The following window styles are used with multiline edit controls but not with rich edit controls:

ES_LOWERCASE | ES_PASSWORD
ES_OEMCONVERT | ES_UPPERCASE

+$#AK Rich Edit Control Reference

The messages, notification messages, and structures associated with rich edit controls are grouped as follows. Some of these application programming interface (API) elements are also used with edit controls.

Formatting

CHARFORMAT
EM_GETCHARFORMAT
EM_GETPARAFORMAT
EM_GETRECT
EM_SETBKGNDCOLOR
EM_SETCHARFORMAT
EM_SETEVENTMASK
EM_SETPARAFORMAT
EM_SETRECT
PARAFORMAT

Selection and Hit Testing

CHARRANGE
EM_CHARFROMPOS
EM_EXGETSEL
EM_EXSETSEL
EM_GETFIRSTVISIBLELINE
EM_GETSEL
EM_HIDESELECTION
EM_POSFROMCHAR
EM_SELECTIONTYPE
EM_SETSEL
EN_SELCHANGE
SELCHANGE

Text Operations

EM_EXLIMITTEXT
EM_FINDTEXT
EM_GETLIMITTEXT
EM_GETSELTEXT
EM_GETTEXTRANGE
EM_REPLACESEL
EM_SETLIMITTEXT
FINDTEXT
TEXTRANGE

Word and Line Breaks

EM_EXLINEFROMCHAR
EM_FINDWORDBREAK
EM_GETWORDBREAKPROC
EM_SETWORDBREAKPROC
EM_GETWORDBREAKPROCEX
EM_SETWORDBREAKPROCEX
EditWordBreakProcEx

Lines and Scrolling

EM_GETLINE
EM_GETLINECOUNT
EM_GETTHUMB
EM_LINEFROMCHAR
EM_LINEINDEX
EM_LINELENGTH
EM_LINESCROLL
EM_SCROLL
EM_SCROLLCARET

Editing Operations

EM_CANPASTE
EM_CANUNDO
EM_EMPTYUNDOBUFFER
EM_PASTESPECIAL
EM_UNDO

Streams

EDITSTREAM
EM_STREAMIN
EM_STREAMOUT

Printing

EM_DISPLAYBAND
EM_FORMATRANGE
EM_SETTARGETDEVICE
FORMATRANGE

Bottomless Rich Edit Controls

EM_REQUESTRESIZE
EN_REQUESTRESIZE
REQRESIZE

OLE Interfaces

IRichEditOle
IRichEditOleCallback

Miscellaneous

EM_GETEVENTMASK
EM_GETIMECOLOR
EM_GETIMEOPTIONS
EM_GETMODIFY
EM_GETOPTIONS
EM_GETPUNCTUATION
EM_GETWORDWRAPMODE
EM_SETEVENTMASK
EM_SETIMECOLOR
EM_SETIMEOPTIONS
EM_SETEVENTMASK
EM_SETMODIFY
EM_SETREADONLY
EM_SETOPTIONS
EM_SETPUNCTUATION
EM_SETWORDWRAPMODE
EN_CHANGE
EN_CORRECTTEXT
EN_DROPFILES
EN_ERRSPACE
EN_HSCROLL
EN_IMECHANGE
EN_KILLFOCUS
EN_MAXTEXT
EN_MSGFILTER
EN_OLEOPFAILED
EN_PROTECTED
EN_SAVECLIPBOARD
EN_SETFOCUS
EN_STOPNOUNDO
EN_UPDATE
EN_VSCROLL
IMC_GETCOMPOSITIONCOLOR
IMC_SETCOMPOSITIONCOLOR
WM_CONTEXTMENU
WM_CTLCOLOREDIT
COMPCOLOR
ENCORRECTTEXT
ENDROPFILES
ENOLEOPFAILED
ENPROTECTED
ENSAVECLIPBOARD
PUNCTUATION
MSGFILTER

+$#AK Rich Edit Callback Functions

The following callback function is unique to rich edit controls:

EditWordBreakProcEx

+$#A Rich Edit Messages

The following messages are unique to rich edit controls.

EM_CANPASTE
EM_DISPLAYBAND
EM_EXGETSEL
EM_EXLIMITTEXT
EM_EXLINEFROMCHAR
EM_EXSETSEL
EM_FINDTEXT
EM_FINDTEXTEX
EM_FINDWORDBREAK
EM_FORMATRANGE
EM_GETCHARFORMAT
EM_GETEVENTMASK
EM_GETIMECOLOR
EM_GETIMEOPTIONS
EM_GETOLEINTERFACE
EM_GETOPTIONS
EM_GETPARAFORMAT
EM_GETPUNCTUATION
EM_GETSELTEXT
EM_GETTEXTRANGE
EM_GETWORDBREAKPROCEX
EM_GETWORDWRAPMODE
EM_HIDESELECTION
EM_PASTESPECIAL
EM_REQUESTRESIZE
EM_SELECTIONTYPE
EM_SETBKGNDCOLOR
EM_SETCHARFORMAT
EM_SETEVENTMASK
EM_SETIMECOLOR
EM_SETIMEOPTIONS
EM_SETOLEINTERFACE
EM_SETOPTIONS
EM_SETPARAFORMAT
EM_SETPUNCTUATION
EM_SETTARGETDEVICE
EM_SETWORDBREAKPROCEX
EM_SETWORDWRAPMODE
EM_STREAMIN
EM_STREAMOUT
IMC_GETCOMPOSITIONCOLOR
IMC_SETCOMPOSITIONCOLOR
WM_CONTEXTMENU

+$#A Rich Edit Notification Messages

Rich edit controls support most of the notification messages used with edit controls, plus the following notification messages. A rich edit control sends the following notification messages only if they have been enabled by using the EM_SETEVENTMASK message.

EN_CORRECTTEXT
EN_DROPFILES
EN_IMECHANGE
EN_MSGFILTER
EN_OLEOPFAILED
EN_PROTECTED
EN_REQUESTRESIZE
EN_SAVECLIPBOARD
EN_SELCHANGE
EN_STOPNOUNDO

+$#A Rich Edit Structures

The following structures are used with rich edit controls.

CHARFORMAT
CHARRANGE
COMPCOLOR
EDITSTREAM
ENDROPFILES
ENOLEOPFAILED
ENPROTECTED
ENSAVECLIPBOARD
FINDTEXT
FINDTEXTEX
FORMATRANGE
MSGFILTER
PARAFORMAT
PUNCTUATION
REOBJECT
REPASTESPECIAL
REQRESIZE
SELCHANGE
TEXTRANGE

+$#A Rich Edit OLE Interfaces

The rich edit control supports the client functionality defined by OLE. The control provides full support for most OLE client features. It does not support linking to its contents. The client is responsible for dialogs and error messages, managing storage for OLE objects, and document and application window level in-place activation support. The client can use the EM_GETOLEINTERFACE message to obtain an IRichEditOle interface from the rich edit control which allows it to manage objects. The client uses the EM_SETOLEINTERFACE message to register an IRichEditOleCallback interface that the control uses to obtain the required interfaces and storage.

The following OLE interfaces are used with rich text edit controls:

IRichEditOle
IRichEditOleCallback

+$#A Rich Edit Constants

The following sections describe constants used with rich edit controls.

+$#A Rich Edit Control Styles

The following window styles are unique to rich edit controls:

Style | Description
ES_DISABLENOSCROLL | Disables scrollbars instead of hiding them when they are not needed.
ES_EX_NOCALLOLEINIT | Prevents the control from calling the OleInitialize function when created. Useful only in dialog templates because CreateWindowEx does not accept this style.
ES_NOIME | Disables the input method editor (IME) operation. Available for Asian-languages only.
ES_SAVESEL | Preserves the selection when the control loses the focus. By default, the entire contents of the control are selected when it regains the focus.
ES_SELFIME | Directs the rich edit control to allow the application to handle all IME operations. Available for Asian-languages only.
ES_SUNKEN | Displays the control with a sunken border style so that the rich edit control appears recessed into its parent window. Applications developed for Windows 95 should use WS_EX_CLIENTEDGE instead of ES_SUNKEN.
ES_VERTICAL | Draws text and objects in a vertical direction. Available for Asian-languages only.

Rich edit controls also support the following edit control styles. To allow more than one line of text, you must specify the ES_MULTILINE style.

ES_AUTOHSCROLL | ES_NOHIDESEL
ES_AUTOVSCROLL | ES_READONLY
ES_CENTER | ES_RIGHT
ES_LEFT | ES_WANTRETURN
ES_MULTILINE |

Rich edit controls do not support the following edit control styles:

ES_LOWERCASE | ES_PASSWORD
ES_OEMCONVERT | ES_UPPERCASE

+$#A Rich Edit Control Event Mask Flags

The event mask specifies which notification messages a rich edit control sends its parent window. The event mask can be zero or more of these values:

Value | Meaning
ENM_CHANGE | Sends EN_CHANGE notifications.
ENM_CORRECTTEXT | Sends EN_CORRECTTEXT notifications.
ENM_DROPFILES | Sends EN_DROPFILES notifications.
ENM_KEYEVENTS | Sends EN_MSGFILTER notifications for keyboard events.
ENM_MOUSEEVENTS | Sends EN_MSGFILTER notifications for mouse events.
ENM_PROTECTED | Sends EN_PROTECTED notifications.
ENM_RESIZEREQUEST | Sends EN_REQUESTRESIZE notifications.
ENM_SCROLL | Sends EN_HSCROLL notifications.
ENM_SELCHANGE | Sends EN_SELCHANGE notifications.
ENM_UPDATE | Sends EN_UPDATE notifications.

The default event mask is ENM_NONE in which case no notification messages are sent to the parent window. You can retrieve and set the event mask for a rich edit control by using the ENM_GETEVENTMASK and ENM_SETEVENTMASK messages.

Créé avec HelpNDoc Personal Edition: Générateur complet de livres électroniques ePub


Screen Saver Library.rtf

+$#AK Screen Saver Library

The Microsoft® Win32® application programming interface (API) supports special applications called screen savers that start when the mouse and keyboard have been idle for a period of time. Screen savers exist for two main reasons:

• To avoid phosphor burn caused by static images on a screen

• To conceal sensitive information left on a screen

+$#A About Screen Savers

The Desktop application in Windows Control Panel lets users select from a list of screen savers, specify how much time should elapse before the screen saver is started, configure screen savers, and preview screen savers. Screen savers are loaded automatically when Windows starts or when a user activates the screen saver feature by using Control Panel.

Once a screen saver is chosen, Windows monitors keystrokes and mouse movements, starting the screen saver after a period of inactivity. However, Windows does not start the screen saver if any of the following conditions exist:

• The active application is not a Windows-based application.

• A computer-based training (CBT) window is present.

• The active application receives the WM_SYSCOMMAND message with wParam set to the SC_SCREENSAVE value, but does not pass the message to the DefWindowProc function.

Screen savers contain specific exported functions, resource definitions, and variable declarations. The static-link library SCRNSAVE.LIB contains the main function and other startup code required for a screen saver. When a screen saver starts, the startup code in SCRNSAVE.LIB creates a full-screen window. The window class for this window is declared as follows.

WNDCLASS cls;

cls.hCursor = NULL;

cls.hIcon = LoadIcon(hInst, MAKEINTATOM(ID_APP));

cls.lpszMenuName = NULL;

cls.lpszClassName = "WindowsScreenSaverClass";

cls.hbrBackground = GetStockObject(BLACK_BRUSH);

cls.hInstance = hInst;

cls.style = CS_VREDRAW | CS_HREDRAW |

CS_SAVEBITS | CS_DBLCLKS;

cls.lpfnWndProc = (WNDPROC) ScreenSaverProc;

cls.cbWndExtra = 0;

cls.cbClsExtra = 0;

To create a screen saver, most developers create a source-code module containing three required functions and link them with SCRNSAVE.LIB. A screen saver module is responsible only for configuring itself and for providing visual effects.

One of the three required functions in a screen saver module is ScreenSaverProc. This function processes specific messages and passes any unprocessed messages back to SCRNSAVE.LIB. Following are some of the typical messages processed by ScreenSaverProc.

Message | Meaning
WM_CREATE | Retrieve any initialization data from the file REGEDIT.INI. Set a window timer for the screen saver window. Perform any other required initialization.
WM_ERASEBKGND | Erase the screen saver window and prepare for subsequent drawing operations.
WM_TIMER | Perform drawing operations.
WM_DESTROY | Destroy the timer(s) created when the application processed the WM_CREATE message. Perform any additional required cleanup.

ScreenSaverProc passes unprocessed messages to SCRNSAVE.LIB by calling the DefScreenSaverProc function. The following table describes how this function processes various messages.

Message | Action
WM_SETCURSOR | Sets the cursor to the null cursor, removing it from the screen.
WM_PAINT | Paints the screen background.
WM_LBUTTONDOWN | Terminates the screen saver.
WM_MBUTTONDOWN | Terminates the screen saver.
WM_RBUTTONDOWN | Terminates the screen saver.
WM_KEYDOWN | Terminates the screen saver.
WM_MOUSEMOVE | Terminates the screen saver.
WM_ACTIVATE | If wParam is FALSE, terminates the screen saver.

The second required function in a screen saver module is ScreenSaverConfigureDialog. This function displays a dialog box that enables the user to configure the screen saver. (Thus, an application must provide a corresponding dialog box template.) Windows displays the configuration dialog box when the user chooses the Setup button in the Screen Saver box in the Desktop application in Control Panel. The data entered by the user with the configuration dialog box is stored in the REGEDIT.INI file.

The third required function in a screen saver module is RegisterDialogClasses. This function must be called by all screen saver applications. However, applications that do not require special windows or custom controls in the configuration dialog box can simply return TRUE. Applications requiring custom controls or special windows should use this function to register the corresponding window classes.

In addition to creating a module that supports the three functions just described, a screen saver should supply an icon. This icon is visible only when the screen saver is run as a standalone application. (To be run by Control Panel, a screen saver must have the .SCR filename extension; to be run as a standalone application, it must have the .EXE filename extension.) The icon must be identified in the screen saver's resource file by the constant ID_APP, which is defined in the header file SCRNSAVE.H.

One final requirement is a screen saver description string. The resource file for a screen saver must contain a descriptive string that Control Panel displays as the screen saver name. The description string must be the first string in the resource file's string table (identified with the ordinal value 1).

+$#A Using the Screen Saver Functions

• Creating a screen saver

• Installing new screen savers

• Adding help

+$#A Creating a Screen Saver

This section contains example code taken from a screen saver application. At intervals ranging from 1 through 10 seconds, this application repaints the screen with one of four colors: white, light gray, dark gray, and black. The application paints the screen each time it receives a WM_TIMER message. The user can adjust the interval at which this message is sent by displaying the application's configuration dialog box and adjusting a single horizontal scroll bar.

+$#A Supporting the Configuration Dialog Box

Most screen savers provide a configuration dialog box to let the user specify customization data such as unique colors, drawing speeds, line thicknesses, fonts, and so on. To support the configuration dialog box, the application must provide a dialog box template and must also support the ScreenSaverConfigureDialog function. Following is the dialog box template for the sample application.

DLG_SCRNSAVECONFIGURE DIALOG 6, 18, 160, 63
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
CAPTION "Sample Screen-Saver Setup"
FONT 8, "MS Sans Serif"
BEGIN
GROUPBOX "Redraw Speed", 101, 0, 6, 98, 40
SCROLLBAR ID_SPEED, 5, 31, 89, 10
LTEXT "Fast", 103, 6, 21, 20, 8
LTEXT "Slow", 104, 75, 21, 20, 8
PUSHBUTTON "OK", ID_OK, 117, 10, 40, 14
PUSHBUTTON "Cancel", ID_CANCEL, 117, 32, 40, 14
END

You must define the constant used to identify the dialog box template by using the decimal value 2003, as in the following example.

#define DLG_SCRNSAVECONFIGURE 2003

The following example shows the ScreenSaverConfigureDialog function found in the sample application.

#define MINVEL 1 /* minimum redraw-speed value */

#define MAXVEL 10 /* maximum redraw-speed value */

#define DEFVEL 5 /* default redraw-speed value */

LONG lSpeed = DEFVEL; /* redraw-speed variable */

extern HINSTANCE hMainInstance; /* screen saver instance handle */

CHAR szAppName[80]; /* .INI section name */

CHAR szTemp[20]; /* temporary array of characters */

CHAR szRedrawSpeed[] = "Redraw Speed"; /* .INI speed entry */

BOOL WINAPI ScreenSaverConfigureDialog(hDlg, message, wParam, lParam)

HWND hDlg;

UINT message;

DWORD wParam;

LONG lParam;

{

static HWND hSpeed; /* handle of speed scroll bar */

static HWND hOK; /* handle of OK push button */

switch(message)

{

case WM_INITDIALOG:

/* Retrieve the application name from the .RC file. */

LoadString(hMainInstance, idsAppName, szAppName, 40);

/* Retrieve the .INI (or registry) filename. */

LoadString(hMainInstance, idsIniFile, szIniFile,

MAXFILELEN);

/* Retrieve any redraw-speed data from the registry. */

lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,

DEFVEL, szIniFile);

/*

* If the initialization file does not contain an entry

* for this screen saver, use the default value.

*/

if(lSpeed > MAXVEL || lSpeed < MINVEL)

lSpeed = DEFVEL;

/* Initialize the redraw-speed scroll bar control. */

hSpeed = GetDlgItem(hDlg, ID_SPEED);

SetScrollRange(hSpeed, SB_CTL, MINVEL, MAXVEL, FALSE);

SetScrollPos(hSpeed, SB_CTL, lSpeed, TRUE);

/* Retrieve a handle of the OK push button control. */

hOK = GetDlgItem(hDlg, ID_OK);

return TRUE;

case WM_HSCROLL:

/*

* Process scroll bar input, adjusting the lSpeed

* value as appropriate.

*/

switch (LOWORD(wParam))

{

case SB_PAGEUP:

--lSpeed;

break;

case SB_LINEUP:

--lSpeed;

break;

case SB_PAGEDOWN:

++lSpeed;

break;

case SB_LINEDOWN:

++lSpeed;

break;

case SB_THUMBPOSITION:

lSpeed = HIWORD(wParam);

break;

case SB_BOTTOM:

lSpeed = MINVEL;

break;

case SB_TOP:

lSpeed = MAXVEL;

break;

case SB_THUMBTRACK:

case SB_ENDSCROLL:

return TRUE;

break;

}

if ((int) lSpeed <= MINVEL)

lSpeed = MINVEL;

if ((int) lSpeed >= MAXVEL)

lSpeed = MAXVEL;

SetScrollPos((HWND) lParam, SB_CTL, lSpeed, TRUE);

break;

case WM_COMMAND:

switch(LOWORD(wParam))

{

case ID_OK:

/*

* Write the current redraw-speed variable to

* the .INI file.

*/

wsprintf(szTemp, "%ld", lSpeed);

WritePrivateProfileString(szAppName, szRedrawSpeed,

szTemp, szIniFile);

case ID_CANCEL:

EndDialog(hDlg, LOWORD(wParam) == ID_OK);

return TRUE;

}

}

return FALSE;

}

In addition to providing the dialog box template and supporting the ScreenSaverConfigureDialog function, an application must also support the RegisterDialogClasses function. This function registers any nonstandard window classes required by the screen saver. Because the sample application used only standard window classes in its dialog box procedure, this function simply returns TRUE, as in the following example.

BOOL WINAPI RegisterDialogClasses(hInst)

HANDLE hInst;

{

return TRUE;

}

+$#A Supporting the Screen Saver Window Procedure

Each screen saver must support a window procedure named ScreenSaverProc. Like most window procedures, ScreenSaverProc processes a set of specific messages and passes any unprocessed messages to a default procedure. However, instead of passing them to the DefWindowProc function, ScreenSaverProc passes unprocessed messages to the DefScreenSaverProc function. Another difference between ScreenSaverProc and a normal window procedure is that the handle passed to ScreenSaverProc identifies the entire desktop rather than a client window. The following example shows the ScreenSaverProc window procedure for the sample screen saver.

LONG WINAPI ScreenSaverProc(hwnd, message, wParam, lParam)

HWND hwnd;

UINT message;

DWORD wParam;

LONG lParam;

{

static HDC hdc; /* device-context handle */

static RECT rc; /* RECT structure */

static UINT uTimer; /* timer identifier */

switch(message)

{

case WM_CREATE:

/* Retrieve the application name from the .RC file. */

LoadString(hMainInstance, idsAppName, szAppName, 40);

/* Retrieve the .INI (or registry) filename. */

LoadString(hMainInstance, idsIniFile, szIniFile,

MAXFILELEN);

/* Retrieve any redraw-speed data from the registry. */

lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,

DEFVEL, szIniFile);

/*

* Set a timer for the screen saver window using the

* redraw-rate stored in REGEDIT.INI.

*/

uTimer = SetTimer(hwnd, 1, lSpeed * 1000, NULL);

break;

case WM_ERASEBKGND:

/*

* The WM_ERASEBKGND message is issued before the

* WM_TIMER message, allowing the screen saver to

* paint the background as appropriate.

*/

hdc = GetDC(hwnd);

GetClientRect (hwnd, &rc);

FillRect (hdc, &rc, GetStockObject(BLACK_BRUSH));

ReleaseDC(hwnd,hdc);

break;

case WM_TIMER:

/*

* The WM_TIMER message is issued at (lSpeed * 1000)

* intervals, where lSpeed == .001 seconds. This

* code repaints the entire desktop with a white,

* light gray, dark gray, or black brush each

* time a WM_TIMER message is issued.

*/

hdc = GetDC(hwnd);

GetClientRect(hwnd, &rc);

if (i++ <= 4)

FillRect(hdc, &rc, GetStockObject(i));

else

(i = 0);

ReleaseDC(hwnd,hdc);

break;

case WM_DESTROY:

/*

* When the WM_DESTROY message is issued, the screen saver

* must destroy any of the timers that were set at WM_CREATE

* time.

*/

if (uTimer)

KillTimer(hwnd, uTimer);

break;

}

/*

* DefScreenSaverProc processes any messages

* ignored by ScreenSaverProc.

*/

return DefScreenSaverProc(hwnd, message, wParam, lParam);

}

+$#A Creating a Module-Definition File

The ScreenSaverProc and ScreenSaverConfigureDialog functions must be exported in the application's module-definition file; RegisterDialogClasses should not be exported, however. The following example shows the module-definition file for the sample application.

NAME SSTEST.SCR

DESCRIPTION 'SCRNSAVE : Test'

STUB 'WINSTUB.EXE'

EXETYPE WINDOWS

CODE MOVEABLE

DATA MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 4096

EXPORTS

ScreenSaverProc

ScreenSaverConfigureDialog

+$#A Installing New Screen Savers

When compiling the list of available screen savers, Control Panel searches the Windows startup directory for files with the .SCR extension. Because screen savers are standard Windows executable files with .EXE extensions, you must rename them so they have .SCR extensions and copy them to the correct directory.

+$#A Adding Help

The configuration dialog box for a screen saver typically includes a Help button. Screen saver applications can check for the Help button identifier and call the WinHelp function in the same way Help is provided in other Windows-based applications.

+$#A Functions

Following are the functions used with screen savers.

DefScreenSaverProc
RegisterDialogClasses
ScreenSaverConfigureDialog
ScreenSaverProc

Créé avec HelpNDoc Personal Edition: Générateur d'aide complet


Scroll Bars.rtf

+$#AK Scroll Bars

A window in an application written for Microsoft® Windows® can display a data object, such as a document or a bitmap, that is larger than the window's client area. When provided with a scroll bar, the user can scroll a data object in the client area to bring into view the portions of the object that extend beyond the borders of the window.

+$#A About Scroll Bars

Scroll bars should be included in any window for which the content of the client area extends beyond the window's borders. A scroll bar's orientation determines the direction in which scrolling occurs when the user operates the scroll bar. A horizontal scroll bar enables the user to scroll the content of a window to the left or right. A vertical scroll bar enables the user to scroll the content up or down.

+$#A Parts of a Scroll Bar

A scroll bar consists of a shaded shaft with an arrow button at each end and a scroll box (sometimes called a thumb) between the arrow buttons. A scroll bar represents the overall length or width of a data object in a window's client area; the scroll box represents the portion of the object that is visible in the client area. The position of the scroll box changes whenever the user scrolls a data object to display a different portion of it. In version 4.0 or later, Windows also adjusts the size of a scroll bar's scroll box so that it indicates what portion of the entire data object is currently visible in the window. If most of the object is visible, the scroll box occupies most of the scroll bar's shaft. Similarly, if only a small portion of the object is visible, the scroll box occupies a small part of the shaft.

The user scrolls the content of a window by clicking one of the arrow buttons, by clicking the area in the shaded shaft, or by dragging the scroll box. When the user clicks an arrow button, the application scrolls the content by one unit (typically a single line or column). When the user clicks the shaded areas, the application scrolls the content by one window. The amount of scrolling that occurs when the user drags the scroll box depends on the distance the user drags the scroll box and on the scrolling range of the scroll bar. For more information about the scrolling range, see Scroll Box Position and Scrolling Range.

+$#A Standard Scroll Bars and Scroll Bar Controls

A scroll bar is included in a window either as a standard scroll bar or as a scroll bar control. A standard scroll bar is located in the nonclient area of a window. It is created with the window and displayed when the window is displayed. The sole purpose of a standard scroll bar is to enable the user to generate scrolling requests for viewing the entire content of the client area. You can include a standard scroll bar in a window by specifying WS_HSCROLL, WS_VSCROLL, or both styles when you create the window. The WS_HSCROLL style creates a horizontal scroll bar positioned at the bottom of the client area. The WS_VSCROLL style creates a vertical scroll bar positioned at the right of the client area. The SM_CXHSCROLL and SM_CYHSCROLL system metric values define the width and height of a standard horizontal scroll bar. The SM_CXVSCROLL and SM_CYVSCROLL values define the width and height of a standard vertical scroll bar.

A scroll bar control is a control window that belongs to the SCROLLBAR window class. A scroll bar control appears and functions like a standard scroll bar, but it is a separate window. As a separate window, a scroll bar control receives direct input focus, indicated by a flashing caret displayed in the scroll box. Unlike a standard scroll bar, a scroll bar control also has a built-in keyboard interface that enables the user to direct scrolling. You can use as many scroll bar controls as needed in a single window. When you create a scroll bar control, you must specify the scroll bar's size and position. However, if a scroll bar control's window can be resized, adjustments to the scroll bar's size must be made whenever the size of the window changes.

The advantage of using a standard scroll bar is that Windows creates the scroll bar and automatically sets its size and position. However, standard scroll bars are sometimes too restrictive. For example, suppose that you want to divide a client area into quadrants and use a separate set of scroll bars to control the content of each quadrant. You cannot use standard scroll bars because you can only create one set of scroll bars for a particular window. Use scroll bar controls instead, because you can add as many of them to a window as you want.

Applications can provide scroll bar controls for purposes other than scrolling the content of a window. For example, a screen saver application might provide a scroll bar for setting the speed at which graphics are moved about on the screen.

A scroll bar control can have a number of styles that serves to control the orientation and position of the scroll bar. You specify the styles that you want when you call the CreateWindowEx function to create a scroll bar control. Some of the styles create a scroll bar control that uses a default width or height. However, you must always specify the x- and y-coordinates and the other dimensions of the scroll bar. Following are the scroll bar control styles.

Style | Meaning
SBS_BOTTOMALIGN |
 | Aligns the bottom edge of the scroll bar with the bottom edge of the rectangle defined by the CreateWindowEx parameters x, y, nWidth, and nHeight. The scroll bar has the default height for system scroll bars. Use this style with the SBS_HORZ style.
SBS_HORZ |
 | Designates a horizontal scroll bar. If neither the SBS_BOTTOMALIGN nor SBS_TOPALIGN style is specified, the scroll bar has the height, width, and position specified by the parameters of CreateWindowEx.
SBS_LEFTALIGN |
 | Aligns the left edge of the scroll bar with the left edge of the rectangle defined by the parameters of CreateWindowEx. The scroll bar has the default width for system scroll bars. Use this style with the SBS_VERT style.
SBS_RIGHTALIGN |
 | Aligns the right edge of the scroll bar with the right edge of the rectangle defined by the parameters of CreateWindowEx. The scroll bar has the default width for system scroll bars. Use this style with the SBS_VERT style.
SBS_SIZEBOX |
 | Designates a size box. If you specify neither the SBS_SIZEBOXBOTTOMRIGHTALIGN nor the SBS_SIZEBOXTOPLEFTALIGN style, the size box has the height, width, and position specified by the parameters of CreateWindowEx.
SBS_SIZEBOXBOTTOMRIGHTALIGN |
 | Aligns the lower right corner of the size box with the lower right corner of the rectangle specified by the parameters of CreateWindowEx. The size box has the default size for system size boxes. Use this style with the SBS_SIZEBOX style.
SBS_SIZEBOXTOPLEFTALIGN |
 | Aligns the upper left corner of the size box with the upper left corner of the rectangle specified by the parameters of CreateWindowEx. The size box has the default size for system size boxes. Use this style with the SBS_SIZEBOX style.
SBS_SIZEGRIP |
 | Same as SBS_SIZEBOX, but with a raised edge (version 4.0 or later).
SBS_TOPALIGN |
 | Aligns the top edge of the scroll bar with the top edge of the rectangle defined by the parameters of CreateWindowEx. The scroll bar has the default height for system scroll bars. Use this style with the SBS_HORZ style.
SBS_VERT |
 | Designates a vertical scroll bar. If you specify neither the SBS_RIGHTALIGN nor the SBS_LEFTALIGN style, the scroll bar has the height, width, and position specified by the parameters of CreateWindowEx.

+$#A Scroll Box Position and Scrolling Range

The position of the scroll box is represented as an integer; it is relative to the left or upper end of the scroll bar, depending on whether the scroll bar is horizontal or vertical. The position must be within the minimum and maximum values of the scrolling range. For example, in a scroll bar with a range of 0 through 100, position 50 is in the middle, with the remaining positions distributed equally along the scroll bar. The initial range depends on the scroll bar. Standard scroll bars have an initial range of 0 through 100; scroll bar controls have an empty range (both minimum and maximum values are zero), unless you supply an explicit range when the control is created. You can change the range at any time. With operating system version 4.0 or later, you can use the SetScrollInfo function to set the range values, and the GetScrollInfo function to retrieve the current range values. In versions earlier than 4.0, you can use the SetScrollRange and GetScrollRange functions.

An application typically adjusts the scroll range to convenient integers, making it easy to translate the scroll box position into a value corresponding to the data object to be scrolled. For example, if an application must display 260 lines of a text file in a window that can show only 16 lines at a time, the vertical scroll bar range can be set to 1 through 244. If the scroll box is at position 1, the first line will be at the top of the window. If the scroll box is at position 244, the last line (line 260) will be at the bottom of the window. If an application attempts to specify a position value that is less than the minimum or more than the maximum, the minimum or maximum scrolling range value is used instead.

In version 4.0 or later, an application can set a page size for a scroll bar. The page size represents the number of data units that can fit in the client area of the owner window given its current size. For example, if the client area can hold 16 lines of text, an application would set the page size to 16. Windows uses the page size, along with the scrolling range and length of the scroll shaft, to set the size of the scroll box. Whenever a window containing a scroll bar is resized, an application should call the SetScrollInfo function to set the page size. An application can retrieve the current page size by calling the sending GetScrollInfo function.

To establish a useful relationship between the scroll bar range and the data object, an application must adjust the range whenever the size of the data object changes.

As the user moves the scroll box in a scroll bar, the scroll bar reports the scroll box position as an integer in the scrolling range. If the position is the minimum value, the scroll box is at the top of a vertical scroll bar or at the left end of a horizontal scroll bar. If the position is the maximum value, the scroll box is at the bottom of a vertical scroll bar or at right end of a horizontal scroll bar.

In version 4.0 or later, the maximum value that a scroll bar can report (that is, the maximum scrolling position) depends on the page size. If the scroll bar has a page size greater than one, the maximum scrolling position is less than the maximum range value. You can use the following formula to calculate the maximum scrolling position:

MaxScrollPos = MaxRangeValue - (PageSize - 1)

An application must move the scroll box in a scroll bar. Although the user makes a request for scrolling in a scroll bar, the scroll bar does not automatically update the scroll box position. Instead, it passes the request to the parent window, which must scroll the data and update the scroll box position. In version 4.0 or later, an application uses the SetScrollInfo function to update the scroll box position; otherwise, it uses the SetScrollPos function. Because it controls the scroll box movement, the application can move the scroll box in increments that work best for the data being scrolled.

+$#A Scroll Bar Visibility

The system hides and disables a standard scroll bar when equal minimum and maximum values are specified. With version 4.0 or later, the system also hides and disables a standard scroll bar if you specify a page size that includes the entire scroll range of the scroll bar. This is the way to temporarily hide a scroll bar when it is not needed for the content of the client area. There is no need to make scrolling requests through the scroll bar when it is hidden. The system enables the scroll bar and shows it again when you set the minimum and maximum values to unequal values or, in version 4.0 or later, when the page size that does not include the entire scroll range. The ShowScrollBar function can also be used to hide or show a scroll bar. It does not affect the scroll bar's range, page size, or scroll box position.

The EnableScrollBar function can be used to disable one or both arrows of a scroll bar. An application displays disabled arrows in gray and does not respond to user input.

+$#A Scroll Bar Requests

The user makes scrolling requests by clicking various parts of a scroll bar. Windows sends the request to the given window in the form of a WM_HSCROLL or WM_VSCROLL message. A horizontal scroll bar sends the WM_HSCROLL message; a vertical scroll bar sends the WM_VSCROLL message. Each message includes a notification code that corresponds to the user's action, to the handle of the scroll bar (scroll bar controls only), and, in some cases, to the position of the scroll box.

The following figure shows the notification messages that the user generates when clicking various parts of a scroll bar.

{ewc msdncd, EWGraphic, group10617 0 /a "SDK_02.bmp"}

The scroll bar notification codes specify the action the user takes. An application examines the codes that accompany the WM_HSCROLL and WM_VSCROLL messages and then performs the appropriate scrolling operation. In the following table of notification messages, the user's action is specified for each message, followed by the application's response. In each case, a unit is defined by the application as appropriate for the given data. For example, the typical unit for scrolling text vertically is a line of text.

Notification message | Action | Response
SB_LINEUP | The user clicks the top scroll arrow. | Decrements the scroll box position; scrolls toward the top of the data by one unit.
SB_LINEDOWN | The user clicks the bottom scroll arrow. | Increments the scroll box position; scrolls toward the bottom of the data by one unit.
SB_LINELEFT | The user clicks the left scroll arrow. | Decrements the scroll box position; scrolls toward the left end of the data by one unit.
SB_LINERIGHT | The user clicks the right scroll arrow. | Increments the scroll box position; scrolls toward the right end of the data by one unit.
SB_PAGEUP | The user clicks the scroll bar shaft above the scroll box. | Decrements the scroll box position by the number of data units in the window; scrolls toward the top of the data by the same number of units.
SB_PAGEDOWN | The user clicks the scroll bar shaft below the scroll box. | Increments the scroll box position by the number of data units in the window; scrolls toward the bottom of the data by the same number of units.
SB_PAGELEFT | The user clicks the scroll bar shaft to the left of the scroll box. | Decrements the scroll box position by the number of data units in the window; scrolls toward the left end of the data by the same number of units.
SB_PAGERIGHT | The user clicks the scroll bar shaft to the right of the scroll box. | Increments the scroll box position by the number of data units in the window; scrolls toward the right end of the data by the same number of units.
SB_THUMBPOSITION | The user releases the scroll box after dragging it. | Sets the scroll box to the position given in the message; scrolls the data by the same number of units the scroll box has moved.
SB_THUMBTRACK | The user drags the scroll box. | Sets the scroll box to the position given in the message and scrolls the data by the same number of units the scroll box has moved for applications that draw data quickly. Applications that cannot draw data quickly must wait for the SB_THUMBPOSITION message before moving the scroll box and scrolling the data.
SB_ENDSCROLL | The user releases the mouse after holding it on an arrow or in the scroll bar shaft. | No response is needed.

A scroll bar generates the SB_THUMBPOSITION and SB_THUMBTRACK notification messages when the user clicks and drags the scroll box. An application should be programmed to process either the SB_THUMBTRACK or the SB_THUMBPOSITION notification message.

The SB_THUMBPOSITION notification message occurs when the user releases the mouse button after clicking the scroll box. An application that processes this message performs the scrolling operation after the user has dragged the scroll box to the desired position and released the mouse button.

SB_THUMBTRACK notification messages occur as the user drags the scroll box. If an application processes SB_THUMBTRACK messages, it can scroll the content of a window as the user drags the scroll box. However, a scroll bar can generate many SB_THUMBTRACK notification messages in a short period, so an application should process these messages only if it can quickly repaint the content of the window.

+$#A Keyboard Interface for a Scroll Bar

A scroll bar control provides a built-in keyboard interface that enables the user to issue scrolling requests by using the keyboard; a standard scroll bar does not. When a scroll bar control has the keyboard focus, it sends WM_HSCROLL and WM_VSCROLL messages to its parent window when the user presses the arrow keys. The notification message is sent with each message corresponding to the arrow key the user has pressed. Following are the arrow keys and their corresponding notification messages.

Arrow key | Notification message
DOWN | SB_LINEDOWN or SB_LINERIGHT
END | SB_BOTTOM
HOME | SB_TOP
LEFT | SB_LINEUP or SB_LINELEFT
PGDN | SB_PAGEDOWN or SB_PAGERIGHT
PGUP | SB_PAGEUP or SB_PAGELEFT
RIGHT | SB_LINEDOWN or SB_LINERIGHT
UP | SB_LINEUP or SB_LINELEFT

Note that the keyboard interface of a scroll bar control sends the SB_TOP and SB_BOTTOM notification messages. The SB_TOP message indicates that the user has reached the top value of the scrolling range. An application scrolls the window content downward so that the top of the data object is visible. The SB_BOTTOM message indicates that the user has reached the bottom value of the scrolling range. If an application processes the SB_BOTTOM message, it scrolls the window content upward so that the bottom of the data object is visible.

If you want a keyboard interface for a standard scroll bar, you can create one yourself by processing the WM_KEYDOWN message in your window procedure and then performing the appropriate scrolling action based on the virtual-key code that accompanies the message. For information about how to create a keyboard interface for a scroll bar, see Creating a Keyboard Interface for a Standard Scroll Bar.

+$#A Scrolling the Client Area

The simplest way to scroll the content of a client area is to erase and then redraw it. This is the method an application is likely to use with SB_PAGEUP, SB_PAGEDOWN, and SB_TOP notification messages, which typically require completely new content.

For some notification messages, such as SB_LINEUP and SB_LINEDOWN, not all the content need be erased, because some remains visible after scrolling occurs. The ScrollWindowEx function preserves a portion of the client area's content, move the preserved portion a specified amount, and then prepares the rest of the client area for painting new information. ScrollWindowEx uses the BitBlt function to move a specific part of the data object to a new location within the client area. Any uncovered part of the client area (anything not preserved) is invalidated, erased, and painted when the next WM_PAINT message occurs.

The ScrollWindowEx function can be used to exclude a portion of the client area from the scrolling operation. This keeps items with fixed positions, such as child windows, from moving within the client area. It automatically invalidates the portion of the client area that is to receive the new information, so the application does not have to compute its own clipping regions. For more information on clipping, see Clipping.

Usually an application scrolls the content of a window in the direction opposite that indicated by the scroll bar. For example, when the user clicks the shaft in the area below the scroll box, an application scrolls the object in the window upward to reveal a portion of the object that is below the visible portion.

+$#A Scroll Bar Colors and Metrics

The system-defined color value, COLOR_SCROLLBAR, controls the color within a scroll bar's shaft. Use the GetSysColor function to determine the color of the shaft and the SetSysColors function to set the color of the shaft. Note, however, that this change of color affects all scroll bars in the system.

You can get the dimensions of the bitmaps that Windows uses in standard scroll bars by calling the GetSystemMetrics function. Following are the system metric values associated with scroll bars.

System metric | Description
SM_CXHSCROLL | Width of arrow bitmap on horizontal scroll bar
SM_CXHTHUMB | Width of scroll box on horizontal scroll bar. In version 4.0 or later, this value retrieves the width a scroll bar that has a page size of zero.
SM_CXVSCROLL | Width of arrow bitmap on vertical scroll bar
SM_CYHSCROLL | Height of arrow bitmap on horizontal scroll bar
SM_CYVSCROLL | Height of arrow bitmap on vertical scroll bar
SM_CYVTHUMB | Height of scroll box on vertical scroll bar. In version 4.0 or later, this value retrieves the height of a scroll bar that has a page size of zero.

+$#A Using Scroll Bars

• Creating scroll bars

• Scrolling text

• Scrolling a bitmap

• Creating a keyboard interface for a standard scroll bar

Note The examples in this section set the scroll range, scroll position, and page size by using SetScrollInfo function, which is available only with version 4.0 and later. For operating system versions earlier than 4.0, use the SetScrollRange and SetScrollPos functions.

+$#A Creating Scroll Bars

When creating an overlapped, pop-up, or child window, you can add standard scroll bars by using the CreateWindowEx function and specifying WS_HSCROLL, WS_VSCROLL, or both styles. Doing this adds a horizontal or vertical scroll bar, or both, to the window. The following example creates a window with standard horizontal and vertical scroll bars.

hwnd = CreateWindowEx(

0L, /* no extended styles */

"MyAppClass", /* window class */

"Scroll Bar Application", /* text for window title bar */

WS_OVERLAPPEDWINDOW | /* window styles */

WS_HSCROLL |

WS_VSCROLL,

CW_USEDEFAULT, /* default horizontal position */

CW_USEDEFAULT, /* default vertical position */

CW_USEDEFAULT, /* default width */

CW_USEDEFAULT, /* default height */

(HWND) NULL, /* no parent for overlapped windows */

(HMENU) NULL, /* window class menu */

hinst, /* instance owning this window */

(LPVOID) NULL /* pointer not needed */

);

To process scroll bar messages for these scroll bars, you must include appropriate code in the main window procedure.

When using the CreateWindowEx function to create a window, you can add a scroll bar control by specifying the SCROLLBAR window class. This creates a horizontal or vertical scroll bar, depending on whether SBS_HORZ or SBS_VERT is specified as the window style. The scroll bar size and its position relative to its parent window can also be specified. The following example creates a horizontal scroll bar control and positions it in the upper right corner of the window.

hwndScroll = CreateWindowEx(

0L, /* no extended styles */

"SCROLLBAR", /* scroll bar control class */

(LPSTR) NULL, /* text for window title bar */

WS_CHILD | SBS_HORZ, /* scroll bar styles */

0, /* horizontal position */

0, /* vertical position */

200, /* width of the scroll bar */

CW_USEDEFAULT, /* default height */

hwnd, /* handle of main window */

(HMENU) NULL, /* no menu for a scroll bar */

hinst, /* instance owning this window */

(LPVOID) NULL /* pointer not needed */

);

+$#A Scrolling Text

This section describes the changes you can make to an application's main window procedure to enable a user to scroll text. The example in Example of Scrolling Text creates and displays an array of text strings and processes WM_HSCROLL and WM_VSCROLL messages generated by the scroll bars so that the user can scroll text both vertically and horizontally.

+$#A Scrolling Text with the WM_CREATE Message

Scrolling units are typically set while processing the WM_CREATE message. It is convenient to base the scrolling units on the dimensions of the font associated with the window's display context (DC). To retrieve the font dimensions for a specific DC, use the GetTextMetrics function.

In the example in Example of Scrolling Text, one vertical scrolling unit is equivalent to the height of a character cell, plus external leading. One horizontal scrolling unit is equivalent to the average width of a character cell. The horizontal scrolling positions, therefore, do not correspond to actual characters, unless the screen font is fixed-width.

+$#A Scrolling Text with the WM_SIZE Message

When processing the WM_SIZE message, it is convenient to adjust the scrolling range and scrolling position to reflect the dimensions of the client area as well as the number of lines of text that will be displayed.

The SetScrollInfo function sets the minimum and maximum position values, the page size, and the scrolling position for a scroll bar. For versions earlier than 4.0, use the SetScrollRange function to set the minimum and maximum position values, and the SetScrollPos function to adjust the scroll box to reflect the scrolling position.

+$# Scrolling Text with the WM_HSCROLL and WM_VSCROLL Messages

The scroll bar sends WM_HSCROLL and WM_VSCROLL messages to the window procedure whenever the user clicks the scroll bar or drags the scroll box. The low-order words of WM_VSCROLL and WM_HSCROLL each contain a notification message that indicates the direction and magnitude of the scrolling action.

When the WM_HSCROLL and WM_VSCROLL messages are processed, the scroll bar notification message is examined and the scrolling increment is calculated. After the increment is applied to the current scrolling position, the window is scrolled to the new position by using the ScrollWindowEx function, and the position of the scroll box is adjusted by using the SetScrollInfo function. For operating system versions earlier than 4.0, use the SetScrollPos function to adjust the scroll box position.

After a window is scrolled, part of its client area is made invalid. To ensure that the invalid region is updated, the UpdateWindow function is used to generate a WM_PAINT message.

+$#A Scrolling Text with the WM_PAINT Message

When processing the WM_PAINT message, it is convenient to draw the lines of text that you want to appear in the invalid portion of the window. The example in the following section uses the current scrolling position and the dimensions of the invalid region to determine the range of lines within the invalid region to display them.

+$#A Example of Scrolling Text

The following example shows how to have your application scroll text in response to input from the horizontal and vertical scroll bars.

HDC hdc;

PAINTSTRUCT ps;

TEXTMETRIC tm;

SCROLLINFO si;

/* These variables are required to display text. */

static int xClient; /* width of client area */

static int yClient; /* height of client area */

static int xClientMax; /* maximum width of client area */

static int xChar; /* horizontal scrolling unit */

static int yChar; /* vertical scrolling unit */

static int xUpper; /* average width of uppercase letters */

static int xPos; /* current horizontal scrolling position */

static int yPos; /* current vertical scrolling position */

static int xMax; /* maximum horiz. scrolling position */

static int yMax; /* maximum vert. scrolling position */

int xInc; /* horizontal scrolling increment */

int yInc; /* vertical scrolling increment */

int i; /* loop counter */

int x, y; /* horiz. and vert. printing coords */

int FirstLine; /* first line in the invalidated area */

int LastLine; /* last line in the invalidated area */

/* Create an array of lines to display. */

#define LINES 27

static char *abc[] = { "anteater", "bear", "cougar", "dingo",

"elephant", "frog", "gazelle", "hyena", "iguana", "jackal",

"kangaroo", "llama", "moose", "newt", "octopus", "penguin",

"quail", "rat", "squid", "tortoise", "urus", "vole",

"walrus", "xylophone", "yak", "zebra",

"This line contains many words, but no character. Go figure." };

switch (uMsg) {

case WM_CREATE :

/* Get the handle of the client area's device context. */

hdc = GetDC (hwnd);

/* Extract font dimensions from the text metrics. */

GetTextMetrics (hdc, &tm);

xChar = tm.tmAveCharWidth;

xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2;

yChar = tm.tmHeight + tm.tmExternalLeading;

/* Free the device context. */

ReleaseDC (hwnd, hdc);

/*

* Set an arbitrary maximum width for client area.

* (xClientMax is the sum of the widths of 48 average

* lowercase letters and 12 uppercase letters.)

*/

xClientMax = 48 * xChar + 12 * xUpper;

return 0;

case WM_SIZE:

/* Retrieve the dimensions of the client area. */

yClient = HIWORD (lParam);

xClient = LOWORD (lParam);

/*

* Determine the maximum vertical scrolling position.

* The two is added for extra space below the lines

* of text.

*/

yMax = max (0, LINES + 2 - yClient/yChar);

/*

* Make sure the current vertical scrolling position

* does not exceed the maximum.

*/

yPos = min (yPos, yMax);

/*

* Adjust the vertical scrolling range and scroll box

* position to reflect the new yMax and yPos values.

*/

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = 0;

si.nMax = yMax;

si.nPage = yClient / yChar;

si.nPos = yPos;

SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

/*

* Determine the maximum horizontal scrolling position.

* The two is added for extra space to the right of the

* lines of text.

*/

xMax = max (0, 2 + (xClientMax - xClient)/xChar);

/*

* Make sure the current horizontal scrolling position

* does not exceed the maximum.

*/

xPos = min (xPos, xMax);

/*

* Adjust the horizontal scrolling range and scroll box

* position to reflect the new xMax and xPos values.

*/

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = 0;

si.nMax = xMax;

si.nPage = xClient / xChar;

si.nPos = xPos;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

return 0;

case WM_PAINT:

/* Prepare the window for painting. */

hdc = BeginPaint(hwnd, &ps);

/*

* Use the current vertical scrolling position and

* coordinates of the invalid rectangle to determine

* the range of new lines that should be drawn in the

* client area.

*/

FirstLine = max (0, yPos + ps.rcPaint.top/yChar - 1);

LastLine = min (LINES, yPos + ps.rcPaint.bottom/yChar);

/* Display these lines. */

for (i = FirstLine;i < LastLine;i++) {

x = xChar * (1 - xPos);

y = yChar * (1 - yPos + i);

TextOut (hdc, x, y, abc[i], lstrlen(abc[i]));

}

/* Indicate that painting is finished. */

EndPaint(hwnd, &ps);

break;

case WM_HSCROLL:

switch(LOWORD (wParam)) {

/* User clicked shaft left of the scroll box. */

case SB_PAGEUP:

xInc = -8;

break;

/* User clicked shaft right of the scroll box. */

case SB_PAGEDOWN:

xInc = 8;

break;

/* User clicked the left arrow. */

case SB_LINEUP:

xInc = -1;

break;

/* User clicked the right arrow. */

case SB_LINEDOWN:

xInc = 1;

break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:

xInc = HIWORD(wParam) - xPos;

break;

default:

xInc = 0;

}

/*

* If applying the horizontal scrolling increment does not

* take the scrolling position out of the scrolling range,

* increment the scrolling position, adjust the position

* of the scroll box, and update the window.

*/

if (xInc = max (-xPos, min (xInc, xMax - xPos))) {

xPos += xInc;

ScrollWindowEx (hwnd, -xChar * xInc, 0,

(CONST RECT *) NULL, (CONST RECT *) NULL,

(HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = xPos;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

UpdateWindow (hwnd);

}

return 0;

case WM_VSCROLL:

switch(LOWORD (wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:

yInc = min(-1, -yClient / yChar);

break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:

yInc = max(1, yClient / yChar);

break;

/* User clicked the top arrow. */

case SB_LINEUP:

yInc = -1;

break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:

yInc = 1;

break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:

yInc = HIWORD(wParam) - yPos;

break;

default:

yInc = 0;

}

/*

* If applying the vertical scrolling increment does not

* take the scrolling position out of the scrolling range,

* increment the scrolling position, adjust the position

* of the scroll box, and update the window. UpdateWindow

* sends the WM_PAINT message.

*/

if (yInc = max(-yPos, min(yInc, yMax - yPos))) {

yPos += yInc;

ScrollWindow(hwnd, 0, -yChar * yInc,

(CONST RECT *) NULL, (CONST RECT *) NULL,

(HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE);

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = YPos;

SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

UpdateWindow (hwnd);

}

return 0;

+$#A Scrolling a Bitmap

This section describes changes you can make to an application's main window procedure to enable the user to scroll a bitmap. The example in Example of Scrolling a Bitmap includes a menu item that copies the screen content to a bitmap and displays the bitmap in the client area.

The example also processes the WM_HSCROLL and WM_VSCROLL messages generated by the scroll bars so that the user may scroll the bitmap horizontally and vertically. Unlike the example for scrolled text, the bitmap example employs the BitBlt function to draw the invalid portion of the client area.

+$#A Scrolling a Bitmap with the WM_CREATE Message

When the WM_CREATE message is processed, the variables required for scrolling are initialized. Use the CreateCompatibleDC function to create a compatible device context (DC), the CreateBitmap function to create a bitmap, and the SelectObject function to select the bitmap for the DC. Note that a compatible DC is also known as a memory DC.

The device-specific information about the display device is retrieved. If a compatible DC is created for the screen, as in the example, use the GetDeviceCaps function to get this information. The information includes the number of adjacent color bits per pixel, the number of color planes, and the height and width of the DC.

+$#A Scrolling a Bitmap with the WM_SIZE Message

Processing of the WM_SIZE message requires adjusting the scrolling range and position, so it reflects the dimensions of the client area and the bitmap that will be displayed.

The SetScrollInfo function sets the minimum and maximum position values, the page size, and the scrolling position for a scroll bar. For operating system versions earlier than 4.0, use the SetScrollPos function to adjust the scroll box position.

+$# Scrolling a Bitmap with the WM_HSCROLL and WM_VSCROLL Messages

When the WM_HSCROLL and WM_VSCROLL messages are processed, the scroll bar notification message is examined and the scrolling position is set to a new value that reflects the scrolling action of the user. If the scrolling position is within the scrolling range, the window is scrolled to the new position by using the ScrollWindow function. The position of the scroll box is then adjusted by using the SetScrollInfo function. In versions earlier than 4.0, use the SetScrollPos function.

After a window is scrolled, part of its client area is made invalid. To ensure that the invalid region is updated, use the UpdateWindow function to generate a WM_PAINT message. When processing the WM_PAINT message, an application must repaint the invalid region at the bottom of the client area. When scrolling or resizing the client area, the example uses the BitBlt function to copy the appropriate portion of the bitmap to the invalid portion of the client area.

+$#A Example of Scrolling a Bitmap

The following example enables the user to capture the screen content into a bitmap and scroll the bitmap in the client area.

HDC hdc;

PAINTSTRUCT ps;

SCROLLINFO si;

/* These variables are required by BitBlt. */

static HDC hdcWin; /* window DC */

static HDC hdcScreen; /* DC for entire screen */

static HDC hdcScreenCompat; /* memory DC for screen */

static HBITMAP hbmpCompat; /* bitmap handle for old DC */

static BITMAP bmp; /* bitmap data structure */

static BOOL fBlt; /* TRUE if BitBlt occurred */

static BOOL fScroll; /* TRUE if scrolling occurred */

static BOOL fSize; /* TRUE if fBlt & WM_SIZE */

/* These variables are required for horizontal scrolling. */

static int xMinScroll; /* minimum horizontal scroll value */

static int xCurrentScroll; /* current horizontal scroll value */

static int xMaxScroll; /* maximum horizontal scroll value */

/* These variables are required for vertical scrolling. */

static int yMinScroll; /* minimum vertical scroll value */

static int yCurrentScroll; /* current vertical scroll value */

static int yMaxScroll; /* maximum vertical scroll value */

switch (uMsg) {

case WM_CREATE:

/*

* Create a normal DC and a memory DC for the entire

* screen. The normal DC provides a snapshot of the

* screen contents. The memory DC keeps a copy of this

* snapshot in the associated bitmap.

*/

hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,

(LPCSTR) NULL, (CONST DEVMODE *) NULL);

hdcScreenCompat = CreateCompatibleDC(hdcScreen);

/*

* Retrieve the metrics for the bitmap associated with the

* regular device context.

*/

bmp.bmBitsPixel =

(BYTE) GetDeviceCaps(hdcScreen, BITSPIXEL);

bmp.bmPlanes = (BYTE) GetDeviceCaps(hdcScreen, PLANES);

bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);

bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);

/* The width must be byte-aligned. */

bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;

/* Create a bitmap for the compatible DC. */

hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,

bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);

/* Select the bitmap for the compatible DC. */

SelectObject(hdcScreenCompat, hbmpCompat);

/* Initialize the flags. */

fBlt = FALSE;

fScroll = FALSE;

fSize = FALSE;

/* Initialize the horizontal scrolling variables. */

xMinScroll = 0;

xCurrentScroll = 0;

xMaxScroll = 0;

/* Initialize the vertical scrolling variables. */

yMinScroll = 0;

yCurrentScroll = 0;

yMaxScroll = 0;

break;

case WM_SIZE: {

int xNewSize;

int yNewSize;

xNewSize = LOWORD(lParam);

yNewSize = HIWORD(lParam);

if (fBlt)

fSize = TRUE;

/*

* The horizontal scrolling range is defined by

* (bitmap_width) - (client_width). The current horizontal

* scroll value remains within the horizontal scrolling range.

*/

xMaxScroll = max(bmp.bmWidth-xNewSize, 0);

xCurrentScroll = min(xCurrentScroll, xMaxScroll);

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = xMinScroll;

si.nMax = xMaxScroll;

si.nPage = xNewSize;

si.nPos = xCurrentScroll;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

/*

* The vertical scrolling range is defined by

* (bitmap_height) - (client_height). The current vertical

* scroll value remains within the vertical scrolling range.

*/

yMaxScroll = max(bmp.bmHeight - yNewSize, 0);

yCurrentScroll = min(yCurrentScroll, yMaxScroll);

si.cbSize = sizeof(si);

si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;

si.nMin = yMinScroll;

si.nMax = yMaxScroll;

si.nPage = yNewSize;

si.nPos = yCurrentScroll;

SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

}

break;

case WM_PAINT: {

PRECT prect;

hdc = BeginPaint(hwnd, &ps);

/*

* If the window has been resized and the user has

* captured the screen, use the following call to

* BitBlt to paint the window's client area.

*/

if (fSize) {

BitBlt(ps.hdc,

0, 0,

bmp.bmWidth, bmp.bmHeight,

hdcScreenCompat,

xCurrentScroll, yCurrentScroll,

SRCCOPY);

fSize = FALSE;

}

/*

* If scrolling has occurred, use the following call to

* BitBlt to paint the invalid rectangle.

*

* The coordinates of this rectangle are specified in the

* RECT structure to which prect points.

*

* Note that it is necessary to increment the seventh

* argument (prect->left) by xCurrentScroll and the

* eighth argument (prect->top) by yCurrentScroll in

* order to map the correct pixels from the source bitmap.

*/

if (fScroll) {

prect = &ps.rcPaint;

BitBlt(ps.hdc,

prect->left, prect->top,

(prect->right - prect->left),

(prect->bottom - prect->top),

hdcScreenCompat,

prect->left + xCurrentScroll,

prect->top + yCurrentScroll,

SRCCOPY);

fScroll = FALSE;

}

EndPaint(hwnd, &ps);

}

break;

case WM_HSCROLL: {

int xDelta; /* xDelta = new_pos - current_pos */

int xNewPos; /* new position */

int yDelta = 0;

switch (LOWORD(wParam)) {

/* User clicked the shaft left of the scroll box. */

case SB_PAGEUP:

xNewPos = xCurrentScroll - 50;

break;

/* User clicked the shaft right of the scroll box. */

case SB_PAGEDOWN:

xNewPos = xCurrentScroll + 50;

break;

/* User clicked the left arrow. */

case SB_LINEUP:

xNewPos = xCurrentScroll - 5;

break;

/* User clicked the right arrow. */

case SB_LINEDOWN:

xNewPos = xCurrentScroll + 5;

break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:

xNewPos = HIWORD(wParam);

break;

default:

xNewPos = xCurrentScroll;

}

/* New position must be between 0 and the screen width. */

xNewPos = max(0, xNewPos);

xNewPos = min(xMaxScroll, xNewPos);

/* If the current position does not change, do not scroll.*/

if (xNewPos == xCurrentScroll)

break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

xDelta = xNewPos - xCurrentScroll;

/* Reset the current scroll position. */

xCurrentScroll = xNewPos;

/*

* Scroll the window. (The system repaints most of the

* client area when ScrollWindowEx is called; however, it is

* necessary to call UpdateWindow in order to repaint the

* rectangle of pixels that were invalidated.)

*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,

(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,

SW_INVALIDATE);

UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = xCurrentScroll;

SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

}

break;

case WM_VSCROLL: {

int xDelta = 0;

int yDelta; /* yDelta = new_pos - current_pos */

int yNewPos; /* new position */

switch (LOWORD(wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:

yNewPos = yCurrentScroll - 50;

break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:

yNewPos = yCurrentScroll + 50;

break;

/* User clicked the top arrow. */

case SB_LINEUP:

yNewPos = yCurrentScroll - 5;

break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:

yNewPos = yCurrentScroll + 5;

break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:

yNewPos = HIWORD(wParam);

break;

default:

yNewPos = yCurrentScroll;

}

/* New position must be between 0 and the screen height. */

yNewPos = max(0, yNewPos);

yNewPos = min(yMaxScroll, yNewPos);

/* If the current position does not change, do not scroll.*/

if (yNewPos == yCurrentScroll)

break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

yDelta = yNewPos - yCurrentScroll;

/* Reset the current scroll position. */

yCurrentScroll = yNewPos;

/*

* Scroll the window. (The system repaints most of the

* client area when ScrollWindowEx is called; however, it is

* necessary to call UpdateWindow in order to repaint the

* rectangle of pixels that were invalidated.)

*/

ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,

(CONST RECT *) NULL, (HRGN) NULL, (LPRECT) NULL,

SW_INVALIDATE);

UpdateWindow(hwnd);

/* Reset the scroll bar. */

si.cbSize = sizeof(si);

si.fMask = SIF_POS;

si.nPos = yCurrentScroll;

SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

}

break;

case WM_COMMAND: /* uMsg: command from app. menu */

switch(wParam) {

case IDM_STC:

/*

* Copy the contents of the current screen

* into the compatible DC.

*/

BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,

bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);

/*

* Copy the compatible DC to the client area.

*/

hdcWin = GetDC(hwnd);

BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,

hdcScreenCompat, 0, 0, SRCCOPY);

ReleaseDC(hwnd, hdcWin);

fBlt = TRUE;

break;

default:

return (DefWindowProc(hwnd, uMsg,

wParam, lParam));

}

break;

+$#A Creating a Keyboard Interface for a Standard Scroll Bar

Although a scroll bar control provides a built-in keyboard interface, a standard scroll bar does not. To implement a keyboard interface for a standard scroll bar, a window procedure must process the WM_KEYDOWN message and examine the virtual-key code specified by the wParam parameter. If the virtual-key code corresponds to an arrow key, the window procedure sends itself a WM_HSCROLL or WM_VSCROLL message with the low-order word of the wParam parameter set to the appropriate scroll bar notification message. For example, when the user presses the UP arrow key, the window procedure receives a WM_KEYDOWN message with wParam equal to VK_UP. In response, the window procedure sends itself a WM_VSCROLL message with the low-order word of wParam set to the SB_LINEUP notification message.

The following example shows how to include a keyboard interface for a standard scroll bar.

WORD wScrollNotify = 0xFFFF;

.

.

.

case WM_KEYDOWN:

switch (wParam) {

case VK_UP:

wScrollNotify = SB_LINEUP;

break;

case VK_PRIOR:

wScrollNotify = SB_PAGEUP;

break;

case VK_NEXT:

wScrollNotify = SB_PAGEDOWN;

break;

case VK_DOWN:

wScrollNotify = SB_LINEDOWN;

break;

case VK_HOME:

wScrollNotify = SB_TOP;

break;

case VK_END:

wScrollNotify = SB_BOTTOM;

break;

}

if (wScrollNotify != -1)

SendMessage(hwnd, WM_VSCROLL,

MAKELONG(wScrollNotify, 0), 0L);

break;

.

.

.

+$#AK Scroll Bar Reference

The following functions and messages are associated with scroll bars.

+$#A Scroll Bar Functions

Following are the functions used with scroll bars.

EnableScrollBar
GetScrollPos
GetScrollRange
ScrollDC
ScrollWindow
ScrollWindowEx
SetScrollPos
SetScrollRange
ShowScrollBar

+$#A Scroll Bar Messages

Following are the messages used with scroll bars.

SBM_ENABLE_ARROWS
SBM_GETPOS
SBM_GETRANGE
SBM_SETPOS
SBM_SETRANGE
SBM_SETRANGEREDRAW
WM_CTLCOLORSCROLLBAR
WM_HSCROLL
WM_VSCROLL

Créé avec HelpNDoc Personal Edition: Générateur d'aide complet


Security.rtf

+$#AK Security

The security provisions of Microsoft® Windows NT™ are available to Windows-based applications automatically. Every application running on the system is subject to the security imposed by the particular configuration of the local implementation of Windows NT.

+$#AK About Security

The security functions in the Microsoft Win32® application programming interface (API) allow an application to selectively grant and deny access to an object. An application can specify many different kinds of access for particular users and groups of users. The operating system grants or denies access to an object based on a comparison of the security provisions stored with an object with the access rights specified in a token associated with the process or thread requesting the access. These security functions allow an application to query and manipulate the security features of both an object and a process or thread.

The impact of Windows security on most Windows functions is minimal, and a Windows-based application not requiring security functionality usually does not need to incorporate any special code. However, a developer can use the security features of Windows NT to provide a number of services in a Windows-based application. Generally, any application that manipulates a system-wide resource such as the system time, must use the security system to gain access to that resource. A security-aware application might allow the user to query the security attributes of a file, provide specialized feedback when access to a secure file is denied, or customize the security attributes of a file or group of files so that only a subset of other users on a network has access to the information.

Windows NT is designed to support C2-level security as defined by the US Department of Defense. Some of the most important requirements of C2-level security are shown in the following list.

• It must be possible to control access to a resource. This access control must include or exclude individual users or named groups of users.

• Memory must be protected so its contents cannot be read after it is freed by a process.

• Users must identify themselves in a unique manner when they log on. All auditable actions must identify the user performing the action.

• System administrators must be able to audit security-related events. Access to this audit data must be limited to authorized administrators.

• The system must protect itself from external interference or tampering, such as modification of the running system or of system files stored on disk.

+$#A Security Model

All named objects in Windows NT, and some unnamed objects, can be secured. The security attributes of each securable object in Windows are described by a security descriptor, that contains information about the owner of the object and by an access-control list (ACL), identifying the users and groups allowed or denied access to the object. An ACL contains an entry for each user, global group, or local group (alias) being allowed or denied access to the object. Each of these entries is an access-control entry (ACE).

At logon, a user is assigned an access token containing identifiers that represent the user and any groups to which the user belongs. Every process run on behalf of this user will have a copy of this particular access token. When a process attempts to use an object, the system compares the security attributes listed in the access token with the ACEs in the object's ACL. The system compares the access token with each ACE until access is either granted or denied or until there are no more ACEs to check. Conceivably, several ACEs could apply to a token. And, if this occurs, the access rightsaccess rights granted by each ACE accumulate. For example, if one ACE grants read access to a group in an access token and another ACE grants write access to the user, who is also a member of the group, the user will have both read and write access to the object when the access check is complete.

The following illustration shows the relationship between these blocks of security information:

{ewc msdncd, EWGraphic, group10764 0 /a "SDK_01.bmp"}

Typically, the application protecting an object is a server in that it defines the users and groups with access to the object. The application interacts with clients when they attempt to gain access to the object. Users and groups are identified by security identifiers (SIDs). An SID is a structure of variable length that uniquely identifies a user or group. SIDs are stored in a security database that an application can query by calling Win32 functions. With one exception, an SID is used to identify a user or group is never reassigned to another user or group. For a given account, the only SID that will not be the same from logon to logon is the logon-identifier SID. In the model represented by the preceding illustration, SIDs would be used to identify the following:

• The owner and group in the security descriptor

• The recipient of the access being granted by each ACE

• The user and groups in the access token

Security descriptors, SIDs, and ACLs are treated by applications as opaque structures and are intended to be manipulated only by using Win32 functions. This helps ensure that these structures remain syntactically accurate and prevents future enhancements to the security system from breaking existing code.

Pointers to doubleword values or structures must be aligned on doubleword boundaries. However, the exception is strings, for which alignment is not critical. All Win32 memory-allocation functions return handles of doubleword-aligned memory objects.

+$#A Access Tokens

When a user logs on, the system verifies the user's password by comparing it with information stored in a security database. If the password is authenticated, the system produces an access token and attaches it to the user's process. This access token identifies the user in all subsequent interactions with securable objects and contains the following information about a process:

• The user's SID

• Group SIDs

• Privileges

• An owner SID

• The SID for the primary group

• The default discretionary access-control list (ACL)

• The source of the access token

• Whether the token is a primary or impersonation token

• Current impersonation levels

• Other statistics

Every process must have an access token, but even a thread can have one assigned to it. When a thread has an access token, it supersedes the token of the process that owns the thread. This allows individual threads of a server process to impersonate clients by associating a client's access token with the thread. The only exception to this is any action requiring the SE_TCB_NAME privilege. This privilege is always checked against the process token, even if the current thread has an impersonation token.

For more information about privileges, see Privileges. For more information about security identifiers (SIDs), see Security Identifiers (SIDs). For more information about discretionary ACLs, see Access-control Lists (ACLs). For more information about the components of an access token, see the following token structures and enumerated types.

Structure or type | Specifies
TOKEN_CONTROL | Information useful in identifying an access token.
TOKEN_DEFAULT_DACL | The default discretionary ACL for an access token.
TOKEN_GROUPS | Specifies the SIDs of the access token's groups and whether any privileges are enabled.
TOKEN_INFORMATION_CLASS | Information being set in or retrieved from an access token.
TOKEN_OWNER | The SID of an access token's owner.
TOKEN_PRIMARY_GROUP | The SID of the access token's primary group.
TOKEN_PRIVILEGES | The privileges associated with an access token and whether the privileges are enabled.
TOKEN_SOURCE | The source of an access token.
TOKEN_STATISTICS | Statistics associated with an access token.
TOKEN_TYPE | Whether an access token is being used as an impersonation token.
TOKEN_USER | The SID of an access token's user.

Following are the functions an application can use to manipulate access tokens.

Function | Description
AdjustTokenGroups | Changes the group information in an access token.
AdjustTokenPrivileges | Changes the privileges in an access token.
GetTokenInformation | Retrieves information about a token.
OpenProcessToken | Retrieves the handle of the access token for a process.
OpenThreadToken | Retrieves the handle of the access token for a thread.
SetTokenInformation | Changes a token's owner, primary group, or default discretionary ACL.

+$#A Security Descriptors

A security descriptor contains the security information associated with an object. This information can include an owner, a primary group, a discretionary access-control list, and a system access-control list. This information is stored in the form of security identifiers (SIDs) and access-control lists (ACLs). For more information about access-control lists, see Access-control Lists (ACLs).

There are five groups of Win32 functions that manipulate security descriptors, corresponding to five different kinds of objects:

• Files

• User Objects (also called window-management objects)

• Kernel objects

• Registry objects

• Private objects

To manipulate security descriptors for files and directories, an application can use the GetFileSecurity and SetFileSecurity functions. These functions identify files and directories by using character strings, instead of using the handles required by other security functions.

Kernel objects and user objects are manipulated by two pairs of functions: GetKernelObjectSecurity and SetKernelObjectSecurity, and GetUserObjectSecurity and SetUserObjectSecurity. Kernel and user objects are separated in this way because it is possible for a file handle and a window handle to have the same value. An application can use these functions to ensure that the correct object referenced.

An application can use the RegGetKeySecurity and RegSetKeySecurity functions to retrieve and set security descriptors for keys in the registry.

The functions that manipulate private objects allow an application, usually a server, to associate a security descriptor with its own object type. The functions provide the ability to retrieve default information from an access token, support inheritance, and manipulate specific parts of the security descriptor. These functions are CreatePrivateObjectSecurity, GetPrivateObjectSecurity, SetPrivateObjectSecurity, and DestroyPrivateObjectSecurity.

The Win32 functions in the following table enable an application to query a security descriptor or set specific information in it.

Function | Description
GetSecurityDescriptorControl | Retrieves revision and control information.
GetSecurityDescriptorDacl | Retrieves the discretionary ACL.
GetSecurityDescriptorGroup | Retrieves primary group information.
GetSecurityDescriptorLength | Returns the length of a security descriptor.
GetSecurityDescriptorOwner | Retrieves the owner of a security descriptor.
GetSecurityDescriptorSacl | Retrieves the system ACL.
SetSecurityDescriptorDacl | Sets discretionary ACL information.
SetSecurityDescriptorGroup | Sets primary group information.
SetSecurityDescriptorOwner | Sets the owner of the security descriptor.
SetSecurityDescriptorSacl | Sets system ACL information.

To check the revision level and structural integrity of a security descriptor, an application can call the IsValidSecurityDescriptor function.

A security descriptor can be in either an absolute or self-relative format. In absolute format, a security descriptor contains pointers to its information, not the information itself. This format is useful when default settings for the owner, group, and discretionary ACL are available, because an application can simply call the InitializeSecurityDescriptor function to initialize a SECURITY_DESCRIPTOR structure and then point to preexisting components, such as SIDs and ACLs.

A security descriptor in self-relative format contains information in a contiguous block of memory. In this format, a SECURITY_DESCRIPTOR structure always starts the information, but the other components of the security descriptor can follow the structure in any order. Instead of using memory addresses, the security descriptor's components are identified by offsets from the beginning of the descriptor. This format is useful when a security descriptor must be stored on disk, transmitted by means of a communications protocol, or copied in memory.

All security descriptors provided by the operating system as the result of calls to Win32 API functions are in self-relative format. Security descriptors passed back to the operating system can be in either form, depending on the situation.

A server that copies secured objects to various media can use the MakeSelfRelativeSD function to create a self-relative security descriptor from an absolute security descriptor. The MakeAbsoluteSD function can create an absolute security descriptor from a self-relative security descriptor.

+$#A Access-control Lists (ACLs)

Part of the security descriptor for each securable object is a list specifying users and groups, and the access to the object each is granted or denied. This list is an access-control list (ACL). An ACL consists of two parts. The first is a header in the form of an ACL structure. This structure contains information pertaining to the entire ACL, such as its revision level and size. The second part is an ordered list of access-control entries (ACEs). Each ACE identifies a user, group, or local group, and the access each of them is allowed.

There are two types of ACLs: discretionary and system.

A discretionary ACL is controlled by the owner of an object and specifies the access particular users or groups can have to an object. For example, the owner of a file can use a discretionary ACL to control who can and cannot have access to the file.

When an object's discretionary ACL contains no access-control entries, no access rights have been explicitly granted. Therefore, access to the object is implicitly denied. However, when an object has no discretionary ACL, no protection is assigned to the object and any access request is granted. For information about setting no ACL for an object, see Allowing Access.

An object may also have system-level security information associated with it, in the form of a system ACL. System ACLs are controlled by system administrators. A system ACL can enable the system administrator to audit any attempts to gain access to an object. In future releases, a system ACL will also be able to raise an alarm when an unauthorized user attempts to gain access to an object.

An application can use Win32 functions to retrieve, initialize, query, and modify ACLs.

Function | Description
GetAclInformation | Retrieves revision or size information about an ACL.
GetSecurityDescriptorDacl | Retrieves a discretionary ACL from a specified security descriptor.
GetSecurityDescriptorSacl | Retrieves a system ACL from a specified security descriptor.
InitializeAcl | Initializes an ACL by creating an empty ACL structure.
IsValidAcl | Determines whether an ACL's revision level and structure are valid.
SetAclInformation | Changes the revision information in an ACL.
SetSecurityDescriptorDacl | Puts a discretionary ACL into a specified security descriptor, superseding any preexisting discretionary ACL.
SetSecurityDescriptorSacl | Puts a system access-control list into a specified security descriptor, superseding any preexisting system ACL.

To change a security descriptor's ACL, an application generally initializes a new ACL, adds ACEs to it, then uses either the SetSecurityDescriptorDacl or SetSecurityDescriptorSacl function to add the new ACL to the security descriptor. GetSecurityDescriptorDacl and GetSecurityDescriptorSacl retrieve a pointer to a preexisting ACL in a security descriptor so an application can copy information from the previous ACL as it creates a new one.

+$#A Access-control Entries (ACEs)

Each of the entries in an access-control list (ACL) is an access-control entry (ACE). Each ACE consists of three parts:

• A header in the form of an ACE_HEADER structure structure

• An access mask for the specified ACE type

• A security identifier (SID)

The ACE_HEADER structure gives information such as the type of the ACE and its size. The access mask is a 32-bit value that specifies the rights being granted, denied, or monitored. The security identifier is in the form of an SID structure and it specifies the user or group to which the ACE applies.

Discretionary ACLs have two types of ACE defined in the AceType member of the ACE_HEADER structure. They are shown in the following table.

Type | Description
ACCESS_ALLOWED_ACE_TYPE | This ACE grants the specified rights to the user or group identified by the security identifier. It is defined by an ACCESS_ALLOWED_ACE structure.
ACCESS_DENIED_ACE_TYPE | This ACE denies the specified rights to the user or group identified by the security identifier. It is defined by an ACCESS_DENIED_ACE structure.

Only one type of ACE, SYSTEM-AUDIT-ACE-TYPE, is currently supported for system ACLs. It specifies what types of access cause system-level audits. This ACE is defined by a SYSTEM_AUDIT_ACE structure.

The SYSTEM_ALARM_ACE_TYPE is not supported in the current version of Windows NT.

Security auditing is used to keep a log of security-significant events, such as which users access a secured file. The SYSTEM_AUDIT_ACE_TYPE ACE is used to generate security audit messages and causes them to be entered into a system audit log for later processing. An administrator can view this log by using Event Viewer (EVENTVWR.EXE) and can manipulate the log by using the event-logging functions.

The order of ACEs in an ACL is important. An access token is checked against the ACEs in an ACL until it either accumulates the access rights it needs, it is explicitly denied access in an access-denied ACE, or it reaches the end of the ACL and is implicitly denied access. If a user is a member of several groups represented by ACEs in the ACL, the rights granted to each group apply to the user. For example, a user may request read and write access to an object protected by an ACE that grants one group read access only. Another ACE may provide protection that grants write access. If the user belongs to both groups, the request will succeed. An ACL typically denies access to specific users or groups of users and allows access to more general categories of users. Although the ACEs in an ACL can be in any order, the security menu items in File Manager and Registry Editor (REGEDT32.EXE) only work with ACLs in which access-denied ACEs precede access-allowed ACEs.

To change a security descriptor's ACL, an application usually initializes a new ACL, adds ACEs to it, and then adds the ACL to the security descriptor. The AddAccessAllowedAce and AddAccessDeniedAce functions add an ACE at the end of a discretionary ACL. The AddAuditAccessAce function adds an ACE at the end of a system ACL. The AddAce function allows an application to specify the position of a new ACE in an ACL, and the DeleteAce function allows an application to remove an ACE from a specified position in one. The GetAce and FindFirstFreeAce functions are especially useful when copying information from a preexisting ACL to a new one.

+$#A Access Masks and Access Rights

An access right defines a particular set of abilities that can be granted or denied to a process when it attempts to use an object. For example, if an application attempts to create a subkey in the registry but does not have the KEY_CREATE_SUB_KEY access right, the system does not carry out the operation. An application usually requests a set of access rights when it opens an object, and in the case of registry keys, it makes that request when it calls the RegOpenKeyEx function.

An access mask is the component of an access-control entry (ACE) that contains the specific rights, standard rights, and generic rights defining the access a user or group has to an object. Access masks are also used to request access rights when an object is opened. For example, an application that needs to set values, create subkeys, and query values in the registry can open a key using an access mask that combines the KEY_WRITE and KEY_QUERY_VALUE access rights.

An access mask is a single 32-bit value. The first 16 bits are the specific rights and apply only to the object type associated with the access mask. Bits 16 through 23 are the standard rights applying to all objects. Bits 28 through 31 are the generic rights mapped to specific and standard rights when access to an object is requested.

Generic access rights are broad types of access whose exact implementation is determined by the application defining an object. These rights are used when protecting an object. For example, an application that defines a voice-annotation object might define specific access rights by using VOICE_PLAY and VOICE_EDIT for playing and editing the object. It might set up a GENERIC_MAPPING structure in which GENERIC_EXECUTE maps to VOICE_PLAY and GENERIC_WRITE maps to both VOICE_PLAY and VOICE_EDIT.

The following are the generic rights that have been defined.

Constant | Meaning
GENERIC_ALL | Read, write, and execute access
GENERIC_EXECUTE | Execute access
GENERIC_READ | Read access
GENERIC_WRITE | Write access

If an application receives GENERIC_WRITE access to a file, for example, it has specific rights allowing it to write and append data to the file, write file attributes, and write extended attributes. In addition, the application has the STANDARD_RIGHTS_WRITE and SYNCHRONIZE standard access rights.

The following constants are masks for the standard access rights.

Constant | Meaning
DELETE | Delete access.
READ_CONTROL | Read access to the security descriptor not including the system ACL.
STANDARD_RIGHTS_ALL | Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE access.
STANDARD_RIGHTS_EXECUTE | Currently defined to equal READ_CONTROL.
STANDARD_RIGHTS_READ | Currently defined to equal READ_CONTROL.
STANDARD_RIGHTS_REQUIRED | Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.
STANDARD_RIGHTS_WRITE | Currently defined to equal READ_CONTROL.
SYNCHRONIZE | Synchronize access. Allows a thread to wait for the object. This access type is not supported by all object types.
WRITE_DAC | Write access to the discretionary ACL.
WRITE_OWNER | Write access to the owner.

The SPECIFIC_RIGHTS_ALL constant denotes all of the specific rights, even those not be defined for an object.

The system validates each requested access against the process's access token, so requests for broad access can require a great deal of validation time. Therefore, it is generally better for applications to avoid requesting the broadest possible access to an object except when such access is actually required. Certain rights are commonly used in an ACL to deny access to an object, but they are rarely used in requests to open an object. In particular, requesting any of the access rights in the following list has a negative impact on system performance:

EVENT_ALL_ACCESS | SECTION_ALL_ACCESS
FILE_MAP_ALL_ACCESS | SEMAPHORE_ALL_ACCESS
GENERIC_ALL | SERVICE_ALL_ACCESS
KEY_ALL_ACCESS | SPECIFIC_RIGHTS_ALL
MUTEX_ALL_ACCESS | STANDARD_RIGHTS_ALL
PROCESS_ALL_ACCESS | THREAD_ALL_ACCESS
SC_MANAGER_ALL_ACCESS | TOKEN_ALL_ACCESS

The MAXIMUM_ALLOWED constant specifies that an object is to be opened by using all the access rights that are valid for the given user. An additional access type, ACCESS_SYSTEM_SECURITY, is needed to manipulate an object's system ACL. Neither MAXIMUM_ALLOWED nor ACCESS_SYSTEM_SECURITY can be used in an DACL. However, you can use ACCESS_SYSTEM_SECURITY in a SACL to audit use of that bit in an access attempt.

The MapGenericMask function maps a series of generic rights specified in a GENERIC_MAPPING structure to specific and standard rights in an access mask. An application can use the GENERIC_MAPPING structure to specify the access rights for an object when it is created. When a client process requests access to the object, the server can use MapGenericMask to translate the client's requested access into the standard and specific rights for that object.

To discover whether a client process has the proper access rights to an object, a server can call the AccessCheck or AccessCheckAndAuditAlarm function. The AreAllAccessesGranted and AreAnyAccessesGranted functions compare a requested access mask with a granted access mask.

+$#A Security Identifiers (SIDs)

A security identifier (SID) is a unique value of variable length used to identify a user or group. The SID assigned to a user when he or she logs on becomes part of the access token that accompanies any process begun by that user. Except for the logon SID, an SID is always unique. When it has been used to identify a user or group, it cannot be used again, at any time, to identify another user or group.

SIDs identify several separate elements. These include the owner and group in security descriptors, the recipient of the access being granted by access-control entries (ACEs), and the user, as well as groups of which the user is a member in access tokens.

An SID also contains the following information.

• A 48-bit identifier authority value

• A revision level

• A variable number of subauthority values (relative identifiers)

The identifier authority value actually contains two values and is the most important piece of information in an SID. It contains a value identifying the agency that issued the SID, usually representing a Microsoft® Server™ domain, and a 32-bit relative identifier (RID) value, to uniquely identify the user or group in that agency. Joining these values ensures that no two SIDs will be the same, even if two different SID-issuing authorities issue the same RID. Each SID-issuing authority issues a given RID only once.

A standardized shorthand notation for SIDs makes it simpler to visualize their components:

S-R-I-S-S...

In the notation shown above, S identifies the series of digits as an SID, R is the revision level, I is the identifier-authority value, and S is the subauthority value. An SID could be written in this notation as follows:

S-1−4138−86

In this example, the SID has a revision level of 1, an identifier-authority value of 4138, and one subauthority value of 86.

An application is never required to manipulate an SID directly. The following Win32 functions provide all the functionality required to work with SIDs.

Function | Description
AllocateAndInitializeSid | Allocates and initializes an SID with the specified number of subauthorities.
CopySid | Copies a source SID to a buffer.
EqualPrefixSid | Tests two SID prefix values for equality. An SID prefix is the entire SID except for the last subauthority value.
EqualSid | Tests two SIDs for equality. They must match exactly to be considered equal.
FreeSid | Frees an SID previously allocated by using the AllocateAndInitializeSid function.
GetLengthSid | Retrieves the length of an SID.
GetSidIdentifierAuthority | Retrieves a pointer to an SID's identifier authority.
GetSidLengthRequired | Retrieves the size of the buffer required to store an SID with a specified number of subauthorities.
GetSidSubAuthority | Retrieves a pointer to a specified subauthority in an SID.
GetSidSubAuthorityCount | Retrieves the number of subauthorities in an SID.
InitializeSid | Initializes an SID structure.
IsValidSid | Tests the validity of an SID by verifying that the revision number is within a known range and that the number of subauthorities is less than the maximum.
LookupAccountName | Retrieves the SID corresponding to a specified account name. Use the GetUserName function to retrieve the user name for the current thread.
LookupAccountSid | Retrieves the account name corresponding to a specified SID.

Some identifier authorities are predefined, as shown in the following table.

Identifier authority | SID Value
SECURITY_NULL_SID_AUTHORITY | 0
SECURITY_WORLD_SID_AUTHORITY | 1
SECURITY_LOCAL_SID_AUTHORITY | 2
SECURITY_CREATOR_SID_AUTHORITY | 3
SECURITY_NT_AUTHORITY | 5

The following RID values are also defined.

Relative identifier authority | Value | Superior authority
SECURITY_NULL_RID | 0 | S-1−0
SECURITY_WORLD_RID | 0 | S-1−1
SECURITY_LOCAL_RID | 0 | S-1−2
SECURITY_CREATOR_OWNER_RID | 0 | S-1−3
SECURITY_CREATOR_GROUP_RID | 1 | S-1−3

An application can combine an identifier authority and one of these RID values to create an SID that is meaningful on all installations. For example, S-1−1−0 (SECURITY_WORLD_SID_AUTHORITY and SECURITY_WORLD_RID) always identifies the special group representing all users. These security identifiers are universal well-known SIDs. A universal well-known SID is meaningful on all secure systems using this security model, including systems not running Windows NT. The AllocateAndInitializeSid function provides a simple method of combining an identifier authority and an RID.

The following are some universal well-known SIDs.

Universal well-known SID | Value | Identifies
Null SID | (S-1−0−0) | A group with no members. This is often used when an SID value is not known.
World | (S-1−1−0) | A group that includes all users.
Local | (S-1−2−0) | Users who log on to terminals locally (physically) connected to the system.
Creator Owner ID | (S-1−3−0) | A security identifier to be replaced by the security identifier of the user who created a new object. This SID is used in inheritable ACLs.
Creator Group ID | (S-1−3−1) | Identifies a security identifier to be replaced by the primary-group SID of the user who created a new object. Use this SID in inheritable ACLs.

Another predefined identifier authority is SECURITY_NT_AUTHORITY (S-1−5). This identifier authority produces SIDs that are not universal but are meaningful only on Windows NT installations. An application can use the following RID values with SECURITY_NT_AUTHORITY to create well-known SIDs.

Constant | Identifies
SECURITY_DIALUP_RID (S-1−5−1) |
 | Users who log on to terminals using a dialup modem. This is a group identifier.
SECURITY_NETWORK_RID (S-1−5−2) |
 | Users who log on across a network. This is a group identifier.
SECURITY_BATCH_RID (S-1−5−3) |
 | Users who log on using a batch queue facility. This is a group identifier.
SECURITY_INTERACTIVE_RID (S-1−5−4) |
 | Users who log on for interactive operation. This is a group identifier.
SECURITY_LOGON_IDS_RID (S-1−5−5-X-Y) |
 | A logon session. This is used to ensure that only processes in a given logon session can gain access to the window-station objects for that session. The X and Y values for these SIDs are different for each logon session. The value SECURITY_LOGON_IDS_RID_COUNT is the number of RIDs in this identifier (5-X-Y).
SECURITY_SERVICE_RID (S-1−5−6) |
 | An account authorized to perform security services.
SECURITY_LOCAL_SYSTEM_RID (S-1−5−0x12) |
 | The user account used by the operating system.
SECURITY_BUILTIN_DOMAIN_RID (S-1−5−0x20) |
 | The built-in system domain.

The following RIDs are relative to each domain.

RID | Identifies
DOMAIN_USER_RID_ADMIN | The administrative user account in a domain.
DOMAIN_USER_RID_GUEST | The guest-user account in a domain. This account can be logged onto automatically by users who do not have an account.
DOMAIN_GROUP_RID_ADMINS | The domain administrator's group. This account exists only on systems running Microsoft® LAN Manager for Windows NT™, not Windows NT systems.
DOMAIN_GROUP_RID_USERS | A group containing all user accounts in a domain. All users are automatically added to this group.

An alias is a local group and is similar to a global group, but it can contain members from other domains. Its members can be users or other groups. Some domain-relative RIDs, such as the following, can be used to form well-known SIDs used as local groups.

RID | Identifies
DOMAIN_ALIAS_RID_ADMINS | A local group used for administration of the domain.
DOMAIN_ALIAS_RID_USERS | A local group representing all users in the domain.
DOMAIN_ALIAS_RID_GUESTS | A local group representing guests of the domain.
DOMAIN_ALIAS_RID_POWER_USERS | A local group used to represent a user or set of users that expect to treat a system as if it were their personal computer rather than a multiuser workstation.
DOMAIN_ALIAS_RID_ACCOUNT_OPS | A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group permits control over non-administrator accounts.
DOMAIN_ALIAS_RID_SYSTEM_OPS | A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group performs system administrative functions, not including security functions. It establishes network shares, controls printers, unlocks workstations, and performs other operations.
DOMAIN_ALIAS_RID_PRINT_OPS | A local group existing only on systems running Microsoft LAN Manager for Windows NT. This local group controls printers and print queues.
DOMAIN_ALIAS_RID_BACKUP_OPS | A local group used for controlling assignment of file backup-and-restore privileges.
DOMAIN_ALIAS_RID_REPLICATOR | A local group responsible for copying security databases from the primary domain controller to the backup domain controllers.

+$#A Privileges

A privilege is used to control access to an object or service more strictly than is normal with discretionary access control. A system manager uses privileges to control which users are able to manipulate system resources. An application uses privileges when it changes a system resource, such as when it changes the system time or shuts down the system.

A privilege is a locally unique identifier (LUID) identified by a character string. This 64-bit value is guaranteed to be unique on the operating system that generated it until the system is restarted. For example, SE_SYSTEMTIME_NAME is a string identifying an LUID. A privilege has three representations, as shown below.

• A string name, meaningful across systems, called a global program name (for example, SE_SYSTEMTIME_NAME).

• A readable name that can be displayed to the user when necessary. For example, "Change the system time."

• A local representation that differs from computer to computer.

Privileges provide access to services rarely needed by most users. An account usually has privileges that are disabled, and they must be enabled to be used. For example, to set the time on the local computer, an application has to set the SE_PRIVILEGE_ENABLED attribute for the SE_SYSTEMTIME_NAME privilege. For security reasons, disable an enabled privilege when it is no longer needed.

The following privileges are defined by Windows NT.

Privilege | Description
SE_ASSIGNPRIMARYTOKEN_NAME | Required to assign the primary token of a process.
SE_AUDIT_NAME | Required to generate audit-log entries. Give this privilege to secure servers.
SE_BACKUP_NAME | Required to perform backup operations.
SE_CHANGE_NOTIFY_NAME | Required to receive notifications of changes to files or directories. This privilege also causes the system to skip all traversal access checks. It is enabled by default for all users.
SE_CREATE_PAGEFILE_NAME | Required to create a paging file.
SE_CREATE_PERMANENT_NAME | Required to create a permanent object.
SE_CREATE_TOKEN_NAME | Required to create a primary token.
SE_DEBUG_NAME | Required to debug a process.
SE_INC_BASE_PRIORITY_NAME | Required to increase the base priority of a process.
SE_INCREASE_QUOTA_NAME | Required to increase the quota assigned to a process.
SE_LOAD_DRIVER_NAME | Required to load or unload a device driver.
SE_LOCK_MEMORY_NAME | Required to lock physical pages in memory.
SE_PROF_SINGLE_PROCESS_NAME | Required to gather profiling information for a single process.
SE_REMOTE_SHUTDOWN_NAME | Required to shut down a system using a network request.
SE_RESTORE_NAME | Required to perform restore operations.
SE_SECURITY_NAME | Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege identifies its holder as a security operator.
SE_SHUTDOWN_NAME | Required to shut down a local system.
SE_SYSTEM_ENVIRONMENT_NAME | Required to modify the non-volatile RAM of systems that use this type of memory to store configuration information.
SE_SYSTEM_PROFILE_NAME | Required to gather profiling information for the entire system.
SE_SYSTEMTIME_NAME | Required to modify the system time.
SE_TAKE_OWNERSHIP_NAME | Required to take ownership of an object without being granted discretionary access. This privilege allows the owner value to be set only to those values that the holder may legitimately assign as the owner of an object.
SE_TCB_NAME | This privilege identifies its holder as part of the trusted computer base. Some trusted protected subsystems are granted this privilege.
SE_UNSOLICITED_INPUT_NAME | Required to read unsolicited input from a terminal device.

The following functions are provided for working with privileges.

Function | Description
LookupPrivilegeValue | Allows an application to retrieve the LUID corresponding to a privilege on the local system. This is the local representation of that privilege and an application can set it in the TOKEN_PRIVILEGES and LUID_AND_ATTRIBUTES structures.
LookupPrivilegeDisplayName | Retrieves a displayable privilege name. For example, "Force shutdown from a remote system."
LookupPrivilegeName | Retrieves a programmatic privilege name.
PrivilegeCheck | Determines whether a client application has the privileges required to gain access to an object, such as, SE_REMOTE_SHUTDOWN_NAME.

+$#A Impersonation

Impersonation is the ability of a process to take on the security attributes of another process. Typically, a server thread impersonates a client thread to act on behalf of that client to access objects or validate access to its own objects. For example, when a client in a DDE conversation requests information from a DDE server, the server can impersonate the client so the system can verify that the client is allowed access to the information.

Currently, there are three impersonation levels:

Impersonation Level | Meaning
SecurityAnonymous | Indicates the client does not want the server to obtain identification information about the client.
SecurityIdentification | Allows the server to obtain information about the client, such as security identifiers and privileges, without being able to impersonate the client. This is useful for servers who export their own objects, such as tables and views exported by a database product. Using the retrieved client security information, the server is able to make access-validation decisions for itself even though it is unable to use other services as the client.
SecurityImpersonation | Allows the server to impersonate the client's security context on its local system. The server cannot impersonate the client on remote systems.

A fourth level, SecurityDelegation, is not supported in this release of Windows NT.

A DDE server application can impersonate a client by calling the DdeImpersonateClient function. When it has finished the task requiring the impersonation, the DDE server application can revert to its own security information by calling the RevertToSelf function. Similarly, a named-pipe server can use the ImpersonateNamedPipeClient and RevertToSelf functions. The DuplicateToken function creates an impersonation token that duplicates an existing token.

The ImpersonateSelf function enables a thread to generate a copy of its own access token. This is useful when an application needs to change the security attributes of a single thread. For example, sometimes only one thread of a process requires a special privilege.

+$#A Security in New Objects

When an application creates an object, any values explicitly provided in a security descriptor are assigned to the object. If a security descriptor is not explicitly provided, the system searches for security information in different places, depending on the information required.

This guide describes the algorithms for security attribute assignments to newly created objects.

+$#A Assignment of Owner

To assign an owner to a new object when a security descriptor is not provided, the system checks the creator's access token for a default value. If no default value is found, the creator's user identifier (from the access token) is assigned as the object's owner. Even when an owner is provided in a security descriptor, the system checks these values in the access token to make sure the specified owner can be assigned ownership of the object.

+$#A Assignment of Group

To assign a primary group to a new object when a security descriptor is not provided, the system uses the primary group in the creator's access token.

+$#A Assignment of Discretionary ACL

To assign a discretionary access-control list (ACL) to a new object when a security descriptor is not provided, the system checks the parent's discretionary ACL for inheritable access-control entries (ACEs) and creates an ACL from any it finds. If there are no inheritable ACEs, the system checks the creator's security descriptor for a default discretionary ACL. If none is found in the security descriptor, the system looks in the creator's access token. If none of these sources provides a discretionary ACL, the object is created without one, and universal unconditional access to the object is granted.

+$#A Assignment of System ACL

To assign a system ACL to a new object when a security descriptor is not provided, the system checks the parent's system ACL for inheritable ACEs and creates an ACL from any it finds. If there are no inheritable ACEs, the system checks the creator's security descriptor for a default system ACL. When a system ACL is provided explicitly or by default in a security descriptor, the creator of the object must have the SE_SECURITY_NAME privilege, although this privilege is not required if the system ACL is acquired by inheritance. If none of these sources provides a system ACL, the object is created without one.

+$#A Security Attributes Inheritance

The security attributes of an object can be passed from process to process through the mechanism of handle inheritance. In addition, individual ACEs can be inherited in an ACL by new objects.

+$#A Handle Inheritance

If a new process inherits a handle, it also inherits the security attributes associated with that handle. A pointer to a SECURITY_ATTRIBUTES structure is used as a parameter in most Win32 kernel and window-management functions that return object handles. The bInheritHandle member of this structure specifies whether the returned handle is inherited when a new process is created. The security attributes of the inherited handle are identical to those of the original handle.

+$#A ACE Inheritance

Each ACE in an ACL begins with an ACE_HEADER structure structure specifying how that ACE is inherited. Among other things, the flags in an ACE_HEADER structure specify whether the ACE is inherited by container objects (directories or registry keys) or by non-container objects, such as files.

ACE Inheritance in Container Objects

This section outlines the rules governing the inheritance of ACEs by container objects.

If the CONTAINER_INHERIT_ACE flag is set in the ACE_HEADER structure of the parent ACE, a copy of the ACE is added to the end of the inherited ACL and the following actions are performed on the inherited ACE:

• All inheritance flags in the inherited ACE are cleared.

• If the ACE contains an access mask and any generic access flags are set, the generic rights are mapped to the standard and specific rights for the child object type.

If neither the CONTAINER_INHERIT_ACE flag nor the NO_PROPAGATE_INHERIT_ACE flag is set, the OBJECT_INHERIT_ACE and INHERIT_ONLY_ACE flags are set. If both the CONTAINER_INHERIT_ACE and the NO_PROPAGATE_INHERIT_ACE are set, the ACE is added to the end of the inherited ACL.

If neither the NO_PROPAGATE_INHERIT_ACE nor the CONTAINER_INHERIT_ACE flag is set in the ACE_HEADER structure of the parent ACE, a copy of the ACE is added to the end of the inherited ACL and its INHERIT_ONLY_ACE flag is set.

Otherwise, the ACE is not inherited.

ACE Inheritance in Non-container Objects

The following rules govern the inheritance of ACEs in objects other than containers.

If the OBJECT_INHERIT_ACE flag is set in the ACE_HEADER structure of the parent ACE, the ACE is copied to the end of the inherited ACL and:

• The INHERIT_ONLY_ACE flag in the inherited ACE is cleared.

• The NO_PROPAGATE_INHERIT_ACE, OBJECT_INHERIT_ACE, and CONTAINER_INHERIT_ACE flags in the inherited ACE are cleared.

• If the ACE contains an access mask and, if any generic access flags are set, the generic rights are mapped to the standard and specific rights for the child object type.

Otherwise, the ACE is not inherited.

+$#A Duplicating Handles

Among cooperating processes, any one process is only as restricted as the least restricted process. For example, a process can call the DuplicateHandle function to duplicate an object handle belonging to a second (source) process into a third (target) process. If the access rights requested for the target process are greater than the rights held by the source process, the system checks the requested access rights against the security context of the process calling DuplicateHandle. If the calling process has the required access rights, the handle is duplicated, and the target process has greater access to the object than the source process.

+$#A Audit Generation

C2-level security requirements specify that system administrators must be able to audit security-related events and that access to this audit data must be limited to authorized administrators. The Win32 API provides functions enabling an administrator to monitor security-related events.

The system access-control list (ACL) contains audit access-control entries (ACEs) for an object. An application can use the GetSecurityDescriptorSacl and SetSecurityDescriptorSacl functions to retrieve an existing system ACL or to set a new one. The AddAuditAccessAce function adds an ACE to a system ACL that causes the system to record specified access attempts in a security log. This security log can be read by using the Microsoft Windows Event Viewer (EVENTVWR.EXE), and can be manipulated by using the event-logging functions discussed in Event Logging. For more information about system ACLs, see Access-control Lists (ACLs).

An application can use the ObjectPrivilegeAuditAlarm function to generate audit and alarm messages whenever a process attempts to complete a privileged operation. The PrivilegedServiceAuditAlarm generates audit and alarm messages whenever a process attempts to perform a privileged system-service operation.

The ObjectCloseAuditAlarm generates audit messages when an object is deleted. The ObjectOpenAuditAlarm function generates audit messages when a process attempts to open or gain access to an object.

+$#A Securable Objects

The kernel and window-management systems contain securable objects. This term refers to an object that has a security descriptor.

All named objects are securable. Some unnamed objects, such as unnamed process and thread objects, have security descriptors also.

You can use Win32 functions to query and set the security attributes of securable objects. The following are types of securable objects and the functions an application can use to manipulate their security attributes.

Object type | Functions
Files and directories | GetFileSecurity, SetFileSecurity
Mailslots | GetFileSecurity, SetFileSecurity
Pipes | GetFileSecurity, SetFileSecurity
Kernel objects (processes, threads, access tokens, and file-mapping objects) | SetKernelObjectSecurity, GetKernelObjectSecurity
Window-management objects (window stations and desktops) | GetUserObjectSecurity, SetUserObjectSecurity
Registry objects | RegGetKeySecurity, RegSetKeySecurity
Service objects | QueryServiceObjectSecurity, SetServiceObjectSecurity
Synchronization objects | SetKernelObjectSecurity, GetKernelObjectSecurity
Private objects (objects private to the creating application) | CreatePrivateObjectSecurity, DestroyPrivateObjectSecurity, GetPrivateObjectSecurity, SetPrivateObjectSecurity

+$#A File and Directory Objects

File and directory objects can be secured only when the New Technology file system (NTFS) is in use.

The access rights for files and directories must be manipulated by using the generic access rights.

For file objects, the handle returned by the CreateFile function has SYNCHRONIZE access and the right to read file attributes, in addition to the access rights specified in the call (GENERIC_READ, GENERIC_WRITE, or both).

GENERIC_READ access for file objects combines STANDARD_RIGHTS_READ and SYNCHRONIZE with rights that allow the process to read data from the file, read file attributes, and read extended attributes.

GENERIC_WRITE access for file objects combines STANDARD_RIGHTS_WRITE and SYNCHRONIZE with rights that allow the process to write data to the file, append data to it, write file attributes, and write extended attributes.

An application cannot use an access-denied ACE to deny only GENERIC_READ or GENERIC_WRITE access to a file. If an application denies GENERIC_WRITE access to a file, SYNCHRONIZE access is implicitly denied. When the CreateFile function is used in an attempt to open the file for read access, the function requests SYNCHRONIZE access and the operation fails. Instead of denying read or write access to a file, an application can explicitly allow the permitted access rights.

For directory objects, the handle returned by the CreateDirectory function has SYNCHRONIZE access and the right to list the contents of the directory.

For more information about files and directories, see Files.

+$#A Mailslot Objects

The handle returned by the CreateMailslot function has GENERIC_READ, SYNCHRONIZE, and WRITE_DAC access to the mailslot object.

GENERIC_READ access for a mailslot combines STANDARD_RIGHTS_READ with rights allowing the process to read data from the mailslot, read mailslot attributes, and read extended attributes.

For more information about mailslots, see Mailslots.

+$#A Pipe Objects

The security attributes of named pipes differ slightly from those of anonymous pipes.

+$#A Access Rights to Named Pipes

The handle returned by the CreateNamedPipe function always has SYNCHRONIZE access. It also has GENERIC_READ, GENERIC_WRITE, or both, depending on the open mode of the pipe. The following are the access rights for each open mode.

Open mode | Access rights
PIPE_ACCESS_DUPLEX | GENERIC_READ, GENERIC_WRITE, and SYNCHRONIZE
PIPE_ACCESS_INBOUND | GENERIC_READ and SYNCHRONIZE
PIPE_ACCESS_OUTBOUND | GENERIC_WRITE and SYNCHRONIZE

GENERIC_READ access for a named pipe combines STANDARD_RIGHTS_READ access with rights that allow the process to read data from the named pipe and to read named-pipe attributes.

GENERIC_WRITE access for a named pipe combines STANDARD_RIGHTS_WRITE with rights that allow the process to write data to the named pipe and to write named-pipe attributes.

+$#A CreatePipe Function and Anonymous Pipes

The handles returned by the CreatePipe function have GENERIC_READ, GENERIC_WRITE, and SYNCHRONIZE access to the anonymous pipe, in addition to the right to write the attributes of the pipe. GENERIC_READ and GENERIC_WRITE access map to the same access rights as for named pipes.

For more information about pipes, see Pipes.

+$#A Special Access Kernel Objects

The following are the kernel objects for which the system defines special access rights:

• Processes

• Threads

• Access tokens

• File mapping

+$#A Process Objects

The valid access rights for process objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
PROCESS_ALL_ACCESS | Specifies all possible access rights for a process object.
PROCESS_CREATE_PROCESS | Required to create a process.
PROCESS_CREATE_THREAD | Required to create a thread.
PROCESS_DUP_HANDLE | Required to duplicate a handle.
PROCESS_QUERY_INFORMATION | Required to retrieve certain information about a process, such as its priority class.
PROCESS_SET_INFORMATION | Required to set certain information about a process, such as its priority class.
PROCESS_TERMINATE | Required to terminate a process.
PROCESS_VM_OPERATION | Required to perform an operation on the address space of a process.
PROCESS_VM_READ | Required to read memory in a process.
PROCESS_VM_WRITE | Required to write to memory in a process.
SYNCHRONIZE | Required to wait for the process to terminate.

The handle returned by the CreateProcess function has PROCESS_ALL_ACCESS access to the process object.

For more information about processes, see Processes and Threads.

+$#A Thread Objects

The valid access rights for thread objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
SYNCHRONIZE | Required to wait for the thread to exit.
THREAD_ALL_ACCESS | Specifies all possible access rights for a thread object. A thread created with THREAD_ALL_ACCESS access is created with its execution suspended.
THREAD_DIRECT_IMPERSONATION | Required for a server thread that impersonates a client.
THREAD_GET_CONTEXT | Required to read the context of a thread using GetThreadContext.
THREAD_IMPERSONATE | Required to use a thread's security information directly without calling it by using a communication mechanism that provides impersonation services.
THREAD_QUERY_INFORMATION | Required to read certain information from the thread object.
THREAD_SET_CONTEXT | Required to write the context of a thread.
THREAD_SET_INFORMATION | Required to set certain information in the thread object.
THREAD_SET_THREAD_TOKEN | Required to set the impersonation token for a thread.
THREAD_SUSPEND_RESUME | Required to suspend or resume a thread.
THREAD_TERMINATE | Required to terminate a thread.

The handle returned by the CreateThread function has THREAD_ALL_ACCESS access to the thread object.

For more information about threads, see Processes and Threads.

+$#A Access-token Objects

A process or thread cannot wait on an access-token object because the access-token object type does not support the SYNCHRONIZE standard access type.

The access rights defined for access-token objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
TOKEN_ADJUST_DEFAULT | Required to adjust default information, such as the owner, primary group, and default ACL in an access token.
TOKEN_ADJUST_GROUPS | Required to adjust the groups in an access token.
TOKEN_ADJUST_PRIVILEGES | Required to adjust the privileges in an access token.
TOKEN_ASSIGN_PRIMARY | Required to attach a primary token to a process. The SE_CREATE_TOKEN_NAME privilege is also required to accomplish this task.
TOKEN_DUPLICATE | Required to duplicate an access token.
TOKEN_EXECUTE | Combines STANDARD_RIGHTS_EXECUTE and TOKEN_IMPERSONATE.
TOKEN_IMPERSONATE | Required to attach an impersonation access token to a process.
TOKEN_QUERY | Required to query an access token.
TOKEN_QUERY_SOURCE | Required to query the source of an access token.
TOKEN_READ | Combines STANDARD_RIGHTS_READ and TOKEN_QUERY.
TOKEN_WRITE | Combines STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, and TOKEN_ADJUST_DEFAULT.

The TOKEN_ALL_ACCESS access right combines all possible access rights for a token.

The access rights for tokens are typically specified in calls to the OpenProcessToken and OpenThreadToken functions.

+$#A File Mapping Objects

A process or thread cannot wait on an access-token object because the access-token object type does not support the SYNCHRONIZE standard access type.

The access rights defined for file mapping objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
FILE_MAP_READ | Read access to the file-mapping object is requested. This allows a readable view of the file to be mapped.
FILE_MAP_WRITE | Write access to the file mapping object is requested. This allows a writable view of the file to be mapped. If the file mapping access rights do not include PAGE_READWRITE, this access type does not allow writing the mapped file.
SECTION_EXTEND_SIZE | Required to extend the size of a file mapping object.
SECTION_MAP_EXECUTE | Required to complete an operation in a file mapping object.
SECTION_MAP_READ | Equivalent to FILE_MAP_READ.
SECTION_MAP_WRITE | Equivalent to FILE_MAP_WRITE.
SECTION_QUERY | Required to query a file mapping object.

If the call to the CreateFileMapping function specifies the PAGE_READWRITE protection flag, the handle to the file mapping object has SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE access. If the call to CreateFileMapping specifies PAGE_READONLY, the handle of the file mapping object has SECTION_QUERY | SECTION_MAP_READ access.

The FILE_MAP_ALL_ACCESS access right specifies all possible access rights for the file mapping object. The SECTION_ALL_ACCESS access right is equivalent to it.

For more information about file-mapping objects, see File Mapping.

+$#A Window-management Objects

The following are the securable window-management objects.

Securable Object | Description
Window station | An object that contains a clipboard, a set of global atoms, and a group of desktop objects. The interactive window station assigned to the logon session of the interactive user also contains the display device, keyboard, and mouse. Window stations provide the first level of security for a work station and are the source of inheritable security for desktop objects.
Desktop | An object that has a display surface and contains windows, menus, and hooks. A desktop object is a subobject of a window-station object, from which it inherits security.

+$#A Window Station Objects

The following are the window station access rights and their meanings.

Constant | Description
WINSTA_ACCESSCLIPBOARD | Required to use the clipboard.
WINSTA_ACCESSGLOBALATOMS | Required to manipulate global atoms.
WINSTA_CREATEDESKTOP | Required to create new desktop objects on the window station.
WINSTA_ENUMDESKTOPS | Required to enumerate existing desktop objects.
WINSTA_ENUMERATE | Required for the window station to be enumerated.
WINSTA_EXITWINDOWS | Required to successfully call the ExitWindows or ExitWindowsEx function. Window stations can be shared by users and this access type can prevent other users of a window station from logging off the window station owner.
WINSTA_READATTRIBUTES | Required to read the attributes of a window station object. This attribute includes color settings and other global window station properties.
WINSTA_READSCREEN | Required to access screen contents.
WINSTA_WRITEATTRIBUTES | Required to modify the attributes of a window station object. The attributes include color settings and other global window-station properties.

The following are the generic mappings for the interactive window-station object, which is the window station assigned to the logon session of the interactive user.

Value | Maps to
GENERIC_READ | STANDARD_RIGHTS_READ, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_READATTRIBUTES, and WINSTA_READSCREEN
GENERIC_WRITE | STANDARD_RIGHTS_WRITE, WINSTA_ACCESSCLIPBOARD, WINSTA_CREATEDESKTOP, and WINSTA_WRITEATTRIBUTES
GENERIC_EXECUTE | STANDARD_RIGHTS_EXECUTE, WINSTA_ACCESSGLOBALATOMS, and WINSTA_EXITWINDOWS
GENERIC_ALL | STANDARD_RIGHTS_REQUIRED, WINSTA_ACCESSCLIPBOARD, WINSTA_ACCESSGLOBALATOMS, WINSTA_CREATEDESKTOP, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_EXITWINDOWS, WINSTA_READATTRIBUTES, WINSTA_READSCREEN, and WINSTA_WRITEATTRIBUTES

The following are the generic mappings for a noninteractive window-station object. The system assigns noninteractive window stations to all logon sessions other than that of the interactive user.

Value | Maps to
GENERIC_READ | STANDARD_RIGHTS_READ, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, and WINSTA_READATTRIBUTES
GENERIC_WRITE | STANDARD_RIGHTS_WRITE, WINSTA_ACCESSCLIPBOARD, and WINSTA_CREATEDESKTOP
GENERIC_EXECUTE | STANDARD_RIGHTS_EXECUTE, WINSTA_ACCESSGLOBALATOMS, and WINSTA_EXITWINDOWS
GENERIC_ALL | STANDARD_RIGHTS_REQUIRED, WINSTA_ACCESSCLIPBOARD, WINSTA_ACCESSGLOBALATOMS, WINSTA_CREATEDESKTOP, WINSTA_ENUMDESKTOPS, WINSTA_ENUMERATE, WINSTA_EXITWINDOWS, and WINSTA_READATTRIBUTES

An application can call the GetProcessWindowStation function to retrieve a handle of its assigned window station. The security attributes of the window station can be retrieved and set by using this handle in calls to the GetUserObjectSecurity and SetUserObjectSecurity functions. An application can use the GetUserObjectInformation and SetUserObjectInformation functions to get and set information about a window-station object. For more information on window-station objects, see Window Stations and Desktops.

+$#A Desktop Objects

A new desktop inherits its security descriptor from its parent window station.

The following are the desktop access rights and their meanings.

Constant | Description
DESKTOP_CREATEMENU | Required to create a menu on the the desktop.
DESKTOP_CREATEWINDOW | Required to create a window on the desktop.
DESKTOP_ENUMERATE | Required for the desktop to be enumerated.
DESKTOP_HOOKCONTROL | Required to establish any of the window hooks.
DESKTOP_JOURNALPLAYBACK | Required to perform journal playback on a desktop.
DESKTOP_JOURNALRECORD | Required to perform journal recording on a desktop.
DESKTOP_READOBJECTS | Required to read objects on the desktop.
DESKTOP_SWITCHDESKTOP | Required to activate the desktop using the SwitchDesktop function.
DESKTOP_WRITEOBJECTS | Required to write objects on the desktop.

The following are the generic mappings for a desktop object contained in the interactive window station of the user's logon session.

Value | Maps to
GENERIC_READ | DESKTOP_ENUMERATE, DESKTOP_READOBJECTS, and STANDARD_RIGHTS_READ
GENERIC_WRITE | DESKTOP_CREATEMENU, DESKTOP_CREATEWINDOW, DESKTOP_HOOKCONTROL, DESKTOP_JOURNALPLAYBACK, DESKTOP_JOURNALRECORD, DESKTOP_WRITEOBJECTS, and STANDARD_RIGHTS_WRITE
GENERIC_EXECUTE | DESKTOP_SWITCHDESKTOP and STANDARD_RIGHTS_EXECUTE
GENERIC_ALL | DESKTOP_CREATEMENU, DESKTOP_CREATEWINDOW, DESKTOP_ENUMERATE, DESKTOP_HOOKCONTROL, DESKTOP_JOURNALPLAYBACK, DESKTOP_JOURNALRECORD, DESKTOP_READOBJECTS, DESKTOP_SWITCHDESKTOP, DESKTOP_WRITEOBJECTS, and STANDARD_RIGHTS_REQUIRED

An application can call the GetThreadDesktop function to retrieve a handle of the desktop for a given thread. The desktop's security attributes can be retrieved and set by using this handle in calls to GetUserObjectSecurity and SetUserObjectSecurity. An application can use the GetUserObjectInformation and SetUserObjectInformation functions to get and set information about a desktop object. For more information on desktop objects, see Window Stations and Desktops.

+$#A Registry Key Objects

The following are the registry-key access rights and their meanings.

Value | Meaning
KEY_ALL_ACCESS | Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK values.
KEY_CREATE_LINK | Required to create a link to a registry-key object.
KEY_CREATE_SUB_KEY | Required to create a subkey of a registry-key object.
KEY_ENUMERATE_SUB_KEYS | Required to enumerate the subkeys of a registry-key object.
KEY_EXECUTE | Equivalent to KEY_READ.
KEY_NOTIFY | Required to request change notifications for a registry key or for subkeys of a registry key.
KEY_QUERY_VALUE | Required to query a value of a registry-key object.
KEY_READ | Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
KEY_SET_VALUE | Required to create or set a value of a registry-key object.
KEY_WRITE | Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY values.

For more information about registry objects, see Registry.

+$#A Service Objects

The access rights and generic mappings for service-control manager objects and service objects are given in the reference materials for the OpenService and OpenSCManager functions. For information about service objects, see Services.

+$#A Synchronization Objects

There are three kinds of securable synchronization objects: semaphores, events, and mutexes. The fourth type of synchronization object is critical sections. Because they are per-process objects and not kernel objects, they are not securable.

+$#A Semaphore Objects

The valid access rights for semaphore objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
SEMAPHORE_ALL_ACCESS | Specifies all possible access rights for a semaphore object.
SEMAPHORE_MODIFY_STATE | Specifies modify state (release) access.
SYNCHRONIZE | Specifies synchronization (wait) access.

The handle returned by the CreateSemaphore function has SEMAPHORE_ALL_ACCESS access to the semaphore object.

+$#A Event Objects

The valid access rights for event objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
EVENT_ALL_ACCESS | Specifies all possible access rights for an event object.
EVENT_MODIFY_STATE | Specifies modify state (release) access.
SYNCHRONIZE | Specifies synchronization (wait or release) access.

The handle returned by the CreateEvent function has EVENT_ALL_ACCESS access to the semaphore object.

+$#A Mutex Objects

The valid access rights for mutex objects are STANDARD_RIGHTS_REQUIRED and the rights given in the following table.

Value | Meaning
MUTEX_ALL_ACCESS | Specifies all possible access rights for a mutex object.
MUTEX_MODIFY_STATE | Specifies modify state (release) access.
SYNCHRONIZE | Specifies synchronization (wait or release) access.

The handle returned by the CreateMutex function has MUTEX_ALL_ACCESS access to the mutex object.

+$#A Using Security

The following topics demonstrate some practical applications of the Win32 API security functions and structures. As noted in File and Directory Objects, file objects can be secured only when the NTFS file system is in use.

• Denying all access

• Allowing access

• Setting privileges

• Security editor

• Designing a protected server

+$#A Denying All Access

The following example demonstrates adding an empty discretionary access-control list (ACL) to a file's security descriptor. An empty ACL implicitly denies all access to the file.

PSECURITY_DESCRIPTOR pSD;

PACL pACL;

DWORD cbACL = 1024;

/* Initialize a security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,

SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */

if (pSD == NULL) {

ErrorHandler("LocalAlloc");

return FALSE;

}

if (!InitializeSecurityDescriptor(pSD,

SECURITY_DESCRIPTOR_REVISION)) { /* defined in WINNT.H */

ErrorHandler("InitializeSecurityDescriptor");

goto Cleanup;

}

/* Initialize a DACL. */

pACL = (PACL) LocalAlloc(LPTR, cbACL);

if (pACL == NULL) {

ErrorHandler("LocalAlloc");

goto Cleanup;

}

if (!InitializeAcl(pACL, cbACL, ACL_REVISION2)) {

ErrorHandler("InitializeAcl");

goto Cleanup;

}

/* Add an empty ACL to the SD to deny access. */

if (!SetSecurityDescriptorDacl(pSD,

TRUE, /* fDaclPresent flag */

pACL,

FALSE)) { /* not a default DACL */

ErrorHandler("SetSecurityDescriptorDacl");

goto Cleanup;

}

/* Use the new SD as the file's security info. */

if (!SetFileSecurity(lpszTestFile,

DACL_SECURITY_INFORMATION,

pSD)) {

ErrorHandler("SetFileSecurity");

goto Cleanup;

}

Cleanup:

if(pSD != NULL)

LocalFree((HLOCAL) pSD);

if(pACL != NULL)

LocalFree((HLOCAL) pACL);

+$#A Allowing Access

The following example demonstrates adding a NULL discretionary ACL to a file's security descriptor. A NULL ACL implicitly allows all access to the file.

Note: It is fine to write code like this that builds security descriptors from scratch. It is, however, a good practice for people who write code that builds or manipulates security descriptors to first write code that explores the default security descriptors that Windows NT places on objects. For example, if Windows NT by default includes in a DACL an ACE granting the Local Logon SID certain access, it's good to know that, so that a decision not to grant any access to the Local Logon SID would be a conscious decision.

PSECURITY_DESCRIPTOR pSD;

/* Initialize a security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,

SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */

if (pSD == NULL) {

ErrorHandler("LocalAlloc");

return FALSE;

}

if (!InitializeSecurityDescriptor(pSD,

SECURITY_DESCRIPTOR_REVISION)) { /* defined in WINNT.H */

ErrorHandler("InitializeSecurityDescriptor");

goto Cleanup;

}

/* Add a NULL disc. ACL to the security descriptor. */

if (!SetSecurityDescriptorDacl(pSD,

TRUE, /* specifying a disc. ACL */

(PACL) NULL,

FALSE)) { /* not a default disc. ACL */

ErrorHandler("SetSecurityDescriptorDacl");

goto Cleanup;

}

/* Add the security descriptor to the file. */

if (!SetFileSecurity(lpszTestFile,

DACL_SECURITY_INFORMATION,

pSD)) {

ErrorHandler("SetFileSecurity");

goto Cleanup;

}

Cleanup:

if(pSD != NULL)

LocalFree((HLOCAL) pSD);

Rather than allowing all access by using a NULL ACL, applications generally allow access to an object by adding access-control entries (ACEs) to an ACL. The following example demonstrates finding a security identifier (SID) for a particular account and adding an access-allowed ACE for that account.

PSECURITY_DESCRIPTOR pSD;

PACL pACLNew;

DWORD cbACL = 1024;

PSID pSID;

DWORD cbSID = 1024;

LPSTR lpszAccount = "UserABC";

LPSTR lpszDomain;

DWORD cchDomainName = 80;

PSID_NAME_USE psnuType;

/* Initialize a new security descriptor. */

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,

SECURITY_DESCRIPTOR_MIN_LENGTH); /* defined in WINNT.H */

if (pSD == NULL) {

ErrorHandler("LocalAlloc");

return FALSE;

}

if (!InitializeSecurityDescriptor(pSD,

SECURITY_DESCRIPTOR_REVISION)) {

ErrorHandler("InitializeSecurityDescriptor");

goto Cleanup;

}

/* Initialize a new ACL. */

pACLNew = (PACL) LocalAlloc(LPTR, cbACL);

if (pACLNew == NULL) {

ErrorHandler("LocalAlloc");

goto Cleanup;

}

if (!InitializeAcl(pACLNew, cbACL, ACL_REVISION2)) {

ErrorHandler("InitializeAcl");

goto Cleanup;

}

/* Retrieve the SID for UserABC. */

pSID = (PSID) LocalAlloc(LPTR, cbSID);

psnuType = (PSID_NAME_USE) LocalAlloc(LPTR, 1024);

lpszDomain = (LPSTR) LocalAlloc(LPTR, cchDomainName);

if (pSID == NULL || psnuType == NULL ||

lpszDomain == NULL) {

ErrorHandler("LocalAlloc");

goto Cleanup;

}

if (!LookupAccountName((LPSTR) NULL, /* local name */

lpszAccount,

pSID,

&cbSID,

lpszDomain,

&cchDomainName,

psnuType)) {

ErrorHandler("LookupAccountName");

goto Cleanup;

}

if (!IsValidSid(pSID))

TextOut(hdc, 10, yIncrement += 18,

"SID is not valid.", 17);

else

TextOut(hdc, 10, yIncrement += 18,

"SID is valid.", 13);

/* Allow read but not write access to the file. */

if (!AddAccessAllowedAce(pACLNew,

ACL_REVISION2,

GENERIC_READ,

pSID)) {

ErrorHandler("AddAccessAllowedAce");

goto Cleanup;

}

/* Add a new ACL to the security descriptor. */

if (!SetSecurityDescriptorDacl(pSD,

TRUE, /* fDaclPresent flag */

pACLNew,

FALSE)) { /* not a default disc. ACL */

ErrorHandler("SetSecurityDescriptorDacl");

goto Cleanup;

}

/* Apply the new security descriptor to the file. */

if (!SetFileSecurity("d:\\ntfs_sample_file",

DACL_SECURITY_INFORMATION,

pSD)) {

ErrorHandler("SetFileSecurity");

goto Cleanup;

}

TextOut(hdc, 10, yIncrement += 18,

"Successfully added access-allowed ACE to file's DACL.", 53);

Cleanup:

FreeSid(pSID);

if(pSD != NULL)

LocalFree((HLOCAL) pSD);

if(pACLNew != NULL)

LocalFree((HLOCAL) pACLNew);

if(psnuType != NULL)

LocalFree((HLOCAL) psnuType);

if(lpszDomain != NULL)

LocalFree((HLOCAL) lpszDomain);

+$#A Setting Privileges

The following example removes the discretionary ACL from a file. If necessary, ownership of the file is given to the Administrator account. If required, the SE_TAKE_OWNERSHIP_NAME privilege is enabled for the account.

LPSTR lpszOwnFile = "d:\\ntfs_sample_file";

PSID pSIDAliasAdmins = NULL;

static SID_IDENTIFIER_AUTHORITY

siaNTAuthority = SECURITY_NT_AUTHORITY;

BOOL FAR PASCAL TakeOwnership()

{

SECURITY_DESCRIPTOR sd;

/*

* Initialize a security descriptor and assign it a NULL

* discretionary ACL to allow unrestricted access.

* Assign the security descriptor to a file.

*/

if (!InitializeSecurityDescriptor(&sd,

SECURITY_DESCRIPTOR_REVISION)) {

ErrorHandler("InitializeSecurityDescriptor");

return FALSE;

}

if (!SetSecurityDescriptorDacl(&sd,

TRUE,

(PACL) NULL,

FALSE)) {

ErrorHandler("SetSecurityDescriptorDacl");

return FALSE;

}

if (SetFileSecurity(lpszOwnFile,

DACL_SECURITY_INFORMATION,

&sd))

return TRUE;

else

ErrorHandler("SetFileSecurity1");

/*

* If the preceding call to SetFileSecurity fails, create

* an Administrator SID and use it to set the owner of

* the security descriptor.

*/

if (!AllocateAndInitializeSid(&siaNTAuthority,

2, /* subauthority count */

/* first subauthority */

SECURITY_BUILTIN_DOMAIN_RID,

/* second subauthority */

DOMAIN_ALIAS_RID_ADMINS,

0, 0, 0, 0, 0, 0,

&pSIDAliasAdmins)) {

ErrorHandler("AllocateAndInitializeSid");

return FALSE;

}

if (!SetSecurityDescriptorOwner(&sd,

pSIDAliasAdmins,

FALSE)) {

ErrorHandler("SetSecurityDescriptorOwner");

FreeSid(pSIDAliasAdmins);

return FALSE;

}

/*

* If the following call to SetFileSecurity fails,

* enable SE_TAKE_OWNERSHIP_NAME in the access token for

* the current process and try again.

*/

if (!SetFileSecurity(lpszOwnFile,

OWNER_SECURITY_INFORMATION,

&sd)) {

ErrorHandler("SetFileSecurity2");

if (!AssertTakeOwnership(TRUE)) { /* local */

MessageBox(NULL, "Must be logged on as Administrator",

"AssertTakeOwnership", MB_OK);

FreeSid(pSIDAliasAdmins);

return FALSE;

}

if (!SetFileSecurity(lpszOwnFile,

OWNER_SECURITY_INFORMATION,

&sd)) {

MessageBox(NULL, "Must be logged on as Administrator",

"SetFileSecurity", MB_OK);

FreeSid(pSIDAliasAdmins);

return FALSE;

}

}

/*

* The Administrator is now the owner of the file.

* Try again to assign a NULL ACL.

*/

if (SetFileSecurity(lpszOwnFile,

DACL_SECURITY_INFORMATION,

&sd)) {

MessageBox(NULL, "Added NULL DACL; protection removed",

"SetFileSecurity", MB_OK);

AssertTakeOwnership(FALSE);

return TRUE;

}

else {

AssertTakeOwnership(FALSE);

ErrorHandler("SetFileSecurity3");

return FALSE;

}

}

BOOL FAR PASCAL AssertTakeOwnership(BOOL fEnable)

{

HANDLE hToken;

LUID TakeOwnershipValue;

TOKEN_PRIVILEGES tkp;

/* Retrieve a handle of the access token. */

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,

&hToken)) {

ErrorHandler("OpenProcessToken");

return FALSE;

}

/*

* Enable the SE_TAKE_OWNERSHIP_NAME privilege or

* disable all privileges, depending on the fEnable

* flag.

*/

if(fEnable) {

if (!LookupPrivilegeValue((LPSTR) NULL,

SE_TAKE_OWNERSHIP_NAME,

&TakeOwnershipValue)) {

ErrorHandler("LookupPrivilegeValue");

return FALSE;

}

tkp.PrivilegeCount = 1;

tkp.Privileges[0].Luid = TakeOwnershipValue;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

AdjustTokenPrivileges(hToken,

FALSE,

&tkp,

sizeof(TOKEN_PRIVILEGES),

(PTOKEN_PRIVILEGES) NULL,

(PDWORD) NULL);

/*

* The return value of AdjustTokenPrivileges cannot

* be tested.

*/

if (GetLastError() != ERROR_SUCCESS) {

ErrorHandler("AdjustTokenPrivileges");

return FALSE;

}

}

else {

AdjustTokenPrivileges(hToken,

TRUE, /* disable all privileges */

(PTOKEN_PRIVILEGES) NULL,

(DWORD) 0,

(PTOKEN_PRIVILEGES) NULL,

(PDWORD) NULL);

/*

* The return value of AdjustTokenPrivileges cannot

* be tested.

*/

if (GetLastError() != ERROR_SUCCESS) {

ErrorHandler("AdjustTokenPrivileges");

return FALSE;

}

}

return TRUE;

}

+$#A Security Editor

Both File Manager and the Microsoft Windows NT Registry Editor (REGEDT32.EXE) include a security editor that allows a user with the appropriate privileges and access rights to change the security attributes of files and registry keys. This security editor imposes guidelines on the form of the security information. Applications that modify the security of files, directories, or registry keys can follow these guidelines so the security editor will function correctly after the modifications.

The security editor accepts discretionary ACLs having either zero or more ACCESS_DENIED_ACE access-control entries (ACEs) or zero or more ACCESS_ALLOWED_ACE ACEs. The two ACE types cannot be mixed in a discretionary ACL. Neither does the security editor accept ACCESS_DENIED_ACE ACEs that deny only partial access. For example, it would reject an ACE that denies only read access to a file.

The security editor also imposes a requirement on inheritance flags. These flags are ignored for noncontainer objects, such as files, but are important for container objects, such as directories and registry keys. For container objects that support permissions on objects in the container, make sure that each security identifier (SID) has an ACE inherited by objects and an ACE inherited by containers. Accordingly, the discretionary ACL should contain ACEs in which the AceFlags member of the ACE_HEADER structure structure contains the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags. Sometimes these flags can be combined in a single ACE. For example, a discretionary ACL that grants read access to a directory can contain a CONTAINER_INHERIT_ACE ACE that grants GENERIC_READ access to the user or group as well as an ACE with the OBJECT_INHERIT_ACE and INHERIT_ONLY_ACE flags that also grants GENERIC_READ access. In this case, the required flags could be combined into an ACE that grants GENERIC_READ access and combines the CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE flags.

The same rules for inheritance flags apply to system ACLs. The security editor does not support the SYSTEM_ALARM_ACE type and will not edit a system ACL containing one. As noted earlier, this ACE type is not supported by the current version of Windows NT.

+$#A Designing a Protected Server

The designer of a server application that assigns security to private object types must make many decisions about the design of the security interface, such as those in the following list:

• Define object.

• Determine the types of objects and the access types required, including whether to support SYNCHRONIZE.

• Determine whether an object is a container or noncontainer, as well as the appropriate inheritance flags. For more information about container objects, see Networks.

• Define appropriate access types and generic mappings.

• Determine impersonation characteristics.

• Develop security descriptor for the initial object and for new objects assembled from the parent's security descriptor and the client's access token.

• Consider string- versus handle-based object-manipulation functions.

• Resolve potential conflicts in access rights. For example, decide what is to be done if an administrator and a user both have delete access to an object.

For example, the designer of a protected print server might choose to define a server object, a printer object, and a document object. The three access rights in the following table are likely to be required:

Access right | Description
Administrate | Allows the holder to add objects to and delete them from another object. This right applies to the server, the printers, and the documents.
Enumerate | Allows the holder to list a container object's contents. This right applies to the server and to the printers.
Use | Allow the holder to add items to a print queue. This right applies to the printers.

+$#A Security Functions

Following are the functions used to control security.

AccessCheck
AccessCheckAndAuditAlarm
AddAccessAllowedAce
AddAccessDeniedAce
AddAce
AddAuditAccessAce
AdjustTokenGroups
AdjustTokenPrivileges
AllocateAndInitializeSid
AllocateLocallyUniqueId
AreAllAccessesGranted
AreAnyAccessesGranted
CopySid
CreatePrivateObjectSecurity
DdeImpersonateClient
DeleteAce
DestroyPrivateObjectSecurity
DuplicateToken
EqualPrefixSid
EqualSid
FindFirstFreeAce
FreeSid
GetAce
GetAclInformation
GetFileSecurity
GetKernelObjectSecurity
GetLengthSid
GetPrivateObjectSecurity
GetProcessWindowStation
GetSecurityDescriptorControl
GetSecurityDescriptorDacl
GetSecurityDescriptorGroup
GetSecurityDescriptorLength
GetSecurityDescriptorOwner
GetSecurityDescriptorSacl
GetSidIdentifierAuthority
GetSidLengthRequired
GetSidSubAuthority
GetSidSubAuthorityCount
GetTokenInformation
GetUserObjectSecurity
ImpersonateNamedPipeClient
ImpersonateSelf
InitializeAcl
InitializeSecurityDescriptor
InitializeSid
IsValidAcl
IsValidSecurityDescriptor
IsValidSid
LookupAccountName
LookupAccountSid
LookupPrivilegeDisplayName
LookupPrivilegeName
LookupPrivilegeValue
MakeAbsoluteSD
MakeSelfRelativeSD
MapGenericMask
ObjectCloseAuditAlarm
ObjectOpenAuditAlarm
ObjectPrivilegeAuditAlarm
OpenProcessToken
OpenThreadToken
PrivilegeCheck
PrivilegedServiceAuditAlarm
RevertToSelf
SetAclInformation
SetFileSecurity
SetKernelObjectSecurity
SetPrivateObjectSecurity
SetSecurityDescriptorDacl
SetSecurityDescriptorGroup
SetSecurityDescriptorOwner
SetSecurityDescriptorSacl
SetThreadToken
SetTokenInformation
SetUserObjectSecurity

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


Services.rtf

+$#AK Services

In the Microsoft® Win32® application programming interface (API), a service is an executable object that is installed in a registry database maintained by the service control manager.

+$#A About Services

The services database includes information that determines whether each installed service is started on demand or is started automatically when the system starts. The database can also contain logon and security information for a service so that a service can run even though no user is logged on. It also enables system administrators to customize security requirements for each service and thereby control access to the service. No more than one instance of a service can be running at a time.

Services can be divided into these two groups: Win32 services that conform to the interface rules of the service control manager, and driver services that conform to the device driver protocols for Microsoft Windows NT™.

+$#A Service Control Manager

A service control manager provides a unified and secure means of controlling services. Each machine that is running Windows NT has a service control manager process started by the system at system startup. The service control manager is a remote procedure call (RPC) server. The functions in the Win32 API that access a service control manager are provided through an RPC server so that service configuration and service control processes can manipulate services on remote machines.

The Win32 API includes a set of functions that provide a programmer's interface for the following tasks performed by the service control manager:

• Using the registry to maintain a database of installed service objects.

• Starting services either when the system is starting up or upon demand.

• Maintaining status information on running services.

• Transmitting control requests to running services.

Three types of programs use these functions:

Type | Description
Win32 service process |
 | A process that provides executable code for one or more services. Win32 services use functions that connect to the service control manager and send status information to it. Driver services do not use these functions.
Service configuration program |
 | A program that queries or modifies a service control manager database. Service configuration programs use functions that open a database, install or delete services in a database, and query or modify the configuration and security parameters of installed services. The functions are used to manipulate Win32 services and driver services.
Service control program |
 | A program that starts a service and controls a running service. Service control programs use functions that send requests to the service control manager, which starts the requested service or sends the control request to the specified service.

+$#A Database of Installed Services

The service control manager maintains a ServicesActive database in the registry. This is the currently active database that was used to start the system. The following is the registry path to this database.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

In this directory, there is a ServiceName key for each installed Win32 service or driver service. The ServiceName key is the name of the service specified by the CreateService function when the service was installed.

The database includes the following information about each installed service:

• The type of service. For Win32 services, the service type indicates whether the service executes in its own process or shares a process with other Win32 services. For driver services, it indicates whether the service is a kernel driver or a file system driver.

• The start type of the service. The start type indicates whether the service is started automatically at system startup or whether the service control manager starts it when requested by a service control process. The start type can also indicate whether the service is disabled, in which case it cannot be started.

• A fully qualified path of the service's executable file. The filename extension is .EXE for Win32 services and .SYS for driver services.

• Optional information used by the service control manager to determine the proper order for starting services. This information can include a list of services that must be running before the service can start, the name of a load ordering group that the service is part of, and a tag identifier that indicates the start order of the service in its load ordering group.

• For Win32 services, an optional logon account name in which the service process runs and an optional password for this account. If no account is specified, the service runs in the LocalSystem account.

• For driver services, an optional driver object name (for example, \FileSystem\Rdr or \Driver\Xns), used by the I/O system to load the device driver. If no name is specified, the I/O system creates a default name based on the service name.

+$#A Win32 Service Processes

A Win32 service process contains executable code for one or more services. To conform to the interface requirements of the service control manager, it must include:

• A main function that immediately calls the StartServiceCtrlDispatcher function to connect the main thread of the process to the service control manager.

• An entry point function, ServiceMain, for each service that can run in the process.

• A control handler function, Handler, for each service that can run in the process.

For simplicity, the discussions in this section use the term service to mean Win32 service. The information in this section does not apply to driver services.

+$#A Main Function

When the service control manager starts a service process, it waits for the process to call the StartServiceCtrlDispatcher function. If a service does not share a process with other services (SERVICE_WIN32_OWN_PROCESS services), the main thread of a service process should make this call immediately, leaving all initialization tasks to be performed by the ServiceMain function after the service starts. For services where multiple services share a process (SERVICE_WIN32_SHARE_PROCESS services) and where some common process-wide initialization needs to be done before any ServiceMain function is called, the main thread can do the work before calling StartServiceCtrlDispatcher, as long as it takes less than 30 seconds. Otherwise, another thread must be created to do the process-wide initialization, while the main thread calls StartServiceCtrlDispatcher. Any service specific initialization should still be done by the individual ServiceMain functions.

If StartServiceCtrlDispatcher succeeds, it connects the calling thread to the service control manager and does not return until all running services in the process have terminated. The service control manager uses this connection to send control and service start requests to the main thread of the service process. The main thread acts as a dispatcher by calling the appropriate Handler function to handle control requests or by creating a new thread to execute the appropriate ServiceMain function when a new service is started.

The StartServiceCtrlDispatcher function specifies a dispatch table containing an entry for each service that can run in the calling process. Each entry specifies the ServiceMain function for that service. For SERVICE_WIN32_SHARE_PROCESS services, each entry must contain the name of a service. This name is the service name that was specified by the CreateService function when the service was installed. For SERVICE_WIN32_OWN_PROCESS services, the service name in the table entry is ignored.

+$#A ServiceMain Function

The ServiceMain function is the entry point for a service. When a service control program calls the StartService function and the service process for this service is not yet running, the service control manager starts the process and sends a start request to the dispatcher in the service's main thread. The dispatcher creates a new thread to execute the ServiceMain function for the service.

A ServiceMain function should perform the following tasks:

1. Call the RegisterServiceCtrlHandler function immediately to register a Handler function to handle control requests for the service. The return value of RegisterServiceCtrlHandler is a SERVICE_STATUS_HANDLE type that identifies the service in calls to the SetServiceStatus function. A service calls SetServiceStatus to inform the service control manager of its status.

2. Perform initialization. If the execution time of the initialization code is expected to be very short (less than one second), initialization can be performed directly in ServiceMain.

If the initialization time is expected to be longer than one second, call SetServiceStatus, specifying SERVICE_START_PENDING as the current service state. As initialization continues, the service should make additional calls to SetServiceStatus to report progress. Sending multiple SetServiceStatus calls is useful for both service debugging and product support.

The following members of the SERVICE_STATUS structure passed in SetServiceStatus calls should change as the initialization progresses:

Member | Value
dwCurrentState | Set this value to SERVICE_START_PENDING during initialization. Set it to SERVICE_RUNNING when initialization is complete.
dwControlsAccepted | You can disable control codes during initialization and enable them when initialization is complete.
dwCheckPoint | Increment this value each time SetServiceStatus. is called. That informs the service control manager of the progress of a lengthy initialization.
dwWaitHint | This value is an estimate of how long it will take to complete the next initialization step. It should reflect a worst-case estimate of the amount of time, in milliseconds, that will elapse before the next pending call to SetServiceStatus. If this interval elapses and dwCheckPoint has not been incremented, the service control manager or service control program can assume that an error has occurred.

3. When initialization is complete, call SetServiceStatus to specify the current state as SERVICE_RUNNING. The ServiceMain function can then go on to perform the service tasks, or, if there are no pending tasks, it can return. Any change in the state of the service warrants a call to SetServiceStatus to report new status information. If the service stops due to an error, the service should use the dwServiceSpecificExitCode and dwWin32ExitCode members of the SERVICE_STATUS structure to identify the error.

+$#A The Control Handler Function

Each Win32 service has a control handler function, the Handler function, that is invoked by the main thread's dispatcher when the service process receives a control request. Control requests are generated by application programs that call the ControlService function. To register a Handler function, the service's ServiceMain function calls RegisterServiceCtrlHandler.

Whenever a service's Handler function is invoked, the service must call the SetServiceStatus function to report its status to the service control manager. This call must be done regardless of whether the status changed.

There is a set of standard control codes that the ControlService function can specify. All services must accept and process the SERVICE_CONTROL_INTERROGATE control code. Any of the other standard control codes can be enabled or disabled by calling SetServiceStatus. Services can also handle additional user-defined control codes, which must be in the range 128 to 255.

The standard control codes are listed below:

Control | Meaning
SERVICE_CONTROL_STOP |
 | Requests the service to stop.
SERVICE_CONTROL_PAUSE |
 | Requests the service to pause.
SERVICE_CONTROL_CONTINUE |
 | Requests the paused service to resume.
SERVICE_CONTROL_INTERROGATE |
 | Requests the service to immediately update its current status information to the service control manager.
SERVICE_CONTROL_SHUTDOWN |
 | Requests the service to perform cleanup tasks, because the system is shutting down. Due to the extremely limited time available for shutdown, this control code should be used only by services that must perform a specific cleanup operation that takes a minimal amount of time before the system shuts down. The Event Log and Server services are examples of services that handle SERVICE_CONTROL_SHUTDOWN. The Event Log service needs to clear a dirty bit in the files that it maintains. The Server service needs to shutdown to prevent future network connections to a system that is shut down.
 | To respond quickly to the SERVICE_CONTROL_SHUTDOWN code, the service may send a SERVICE_STOP_PENDING message and allow another thread to complete the shutdown operation. Service shutdown is complete when the SERVICE_STOPPED message is sent. The system gives the service control manager a limited amount of time (about 20 seconds) to complete service shutdown, after which time system shutdown proceeds regardless of whether service shutdown is complete. .

+$#A Service Configuration Programs

Service configuration programs are used by programmers or system administrators to query or modify a service control manager database. These databases are maintained in the registry, so they can also be accessed by using the registry functions or a registry editor. It is, however, safer to use the service control manager's configuration functions, which ensure that the service is installed and configured properly.

Service control manager databases and services are implemented as objects that should only be accessed through the service control manager's configuration functions. A database of installed services is represented by an SCManager object, and an installed service is represented by a service object. An SCManager object is a container object that holds service objects.

The handle type of both SCManager objects and service objects is SC_HANDLE. The OpenSCManager function returns a handle to an SCManager object on a specified machine. The CreateService and OpenService functions return a handle to a service object installed in a specified SCManager object. The CloseServiceHandle function closes SC_HANDLE to either an SCManager object or a service object.

The functions that return SC_HANDLE can request different types of access to the SCManager or service object. The requested access is granted or denied depending on the access token of the calling process and the security descriptor associated with the SCManager or service object.

A configuration program uses the CreateService function to install a new service in an service control manager database. This function specifies the name of the service and provides the service's configuration information that is stored in the database. For a description of the information stored in the database for each service, see Database of Installed Services.The DeleteService function removes an installed service from its database.

The following functions are available to determine the current configuration of a service control manager database:

Function | Description
QueryServiceConfig |
 | Fills in a buffer with a QUERY_SERVICE_CONFIG structure, containing the current configuration information for a specified service object.
QueryServiceObjectSecurity |
 | Fills in a buffer with the SECURITY_DESCRIPTOR structure of a specified SCManager or service object.
EnumDependentServices |
 | Fills in a buffer with an array of ENUM_SERVICE_STATUS structures, containing the name and current status of all services that are dependent on a specified service object.
EnumServicesStatus |
 | Fills in a buffer with an array of ENUM_SERVICE_STATUS structures, containing the name and current status of all services in a specified SCManager object.

To modify the configuration information for a service object, a configuration program uses the ChangeServiceConfig function. To modify the security descriptor for either an SCManager object or a service object, a configuration program uses the SetServiceObjectSecurity function.

Before using either of these functions to reconfigure a service object, a program should use the LockServiceDatabase function. This function tries to acquire a lock on the specified service control manager database and, if successful, prevents the service control manager from starting a service while the database is being reconfigured. A lock should also be acquired before using the registry functions to reconfigure a service object. Note, however, that failure to acquire a lock does not prevent a configuration program from successfully reconfiguring a service object. The only effect of a lock is to prevent the service control manager from starting a service in the locked database. To release the lock on the database when the reconfiguration is complete, use the UnlockServiceDatabase function. To determine whether the database is locked, use the QueryServiceLockStatus function.

+$#A Service Control Programs

A service control program performs the following actions:

• Starts a Win32 service or a driver service, if the service's start type is SERVICE_DEMAND_START.

• Sends control requests to a running service.

• Queries the current status of a running service.

The program must have an open handle to any service object that it wants to start or control. To do this, it must perform these steps:

1. Use the OpenSCManager function to open a handle to a service control manager database on a specified machine.

2. Use the OpenService or CreateService function to open a handle to the desired service object.

+$#A Service Startup

To start a service, use the StartService function. For Win32 services, the calling process can use the parameters of this function to specify an array of arguments that are passed to the ServiceMain function of the service. This function fails if the service control manager database is locked. If this occurs, the service control program can wait a few seconds and call StartService again. It can check the current lock status of the database by calling the QueryServiceLockStatus function.

When starting a driver service, StartService returns after the device driver has completed its initialization.

When starting a Win32 service, StartService returns after the ServiceMain function thread for the new service has been created. During its initialization, a service specifies a SERVICE_STATUS structure in calls to the SetServiceStatus function to report its progress to the service control manager. The service control program can retrieve this status information by calling the QueryServiceStatus function. During initialization, the dwCurrentState member of the SERVICE_STATUS structure should be SERVICE_START_PENDING. The service should increment the dwCheckPoint member at regular intervals during initialization. The dwWaitHint member is a time interval, in milliseconds, that indicates how long the service control program should wait before calling QueryServiceStatus again. When the initialization is complete, the service changes the dwCurrentState member to SERVICE_RUNNING.

+$#A Service Control Requests

To send control requests to a running service, use the ControlService function. This function specifies a control value that is passed to the Handler function of the specified service. This control value can be a user-defined code, or it can be one of the standard codes that enable the calling program to perform the following actions:

• Stops or pauses a service.

• Resumes a paused service.

• Gets updated status information from a service.

Each service specifies the control values that it will accept and process. To determine which of the standard control values are accepted by a service, use the QueryServiceStatus function or specify the SERVICE_CONTROL_INTERROGATE control value in a call to the ControlService function. The dwControlsAccepted member of the SERVICE_STATUS structure returned by these functions indicates whether the service can be stopped and whether it can be paused and resumed. All services accept the SERVICE_CONTROL_INTERROGATE control value, which requests the service to immediately report its current status.

Note, however, that the QueryServiceStatus function reports the service control manager's most recent status for a specified service, but does not get an updated status from the service itself. Using the SERVICE_CONTROL_INTERROGATE control value in a call to ControlService ensures that the status information returned is current.

+$#A Service Security

When a process uses the OpenSCManager function to open a handle to a service control manager database, the system performs a security check before granting the requested access. All processes are permitted SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, and SC_MANAGER_QUERY_LOCK_STATUS access to all service control manager databases. This access enables any process to open a service control manager database handle that it can use for the OpenService, EnumServicesStatus, and QueryServiceLockStatus functions. Only processes with Administrator privileges are able to open a database handle that can be used by the CreateService and LockServiceDatabase functions.

Security for service objects is provided by an access check performed when a process uses the OpenService function. The access permitted to different users depends on the SECURITY_DESCRIPTOR structure associated with the service object. The service control manager creates a service object's security descriptor when the service is installed by the CreateService function. You can use the QueryServiceObjectSecurity and SetServiceObjectSecurity functions to query and set the security descriptor of a service object. The default security descriptor of a service object permits the following access:

• All users have SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_ENUMERATE_DEPENDENTS, SERVICE_INTERROGATE, and SERVICE_USER_DEFINED_CONTROL access.

• Local users have SERVICE_START access in addition to those listed above.

• LocalSystem users and PowerUsers have SERVICE_START, SERVICE_PAUSE_CONTINUE, and SERVICE_STOP access in addition to those listed above.

• Administrators have SERVICE_ALL_ACCESS access.

+$#A Win32 Service User Accounts

Each Win32 service runs in a user account. The user name and password of an account are specified by the CreateService function at the time the service is installed. The user name and password can be changed by using the ChangeServiceConfig function. You can use the QueryServiceConfig function to get the user name (but not the password) of a service object.

When starting a service process, the service control manager logs on to the account associated with the service. If the log on is successful, the system produces an access token and attaches it to the new service process. This token identifies the service process in all subsequent interactions with securable objects (objects that have a security descriptor associated with them). For example, if the service tries to open a handle to a pipe, the system compares the service's access token to the pipe's security descriptor before granting access.

A SERVICE_WIN32_OWN_PROCESS service that runs in a process by itself can have an account from either the built-in (local), primary, or trusted domain. A SERVICE_WIN32_SHARE_PROCESS service that can share a process with multiple services must be installed to run in the LocalSystem account. This restriction exists because different services in the same process must run under the same security context.

The service control manager does not try to maintain the passwords of service user accounts. If a password is expired, the logon fails and the service fails to start. The system administrator who assigns accounts to services can create accounts with passwords that never expire. The administrator can also manage accounts with passwords that expire by using a service configuration program to periodically change the passwords.

If a service needs to recognize another service before sharing its information, the second service can either use the same account as the first service, or it can run in an account belonging to an alias that is recognized by the first service. Services that need to run in a distributed manner across the network should run in domain-wide accounts.

+$#A Interactive Services

An interactive service is a Win32 service that allows the user's desktop to interact with the service. Interactive services run in the LocalSystem account. Services are configured to be interactive when the dwServiceType parameter in a CreateService call is set to include the SERVICE_INTERACTIVE_PROCESS bit flag. SERVICE_INTERACTIVE_PROCESS may be used only with the SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS flags. This bit flag affects only the service process when it is created (that is, when the first service in the process is started).

The following key in the registry contains a value called NoInteractiveServices that controls the effect of the SERVICE_INTERACTIVE_PROCESS flag:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows

The NoInteractiveServices value defaults to 0, which means that services marked with the SERVICE_INTERACTIVE_PROCESS flag will be allowed to be interactive. When the NoInteractiveServices value is set to nonzero, any service started thereafter, regardless of whether it has been configured with SERVICE_INTERACTIVE_PROCESS, will not be allowed to run interactively. Setting NoInteractiveServices to nonzero completely guarantees a secure system.

+$#A Using Services

• Writing a Win32 service process's main function

• Writing a ServiceMain function

• Writing a control handler function

• Opening an SCManager database

• Installing a service

• Deleting a service

• Changing a service configuration

• Querying a service's configuration

• Starting a service

• Sending control requests to a service

+$#A Writing a Win32 Service Process's main Function

The main function of a Win32 service process calls the StartServiceCtrlDispatcher function to connect to the service control manager and start the control dispatcher thread. The dispatcher thread loops, waiting for incoming control requests for the services specified in the dispatch table. This thread does not return unless there is an error or all of the services in the process have terminated. When all services in a process have terminated, the service control manager sends a control request to the dispatcher thread telling it to shut down. The thread can then return from the StartServiceCtrlDispatcher call and the process can terminate.

The example provided in this section demonstrates a service process that supports only one service. Service processes with a single service call StartServiceCtrlDispatcher immediately because its entry-point function, ServiceMain, can perform all necessary initialization.

If your service process supports multiple services, the implementation of the main function may differ slightly. The names of the additional services are added to the dispatch table so they can be monitored by the dispatcher thread. Some multiple service processes may need to perform process-wide initialization tasks before starting any of the services with the StartServiceCtrlDispatcher call. If this is the case and the initialization is brief (less than 30 seconds), the main thread can take care of it before calling StartServiceCtrlDispatcher. If this initialization typically lasts longer than 30 seconds, one of the following strategies can be used:

• Call CreateThread to create a thread to perform the process-wide initialization.

• Use a global flag that each service checks during its initialization so that the first service to start up performs the tasks that are process-wide.

The following example calls a local function, SvcDebugOut, to print informational messages and errors to the debugger, and a Win32 function, GetLastError, to retrieve a detailed error code when RegisterServiceCtrlHandler fails. SvcDebugOut takes two parameters: a single string that can contain one formatted output character and a numeric value to be used as the formatted character.

/* Globals */

SERVICE_STATUS MyServiceStatus;

SERVICE_STATUS_HANDLE MyServiceStatusHandle;

/* Function Prototypes */

VOID MyServiceStart (DWORD argc, LPTSTR *argv);

VOID MyServiceCtrlHandler (IN DWORD opcode);

DWORD MyServiceInitialization(DWORD argc, LPTSTR *argv,
DWORD *specificError);

VOID _CRTAPI1 main(void)

{

SERVICE_TABLE_ENTRY DispatchTable[] = {

{ TEXT("MyService"), MyServiceStart },

{ NULL, NULL }

};

if (!StartServiceCtrlDispatcher( DispatchTable)) {

SvcDebugOut(" [MY_SERVICE] StartServiceCtrlDispatcher error =
%d\n", GetLastError());

}

}

VOID SvcDebugOut(LPSTR String, DWORD Status)

{

CHAR Buffer[1024];

if (strlen(String) < 1000) {

sprintf(Buffer, String, Status);

OutputDebugStringA(Buffer);

}

}

+$#A Writing a ServiceMain Function

The MyServiceStart function in the following example is the service's entry point. When the control dispatcher is told to start a service, it creates a thread that will begin executing at this point. MyServiceStart has access to command-line arguments in the same manner as does the typical main function. The first parameter contains the number of arguments being passed to the service. There will always be at least one argument. The second parameter is a pointer to an array of string pointers. The first item in the array always points to the service name.

MyServiceStart first fills in the SERVICE_STATUS structure including the control codes that it will accept. Note that although this service accepts PAUSE_CONTINUE, it does nothing significant when told to pause. PAUSE_CONTINUE was included for illustration purposes only; if pausing does not add value to your service, do not support it. The dwCheckPoint, dwWaitHint, and the exit code fields are all initialized to zero.

MyServiceStart then calls the RegisterServiceCtrlHandler function to register MyService as the service's Handler function and begin initialization. The following sample initialization function, MyServiceInitialization, is included for illustration purposes; it does not perform any initialization tasks such as creating additional threads. If your service's initialization performs tasks that are expected to take longer than one second, your code must call SetServiceStatus periodically to send out wait hints and check points indicating that progress is being made.

When initialization has completed successfully, a call to SetServiceStatus with a status of SERVICE_RUNNING is made and the service can continue with its work. If an error has occurred in initialization, MyServiceStart reports SERVICE_STOPPED with the SetServiceStatus call and returns.

Since this sample service does not do anything, MyServiceStart simply returns control to the caller. However, a real service should use this thread to do whatever work it was designed to do. The ServiceMain function for a service that does not need a thread to do its work, such as a service that processes only RPC requests, should return control to the caller. It is important to return, rather than call the ExitThread function, because returning allows for cleanup of the memory allocated for the arguments.

To output debugging information, MyServiceStart calls SvcDebugOut. The source code for SvcDebugOut is listed in Writing a Win32 Service Process's main Function.

void MyServiceStart (DWORD argc, LPTSTR *argv)

{

DWORD status;

DWORD specificError;

MyServiceStatus.dwServiceType = SERVICE_WIN32;

MyServiceStatus.dwCurrentState = SERVICE_START_PENDING;

MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |

SERVICE_ACCEPT_PAUSE_CONTINUE;

MyServiceStatus.dwWin32ExitCode = 0;

MyServiceStatus.dwServiceSpecificExitCode = 0;

MyServiceStatus.dwCheckPoint = 0;

MyServiceStatus.dwWaitHint = 0;

MyServiceStatusHandle = RegisterServiceCtrlHandler(

TEXT("MyService"),

MyServiceCtrlHandler);

if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {

SvcDebugOut(" [MY_SERVICE] RegisterServiceCtrlHandler
failed %d\n", GetLastError());

return;

}

// Initialization code goes here.

status = MyServiceInitialization(argc,argv, &specificError);

// Handle error condition

if (status != NO_ERROR) {

MyServiceStatus.dwCurrentState = SERVICE_STOPPED;

MyServiceStatus.dwCheckPoint = 0;

MyServiceStatus.dwWaitHint = 0;

MyServiceStatus.dwWin32ExitCode = status;

MyServiceStatus.dwServiceSpecificExitCode = specificError;

SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);

return;

}

// Initialization complete - report running status

MyServiceStatus.dwCurrentState = SERVICE_RUNNING;

MyServiceStatus.dwCheckPoint = 0;

MyServiceStatus.dwWaitHint = 0;

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {

status = GetLastError();

SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);

}

// This is where the service does its work. //

SvcDebugOut(" [MY_SERVICE] Returning the Main Thread \n",0);

return;

}

/* stub initialization function */

DWORD MyServiceInitialization(DWORD argc, LPTSTR *argv,
DWORD *specificError)

{

argv;

argc;

specificError;

return(0);

}

+$#A Writing a Control Handler Function

The MyServiceCtrlHandler function in the following example is the service's Handler function. When this function is called by the dispatcher thread, it handles the control code passed in the parameter Opcode and then calls the SetServiceStatus function to update the service's status. Every time a Handler function receives a control code, it is appropriate to return status with a call to SetServiceStatus regardless of whether the service operates on the control.

The Handler function executes in the context of the dispatcher thread. Therefore, this function should not perform time-consuming operations. If an operation such as a stop request is going to take a long time, the Handler function should send the SERVICE_STOP_PENDING message in a call to SetServiceStatus and then signal the other service thread(s), if necessary, that a stop operation is in progress. Then the function should return so that the dispatcher can service more requests. One of the other service threads becomes responsible for sending further wait hints and the final SERVICE_STOPPED message.

Note that when the pause control is received, MyServiceCtrlHandler simply sets the dwCurrentState field in the SERVICE_STATUS structure to SERVICE_PAUSED. Likewise, when the continue control is received, the state is set to SERVICE_RUNNING. Note that MyServiceCtrlHandler is not presented as an accurate example of how to handle the pause and continue controls. Since MyServiceCtrlHandler is a template for a Handler function, code for the pause and continue controls is included for completeness. A real service that supports either the pause or continue control should handle these controls in whatever way makes sense for that service. Many services support neither the pause or continue control. If the service indicates that it does not support pause or continue with the dwControlsAccepted parameter, then the service control manager will not send pause or continue controls to the service's Handler function.

When handling the stop request for a service that will take a long time, the current thread should create another thread or ask an existing one to handle the stop process. The current thread should simply call SetServiceStatus with the SERVICE_STOP_PENDING message and return to the caller.

To output debugging information, MyServiceCtrlHandler calls SvcDebugOut. The source code SvcDebugOut is listed in Writing a Win32 Service Process's main Function.

VOID MyServiceCtrlHandler ( IN DWORD Opcode)

{

DWORD status;

switch(Opcode) {

case SERVICE_CONTROL_PAUSE:

/* Do whatever it takes to pause here.

MyServiceStatus.dwCurrentState = SERVICE_PAUSED;

break;

case SERVICE_CONTROL_CONTINUE:

/* Do whatever it takes to continue here.

MyServiceStatus.dwCurrentState = SERVICE_RUNNING;

break;

case SERVICE_CONTROL_STOP:

/* Do whatever it takes to stop here.

MyServiceStatus.dwWin32ExitCode = 0;

MyServiceStatus.dwCurrentState = SERVICE_STOPPED;

MyServiceStatus.dwCheckPoint = 0;

MyServiceStatus.dwWaitHint = 0;

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus))
{

status = GetLastError();

SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);

}

SvcDebugOut(" [MY_SERVICE] Leaving MyService \n",0);

return;

case SERVICE_CONTROL_INTERROGATE:

/* fall through to send current status

break;

default:

SvcDebugOut(" [MY_SERVICE] Unrecognized opcode %ld\n", Opcode);

}

/* Send current status.

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {

status = GetLastError();

SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);

}

return;

}

+$#A Opening an SCManager Database

The examples in the following sections require an open handle to an SCManager database object. Different operations on a service control manager database require different levels of access, and you should only request the minimum access required. If SC_MANAGER_ALL_ACCESS is requested, the OpenSCManager function fails if the calling process does not have administrator privileges. The following example shows how to request full access to the ServicesActive database on the local machine.

/* Open a handle to the SC Manager database. */

schSCManager = OpenSCManager(

NULL, /* local machine */

NULL, /* ServicesActive database */

SC_MANAGER_ALL_ACCESS); /* full access rights */

if (schSCManager == NULL)

MyErrorExit("OpenSCManager");

+$#A Installing a Service

A service configuration program uses the CreateService function to install a service in a service control manager database. The application-defined schSCManager handle must have SC_MANAGER_CREATE_SERVICE access to the SCManager object. The following example shows how to install a service.

VOID CreateSampleService() {

LPCTSTR lpszBinaryPathName =

TEXT("%SystemRoot%\\system\\testserv.exe");

schService = CreateService(

schSCManager, /* SCManager database */

TEXT("Sample_Srv"), /* name of service */

lpszDisplayName, /* service name to display */

SERVICE_ALL_ACCESS, /* desired access */

SERVICE_WIN32_OWN_PROCESS, /* service type */

SERVICE_DEMAND_START, /* start type */

SERVICE_ERROR_NORMAL, /* error control type */

lpszBinaryPathName, /* service's binary */

NULL, /* no load ordering group */

NULL, /* no tag identifier */

NULL, /* no dependencies */

NULL, /* LocalSystem account */

NULL); /* no password */

if (schService == NULL)

MyErrorExit("CreateService");

else

printf("CreateService SUCCESS\n");

CloseServiceHandle(schService);

}

+$#A Deleting a Service

In the following example, a service configuration program uses the OpenService function to get a handle with DELETE access to an installed service object. The program then uses the service object handle in the DeleteService function to remove the service from the service control manager database.

VOID DeleteSampleService() {

schService = OpenService(

schSCManager, /* SCManager database */

TEXT("Sample_Srv"), /* name of service */

DELETE); /* only need DELETE access */

if (schService == NULL)

MyErrorExit("OpenService");

if (! DeleteService(schService) )

MyErrorExit("DeleteService");

else

printf("DeleteService SUCCESS\n");

CloseServiceHandle(schService);

}

+$#A Changing a Service Configuration

In the following example, a service configuration program uses the ChangeServiceConfig function to change the configuration parameters of an installed service. The program first tries to lock the database, to prevent the service control manager from starting a service while it is being reconfigured. If it successfully locks the database, the program opens a handle to the service object, modifies its configuration, unlocks the database, and then closes the service object handle. If the program does not successfully in lock the database, it uses the QueryServiceLockStatus function to retrieve information about the lock.

VOID ReconfigureSampleService(BOOL fDisable) {

SC_LOCK sclLock;

LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;

DWORD dwBytesNeeded, dwStartType;

/* Need to acquire database lock before reconfiguring. */

sclLock = LockServiceDatabase(schSCManager);

/* If the database cannot be locked, report the details. */

if (sclLock == NULL) {

/* Exit if the database is not locked by another process. */

if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED)

MyErrorExit("LockServiceDatabase");

/* Allocate a buffer to get details about the lock. */

lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(

LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);

if (lpqslsBuf == NULL)

MyErrorExit("LocalAlloc");

/* Get and print the lock status information. */

if (! QueryServiceLockStatus(

schSCManager,

lpqslsBuf,

sizeof(QUERY_SERVICE_LOCK_STATUS)+256,

&dwBytesNeeded) )

MyErrorExit("QueryServiceLockStatus");

if (lpqslsBuf->fIsLocked)

printf("Locked by: %s, duration: %d seconds\n",

lpqslsBuf->lpLockOwner,

lpqslsBuf->dwLockDuration);

else

printf("No longer locked\n");

LocalFree(lpqslsBuf);

MyErrorExit("Could not lock database");

}

/* The database is locked, so it is safe to make changes. */

/* Open a handle to the service. */

schService = OpenService(

schSCManager, /* SCManager database */

TEXT("Sample_Srv"), /* name of service */

SERVICE_CHANGE_CONFIG); /* need CHANGE access */

if (schService == NULL)

MyErrorExit("OpenService");

dwStartType = (fDisable) ? SERVICE_DISABLED :

SERVICE_DEMAND_START;

if (! ChangeServiceConfig(

schService, /* handle of service */

SERVICE_NO_CHANGE, /* service type: no change */

dwStartType, /* *change* service start type */

SERVICE_NO_CHANGE, /* error control: no change */

NULL, /* binary path: no change */

NULL, /* load order group: no change */

NULL, /* tag ID: no change */

NULL, /* dependencies: no change */

NULL, /* account name: no change */

NULL) ) /* password: no change */

MyErrorExit("ChangeServiceConfig");

else

printf("ChangeServiceConfig SUCCESS\n");

/* Release the database lock. */

UnlockServiceDatabase(sclLock);

/* Close the handle to the service. */

CloseServiceHandle(schService);

}

+$#A Querying a Service's Configuration

In the following example, a service configuration program uses the OpenService function to get a handle with SERVICE_QUERY_CONFIG access to an installed service object. Then the program uses the service object handle in the QueryServiceConfig function to retrieve the current configuration of the service.

VOID GetSampleServiceConfig() {

LPQUERY_SERVICE_CONFIG lpqscBuf;

DWORD dwBytesNeeded;

/* Open a handle to the service. */

schService = OpenService(

schSCManager, /* SCManager database */

TEXT("Sample_Srv"), /* name of service */

SERVICE_QUERY_CONFIG); /* need QUERY access */

if (schService == NULL)

MyErrorExit("OpenService");

/* Allocate a buffer for the information configuration. */

lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(

LPTR, 4096);

if (lpqscBuf == NULL)

MyErrorExit("LocalAlloc");

/* Get and print the information configuration. */

if (! QueryServiceConfig(

schService,

lpqscBuf,

4096,

&dwBytesNeeded) )

MyErrorExit("QueryServiceConfig");

printf("\nSample_Srv configuration: \n");

printf(" Type: 0x%x\n", lpqscBuf->dwServiceType);

printf(" Start Type: 0x%x\n", lpqscBuf->dwStartType);

printf(" Err Control: 0x%x\n", lpqscBuf->dwErrorControl);

printf(" Binary path: %s\n", lpqscBuf->lpBinaryPathName);

if (lpqscBuf->lpLoadOrderGroup != NULL)

printf(" Load order group: %s\n",

lpqscBuf->lpLoadOrderGroup);

if (lpqscBuf->dwTagId != 0)

printf(" Tag ID: %d\n", lpqscBuf->dwTagId);

if (lpqscBuf->lpDependencies != NULL)

printf(" Dependencies: %s\n", lpqscBuf->lpDependencies);

if (lpqscBuf->lpServiceStartName != NULL)

printf(" Start Name: %s\n",

lpqscBuf->lpServiceStartName);

LocalFree(lpqscBuf);

}

+$#A Starting a Service

To start a service, the following example opens a handle to an installed database and then specifies the handle in a call to the StartService function. StartService can be used to start either a Win32 service or a driver service, but this example assumes that a Win32 service is being started. After starting the service, the program uses the members of the SERVICE_STATUS structure returned by the QueryServiceStatus function to track the progress of the Win32 service.

VOID StartSampleService() {

SERVICE_STATUS ssStatus;

DWORD dwOldCheckPoint;

schService = OpenService(

schSCManager, /* S. C. Manager database */

TEXT("Sample_Srv"), /* name of service */

SERVICE_ALL_ACCESS);

// SERVICE_START | /* need START access */

// SERVICE_QUERY_CONFIG); /* and QUERY access */

if (schService == NULL)

MyErrorExit("OpenService");

if (! StartService(schService, /* handle of service */

0, /* number of arguments */

NULL) ) /* no arguments */

MyErrorExit("StartService");

else

printf("Service start pending\n");

/* Check the status until the service is running. */

if (! QueryServiceStatus(

schService, /* handle of service */

&ssStatus) ) /* address of status info */

MyErrorExit("QueryServiceStatus");

while (ssStatus.dwCurrentState != SERVICE_RUNNING) {

/* Save the current checkpoint. */

dwOldCheckPoint = ssStatus.dwCheckPoint;

/* Wait for the specified interval. */

Sleep(ssStatus.dwWaitHint);

/* Check the status again. */

if (! QueryServiceStatus(

schService, /* handle of service */

&ssStatus) ) /* address of status info */

break;

/* Break if the checkpoint has not been incremented. */

if (dwOldCheckPoint >= ssStatus.dwCheckPoint)

break;

}

if (ssStatus.dwCurrentState == SERVICE_RUNNING)

printf("StartService SUCCESS\n");

else {

printf("\nService not started: \n");

printf(" Current State: %d\n",

ssStatus.dwCurrentState);

printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);

printf(" Service Specific Exit Code: %d\n",

ssStatus.dwServiceSpecificExitCode);

printf(" Check Point: %d\n", ssStatus.dwCheckPoint);

printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);

}

CloseServiceHandle(schService);

}

+$#A Sending Control Requests to a Service

The following example uses the ControlService function to send a control value to a running service. Different control values require different levels of access to the service object. For example, a service object handle must have SERVICE_STOP access to send the SERVICE_CONTROL_STOP code. When ControlService returns, a SERVICE_STATUS structure contains the latest status information for the service.

VOID ControlSampleService(DWORD fdwControl) {

SERVICE_STATUS ssStatus;

DWORD fdwAccess;

/* The required service object access depends on the control. */

switch (fdwControl) {

case SERVICE_CONTROL_STOP:

fdwAccess = SERVICE_STOP;

break;

case SERVICE_CONTROL_PAUSE:

case SERVICE_CONTROL_CONTINUE:

fdwAccess = SERVICE_PAUSE_CONTINUE;

break;

case SERVICE_CONTROL_INTERROGATE:

fdwAccess = SERVICE_INTERROGATE;

break;

default:

fdwAccess = SERVICE_INTERROGATE;

}

/* Open a handle to the service. */

schService = OpenService(

schSCManager, /* SCManager database */

TEXT("Sample_Srv"), /* name of service */

fdwAccess); /* specify access */

if (schService == NULL)

MyErrorExit("OpenService");

/* Send a control value to the service. */

if (! ControlService(

schService, /* handle of service */

fdwControl, /* control value to send */

&ssStatus) ) /* address of status info */

MyErrorExit("ControlService");

/* Print the service status. */

printf("\nStatus of Sample_Srv: \n");

printf(" Service Type: 0x%x\n", ssStatus.dwServiceType);

printf(" Current State: 0x%x\n", ssStatus.dwCurrentState);

printf(" Controls Accepted: 0x%x\n",

ssStatus.dwControlsAccepted);

printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);

printf(" Service Specific Exit Code: %d\n",

ssStatus.dwServiceSpecificExitCode);

printf(" Check Point: %d\n", ssStatus.dwCheckPoint);

printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);

return;

}

+$#A Service Functions

Following are the Win32 functions used by services and by programs that control or configure services:

ChangeServiceConfig
CloseServiceHandle
ControlService
CreateService
DeleteService
EnumDependentServices
EnumServicesStatus
GetServiceDisplayName
GetServiceKeyName
Handler
LockServiceDatabase
NotifyBootConfigStatus
OpenSCManager
OpenService
QueryServiceConfig
QueryServiceLockStatus
QueryServiceObjectSecurity
QueryServiceStatus
RegisterServiceCtrlHandler
ServiceMain
SetServiceBits
SetServiceObjectSecurity
SetServiceStatus
StartService
StartServiceCtrlDispatcher
UnlockServiceDatabase

Créé avec HelpNDoc Personal Edition: Création d'aide CHM, PDF, DOC et HTML d'une même source


Shell Dynamic Data Exchange Interface.rtf

+$#AK Shell Dynamic Data Exchange Interface

This overview describes the dynamic data exchange (DDE) interface of Program Manager (PROGMAN.EXE). Program Manager is a shell ⎯ that is, an application that enables users to group, start, and otherwise control other applications for the Microsoft® Windows® operating system.

+$#A About Program Manager Initialization

Program Manager starts automatically when the user starts Windows and runs as long as Windows is in use. When it starts, Program Manager displays one or more windows within its main window. Each window contains icons that correspond to logically related Windows-based applications. For example, the Accessories window contains icons for Windows Write, Paintbrush, Notepad, and other accessory applications.

Program Manager stores initialization and configuration information in the registry. This information is stored under the Settings, Groups, and Restrictions keys in the following section of the registry:

HKEY_CURRENT_USER\Software\Microsoft\WindowsNT\
CurrentVersion\Program Manager

Additional information is stored in the Program Groups key in the following section of the registry:

HKEY_LOCAL_MACHINE\SOFTWARE

+$#A Settings Key

The Settings key contains information that Program Manager uses to configure its environment. The following entries appear in the Settings key.

Entry | Description
MinOnRun | Specifies whether to minimize Program Manager when an application is started.
AutoArrange | Specifies whether Program Manager should automatically arrange icons within groups.
SaveSettings | Specifies whether to save the position of the Program Manager window when Program Manager terminates.
DISPLAY.DRV | Specifies the filename of the display driver in use when Program Manager last terminated. When Program Manager starts, it compares this value to the filename of the display driver currently in use. If the names are different, Program Manager re-extracts the application icons.
Window | Specifies the location and dimensions of the Program Manager window.
Order | Specifies the order that the groups listed in the Groups key appear in the Program Manager window.
Startup | Specifies the name of the startup group. Program Manager automatically starts the applications in the startup group whenever it starts.

+$#A Groups Key

The windows that appear in the Program Manager window correspond to group files. From the user's perspective, a group file is a collection of icons that represent logically related applications, but from the programmer's perspective, a group file is actually a collection of data. This data includes the color information for the icons (AND and XOR masks), an offset to the resource header of each icon, the ideal resolution for displaying each icon, the name of the executable (.EXE) file that contains the application, and so on.

There are two types of groups: common and personal. A common group is shared among multiple users through network connections; a personal group is not shared. Program Manager stores information about personal groups under the Groups key. It stores information about common groups under the HKEY_LOCAL_MACHINE\SOFTWARE key.

The Groups key of the registry identifies the names of the group files that Program Manager displays as unique windows or icons. The groups must be numbered, but they need not be listed in any particular order. Program Manager never changes the number of an existing group.

+$#A Restrictions Key

The Restrictions key controls whether certain features of Program Manager are enabled or disabled. The following entries can appear in the Restrictions key.

Entry | Description
NoRun | Specifies whether to disable the Run command on the File menu. If this entry is set to 1, the Run command is disabled. If this entry is set to 0, the Run command is enabled. The default is 0 if no value is specified.
NoClose | Specifies whether to prevent the user from closing Program Manager through the File menu, the System menu, the ALT+F4 key combination, or the Task List. If this entry is set to 1, closing is prevented. If this entry is set to 0, closing is allowed. The default is 0 if no value is specified.
EditLevel | Controls the extent to which the user can modify read-write groups. (Shared read-only groups cannot be modified.) This entry can be set to one of the following values.

Value | Action
0 | Allows any modifications to the group. This is the default value.
1 | Prevents the user from creating, deleting, or renaming groups.
2 | Prevents the user from creating, deleting, or renaming groups and from creating or deleting items in a group.
3 | Prevents the user from creating, deleting, or renaming groups; from creating or deleting items in a group; and from changing command lines for items in a group.
4 | Prevents the user from changing any property of an item in a group; from creating, deleting, or renaming groups; from creating or deleting items in a group; and from changing command lines for items in a group.

NoFileMenu | Specifies whether to disable the File menu and all of its commands. If this entry is set to 1, the File menu is disabled. If this entry is set to 0, the menu is enabled. The default setting is 0 if no value is specified.
NoSaveSettings | Specifies whether to disable the Save Settings on Exit command on the Options menu. If this entry is set to 1, the Save Settings on Exit command is disabled. If this entry is set to 0, the command is enabled. The default setting is 0 if no value is specified.

Setting the value of NoRun to 1 and the value of EditLevel to 3 prevents a user from using Program Manager to run any applications not already in a program group.

+$#A Command-String Interface

Program Manager has a DDE command-string interface that allows other applications to create, display, delete, and reload groups; add items to groups; replace items in groups; delete items from groups; and close Program Manager. The following commands perform these actions:

AddItem | ExitProgman
CreateGroup | Reload
DeleteGroup | ReplaceItem
DeleteItem | ShowGroup

For example, the setup program for an application can use these commands to instruct Program Manager to install the application's icon in a group.

Multiple commands can be concatenated; each command must be contained in square brackets, and parameters must be contained in parentheses and separated by commas. Quotation marks must delimit arguments that contain spaces, brackets, or parentheses. For example, the following set of commands adds WINAPP.EXE to the Windows Applications group:

[CreateGroup("Windows Applications")]

[ShowGroup("My Group",1)]

[AddItem(winapp.exe,Win App,winapp.exe,2)]

To use these commands, an application must first initiate a DDE conversation with Program Manager. The application and topic names for the conversation are both PROGMAN. Then the application sends the WM_DDE_EXECUTE message, specifying the appropriate command and its parameters.

Note The user can configure Windows to use a shell other than Program Manager as the default. Therefore, do not design an application that assumes Program Manager will be available for a DDE conversation.

The following sections describe Program Manager DDE command strings in detail. In the syntax blocks in the following sections, brackets enclose optional parameters.

+$#A CreateGroup

The syntax for the CreateGroup command has this form:

CreateGroup(GroupName[,CommonGroupFlag])

The CreateGroup command instructs Program Manager to create a new group or activate the window of an existing group.

Following are the parameters for this command:

GroupName

Identifies the group to be created or activated. This parameter is a string. If a group already exists with the name specified by GroupName, CreateGroup activates the group window.

CommonGroupFlag

Specifies whether the new group is a common group or a personal group. If this parameter is 1, a common group is created; if it is 0, a personal group is created. To create a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails. The default value of this parameter is 1 (common group) if no value is specified and if the user has administrative privileges. The default value is 0 (personal group) if no value is specified and the user does not have administrative privileges.

Note If the second parameter of the CreateGroup command specifies the path of the group file (as was required in Windows version 3.1), the parameter is ignored.

+$#A ShowGroup

The syntax for the ShowGroup command has this form:

ShowGroup(GroupName,ShowCommand[,CommonGroupFlag])

The ShowGroup command instructs Program Manager to minimize, maximize, or restore the window of an existing group.

Following are the parameters for this command:

GroupName

Identifies the group window to be minimized, maximized, or restored.

ShowCommand

Specifies the action Program Manager is to perform on the group window. This parameter is an integer. It must have one of the following values.

Value | Action
1 | Activates and displays the group window. If the window is minimized or maximized, Windows restores it to its original size and position.
2 | Activates the group window and displays it as an icon.
3 | Activates the group window and displays it as a maximized window.
4 | Displays the group window in its most recent size and position. The window that is currently active remains active.
5 | Activates the group window and displays it in its current size and position.
6 | Minimizes the group window.
7 | Displays the group window as an icon. The currently active window remains active.
8 | Displays the group window in its current state. The currently active window remains active.

CommonGroupFlag

Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being minimized, maximized, or restored is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To minimize, maximize, or restore a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

+$#A DeleteGroup

The syntax for the DeleteGroup command has this form:

DeleteGroup(GroupName[,CommonGroupFlag])

The DeleteGroup command instructs Program Manager to delete an existing group.

Following are the parameters for this command:

GroupName

Identifies the group to be deleted.

CommonGroupFlag

Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being deleted is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To delete a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

Note If the second parameter of the DeleteGroup command specifies the path of the group file (as was required in Windows version 3.1), the parameter is ignored.

+$#A Reload

The syntax for the Reload command has this form:

Reload(GroupName[,CommonGroupFlag])

The ReloadGroup command instructs Program Manager to remove and reload an existing group. An application that modifies group files can use this command to cause Program Manager to update the groups when it has finished making modifications.

Following are the parameters for this command:

GroupName

Identifies the group to be removed and reloaded. If no value is specified for the GroupName parameter, Program Manager unloads all groups and reloads the groups specified in the Groups key of the registry. The Settings and Restrictions keys are not reread.

CommonGroupFlag

Specifies whether the group is a common group or a personal group. If this parameter is 1, the group being reloaded is a common group; if it is 0, the group is a personal group. This parameter defaults to 1 (common group) if no value is specified. To reload a common group, the user currently logged on must have administrative privileges. If this parameter is 1 but the user does not have administrative privileges, the command fails.

+$#A AddItem

The syntax for the AddItem command has this form:

AddItem(CmdLine[,
Name[,IconPath[,IconIndex[,xPos,yPos[,DefDir[,
HotKey[,fMinimize[fSeparateMemSpace] ] ] ] ] ] ])

The AddItem command instructs Program Manager to add an icon to an existing group. There is a limit of 50 items per group.

Following are the parameters for this command:

CmdLine

Specifies the full command line required to run the application. This parameter is a string. At a minimum, this string is the name of the executable file for the application. It can also include the full path of the application and any parameters required by the application.

Name

Specifies the title that is displayed below the icon in the group window.

IconPath

Identifies the filename for the icon to be displayed in the group window. This parameter is a string. This file can be either a Windows-based executable file or an icon file. If no value is specified for the IconPath parameter, Program Manager uses the first icon in the file specified by the CmdLine parameter if that file is an executable file. If CmdLine specifies an associated file, Program Manager uses the first icon of the associated executable file. The association is taken from the registry. (For more information about the registry, see Registry.) If CmdLine specifies neither an executable file nor an associated executable file, Program Manager uses a default icon.

IconIndex

Specifies the index of the icon in the file identified by the IconPath parameter. The IconIndex parameter is an integer. PROGMAN.EXE contains five built-in icons that can be used for programs not written for Windows.

xPos

Specifies the horizontal position of the icon in the group window. This parameter is an integer. Both the xPos and yPos parameters must be used to specify the position of the icon. If the position is not specified, Program Manager places the icon in the next available space.

yPos

Specifies the vertical position of the icon in the group window. This parameter is an integer. Both the xPos and yPos parameters must be used to specify the position of the icon. If the position is not specified, Program Manager places the icon in the next available space.

DefDir

Specifies the name of the default (or working) directory. This parameter is a string.

HotKey

Identifies a hot (or shortcut) key that is specified by the user.

fMinimize

Specifies whether an application window is to be minimized when first displayed.

fSeparateMemSpace

Specifies whether a 16-bit application runs in a separate memory space.

+$#A ReplaceItem

The syntax for the ReplaceItem command has this form:

ReplaceItem(ItemName)

The ReplaceItem command instructs Program Manager to delete an item and record the position of the deleted item. Program Manager adds a new item (specified by the next AddItem command) at this recorded position.

Following is the parameter for this command:

ItemName

Specifies the item to be deleted. Its position is recorded by Program Manager.

+$#A DeleteItem

The syntax for the DeleteItem command has this form:

DeleteItem(ItemName)

The DeleteItem command instructs Program Manager to delete an item from the currently active group.

Following is the parameter for this command:

ItemName

Specifies the item to be deleted from the currently active group.

+$#A ExitProgman

The syntax for the ExitProgman command has this form:

ExitProgman(bSaveGroups)

If Program Manager was started by another application, the ExitProgman command instructs Program Manager to terminate and, optionally, save its group information.

Following is the parameter for this command:

bSaveGroups

Specifies a Boolean value that, if it is nonzero, causes Program Manager to save its group information before closing. If bSaveGroups is zero, Program Manager does not save its group information.

+$#A Requesting Group Information

Program Manager can provide information about its groups to an application. Applications can request this information from Program Manager by using the PROGMAN topic.

An application can obtain a list of Program Manager groups by issuing a request for the Group item. Program Manager provides the list in CF_TEXT format. The list consists of group-name strings separated by carriage returns.

An application can use a group name as an item name to request information about the group. Program Manager provides this information in CF_TEXT format. The fields of group information are separated by commas. The first line of the information contains the group name (in quotation marks), the path of the group file, and the number of items in the group. Each subsequent line contains information about an item in the group, including the command line (in quotation marks), the default directory, the icon path, the position in the group, the icon index, the shortcut key (in numeric form), and the minimize flag.

+$#AK Shell Library

A shell is an application that enables users to group, start, and otherwise control other applications. This overview describes features of the shell for the Microsoft® Windows® operating system.

+$#A About the Shell Library

The following features are supported by the dynamic-link library SHELL.DLL:

• The drag-drop feature

• Associations (used) to find and start applications

• Extraction of icons from executable files

The header file for the functions that support these features is SHELLAPI.H. The library is SHELL32.LIB.

+$#A Using the Shell Features

• Using the drag-drop feature

• Using associations to find and start applications

• Extracting icons from executable files

+$#A Using the Drag-Drop Feature

When an application implements the drag-drop feature, a user can select one or more files in File Manager, drag them to an open application, and drop them there. The application in which the files were dropped receives a message it can use to retrieve the filenames and the coordinates of the point at which the files were dropped.

An application that can accept dropped files from File Manager calls the DragAcceptFiles function for one or more of its windows. When the user releases the mouse button to drop a file or files in the window specified in the call to DragAcceptFiles, File Manager sends the application a WM_DROPFILES message. (File Manager does not send the WM_DROPFILES message to an application unless the application calls DragAcceptFiles.) WM_DROPFILES contains a handle of a structure the application can query to retrieve the name of the dropped file and the coordinates of the cursor when the file was dropped. The application can use the DragQueryFile function to retrieve a count of the files that were dropped and their names. The DragQueryPoint function returns the window coordinates of the cursor when the user released the mouse button.

To free the memory allocated by the system for the WM_DROPFILES message, an application calls the DragFinish function when it is finished.

For example, an application can call the DragAcceptFiles function when it starts and call a drag-drop function when it receives a WM_DROPFILES message, as shown in the following example.

case WM_CREATE:

DragAcceptFiles(hwnd, TRUE);

break;

case WM_DROPFILES:

DragFunc(hwnd, wParam); /* application-defined function */

break;

case WM_DESTROY:

DragAcceptFiles(hwnd, FALSE);

break;

The following example uses the DragQueryPoint function to determine where to begin to write text. The first call to the DragQueryFile function determines the number of dropped files. The loop writes the name of each file, beginning at the point returned by DragQueryPoint.

POINT pt;

WORD cFiles, a;

char lpszFile[80];

DragQueryPoint((HANDLE) wParam, &pt);

cFiles = DragQueryFile((HANDLE) wParam, 0xFFFF, (LPSTR) NULL, 0);

for(a = 0; a < cFiles; pt.y += 20, a++) {

DragQueryFile((HANDLE) wParam, a, lpszFile, sizeof(lpszFile));

TextOut(hdc, pt.x, pt.y, lpszFile, lstrlen(lpszFile));

}

DragFinish((HANDLE) wParam);

+$#A Using Associations to Find and Start Applications

File Manager includes an Associate dialog box that makes it possible for users to associate a filename extension with a specific application. File Manager stores these associations in the registry (under HKEY_CURRENT_USER\Software\Description\Microsoft\Windows\CurrentVersion\Extensions). If a file has a filename extension that is associated with an application, that application starts automatically whenever a user double-clicks that file in File Manager.

Using the FindExecutable and ShellExecute functions, applications can take advantage of such associations to find and start applications or open and print files.

An application can use the FindExecutable function to retrieve the name and handle of the executable file that is associated with a specified filename. The ShellExecute function either opens or prints a specified file, depending on the value of its lpOperation parameter. To open a document file, the function relies on the association of the filename extension.

Windows 95: You can use ShellExecute to open or explore a Windows 95 folder. To open a folder, use either of the following calls:

ShellExecute(handle, NULL, "path_to_folder", NULL, NULL, SW_SHOWNORMAL);

or

ShellExecute(handle, "open", "path_to_folder", NULL, NULL, SW_SHOWNORMAL);

To explore a folder, use the following call:

ShellExecute(handle, "explore", "path_to_folder", NULL, NULL, SW_SHOWNORMAL);

If the lpOperation parameter is NULL, the function opens the file specified by its lpFile parameter. If lpOperation is "open" or "explore", the function will force a open window or explorer.

+$#A Extracting Icons from Executable Files

An application can use the ExtractIcon function to retrieve the handle of an icon from a specified executable file, dynamic-link library, or icon file. The following example uses the DragQueryPoint function to retrieve the coordinates of the point where a file was dropped, the DragQueryFile function to retrieve the filename of a dropped file, and the ExtractIcon function to retrieve the handle of the first icon in the file, if any.

POINT pt;

WORD cFiles;

HDC hdc;

char lpszFile[80];

HANDLE hCurrentInst, hicon;

DragQueryPoint((HANDLE) wParam, &pt);

cFiles = DragQueryFile((HANDLE) wParam, 0xFFFF, NULL, NULL);

if(cFiles > 1) {

TextOut(hdc, pt.x, pt.y,

"Please d1rop only one icon file.", 31);

return FALSE;

}

else {

DragQueryFile((HANDLE) wParam, 0, lpszFile, sizeof(lpszFile));

hCurrentInst = (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE);

hicon = ExtractIcon(hCurrentInst, lpszFile, 0);

if (hicon == NULL)

TextOut(hdc, pt.x, pt.y, "No icons found.", 15);

else if (hicon == (HICON) 1)

TextOut(hdc, pt.x, pt.y,

"File must be .EXE, .ICO, or .DLL.", 33);

else

DrawIcon(hdc, pt.x, pt.y, hicon);

}

DragFinish((HANDLE) wParam);

+$#AK Shell Library Reference

The following functions, interfaces, messages, and structures are associated with shell features.

+$#A Shell Library Functions

The following functions are used to implement shell features.

DragAcceptFiles
DragFinish
DragQueryFile
DragQueryPoint
ExtractAssociatedIcon
ExtractIcon
FindExecutable
ShellAbout
ShellExecute
ShellExecuteEx

+$#A Shell Library Interfaces

The following interfaces are used to implement shell features.

IContextMenu
ICopyHook
IExtractIcon
IShellPropSheetExt
IShellExtInit
IShellLink
IShellFolder
IEnumIDList

+$#A Shell Library Messages

The following message is used to implement shell features.

WM_DROPFILES

+$#AK Shell Library Structures

The following structure is used to implement shell features.

SHELLEXECUTEINFO

+$#AK About Windows 95 Shell Extensions

Applications can extend the shell of the Microsoft Windows 95 operating system in a number of ways. A shell extension enhances the shell by providing additional ways to manipulate file objects, by simplifying the task of browsing through the file system and networks, or by giving the user easier access to tools that manipulate objects in the file system. For example, a shell extension can assign an icon to each file or add commands to the context menu and file menu for a file.

Windows 95 supports the following types of shell extensions:

• Context menu handlers: These add items to the context menu for a particular file object. (The context menu is displayed when the user clicks a file object with the alternate mouse button.)

• Drag-and-drop handlers: These are context menu handlers that the system calls when the user drops an object after dragging it to a new position.

• Icon handlers: These typically add instance-specific icons for file objects. They can also be used to add icons for all files belonging to the same class.

• Property-sheet handlers: These add pages to the property sheet dialog box that the shell displays for a file object. The pages are specific to a class of files or a particular file object.

• Copy-hook handlers: These are called when a folder object is about to be copied, moved, deleted, or renamed. The handler can either allow or prevent the operation.

The design of a shell extension is based on the Component Object Model in Object Linking and Embedding (OLE). The shell accesses an object through interfaces. An application implements the interfaces in a shell extension dynamic-link library (DLL), which is similar to an In-Process Server DLL in OLE.

For more information, see "Extending the Windows 95 Shell" in the Windows 95 Guide to Programming.

+$#AK Windows 95 Shell Extensions on Windows NT

Applications that employ Windows 95 shell-extension handlers (icon handlers, property-page handlers, context-menu handlers, copy hooks, drag-and-drop handlers, and other such DLL-based shell extension classes) can run on the next release of Windows NT with the new shell. To do so, you must take one additional step while registering during setup. This topic describes how to install such applications on Windows NT.

Because shell extensions handlers run in a system process, namely the shell process, Windows NT administrators require control over which shell handlers are allowed to run. When the new shell is available on Windows NT, administrators will have control over which shell handlers can run in much the same way they can now control which device drivers can run.

For the Windows NT shell to recognize and run a shell-extension handler, the handler's CLSID must be listed in the Registry Editor under a new registry key (as well as being registered in the typical way as specified for Windows 95). This registry key contains a list of the handlers that are approved for the shell to run. By default, this key's access control permissions allow only someone with administrator privileges to modify the list.

The CLSID for the extension must be registered at the following location:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved

To register the extension, a "named value" should be added to the "Approved" key. The name of the value must be the string form of the CLSID as obtained from the StringFromCLSID() function, which is used elsewhere in the registry. The value itself should be the ProgID. The ProgID is stored here simply to make inspection of the registry easier, as it is easier to decipher than a CLSID. The Windows NT shell does not look at this value, however, only at the presence or absence of the CLSID.

Your setup application may or may not be able to write to this key, depending on the privileges of the person installing the application. The setup application should attempt to open the key described above, requesting the KEY_SET_VALUE permission. If it succeeds, the new CLSID can be added to fully register the corresponding shell extension. If the request fails with a security violation, then the person installing the application does not have permission to register new shell extensions. In this case, the setup application might warn the user that some application features will not be available unless an administrator turns them on (by installing the application, or by writing the registry keys directly). Or, if the shell extension is crucial to the application's functioning, the setup application might cause the installation to fail completely, notifying the user that the program must be installed by an administrator.

When setting up on Windows 95, it is not necessary to write the CLSID to this key, although writing it is harmless. Note, however, that the key may not exist in a Windows 95 installation, so that if your setup application does attempt to open the key, it may fail.

The following sample code details this process. Full error handling has been omitted.

#include <windows.h>

#include <string.h>

void main(void)

{

//

// First, attempt to open the registry key where approved extensions are

// listed. Note the extra slashes within the second parameter (the

// registry path string).

//

long err;

HKEY hkApproved;

err = RegOpenKeyEx(

HKEY_LOCAL_MACHINE,

"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",

0,

KEY_SET_VALUE,

&hkApproved);

if (err == ERROR_ACCESS_DENIED)

{

//

// The user does not have permissions to add a new value to this key. In this

// case, a reasonable action would be to warn the user that some

// application features will not be available unless an administrator

// installs the application. If the shell extension is central to the

// functioning of the application, tell the user that the install

// can only be performed by an administrator, and stop the install.

//

}

else if (err == ERROR_FILE_NOT_FOUND)

{

//

// The key does not exist. This should only happen if setup is running

// on Windows 95 instead of Windows NT, or if you are installing on an older

// version of either operating system that does not have the Windows 95-style UI.

//

}

else if (err != ERROR_SUCCESS)

{

//

// some other problem...

//

}

else

{

//

// The open of the key succeeded. Now register the new shell extension

// under this key. This requires having the ProgID and string form of the

// CLSID handy.

//

//

// Assume that lpstrProgID contains our ProgID string.

//

LPSTR lpstrProgID = "My Bogus Class";

//

// Assume that clsidExtension contains the CLSID struct. The code below

// creates a string form this CLSID. If a string version of

// the CLSID is already handy, skip this code.

//

CLSID clsidExtension = {0x11111111, 0x1111, 0x1111, 0x11, 0x11,

0x11, 0x11, 0x11, 0x11, 0x11, 0x11};

HRESULT hr;

LPOLESTR lpolestrCLSID;

CHAR rgchCLSID[40];

CoInitialize(NULL);

hr = StringFromCLSID(clsidExtension, &lpolestrCLSID);

//

// StringFromCLSID returns a Unicode string, so convert to ANSI for

// calling the registry. Note that on Windows NT you can call the Unicode

// version of the registry API instead.

//

WideCharToMultiByte(CP_ACP, 0, lpolestrCLSID, -1, rgchCLSID, 40,

NULL, NULL);

CoTaskMemFree(lpolestrCLSID);

CoUninitialize();

//

// Now add the new value to the registry.

// Note that each new shell extension CLSID must be registered here.

//

err = RegSetValueEx(

hkApproved,

rgchCLSID,

0,

REG_SZ,

(const BYTE *)lpstrProgID,

strlen(lpstrProgID));

//

// Finally, close the key.

//

err = RegCloseKey(hkApproved);

}

}

Créé avec HelpNDoc Personal Edition: Générateur de documentation complet


Socket Functions.rtf

+$#AK Socket Functions

In each routine it is indicated that the header file WINSOCK.H must be included. The Berkeley-compatible header files that are supported are provided for compatibility purposes only, and each of them will simply include WINSOCK.H. The Windows header file WINDOWS.H is also needed, but WINSOCK.H will include it if necessary.

The Windows Sockets specification includes the following Berkeley-style socket functions:

accept* | An incoming connection is acknowledged and associated with an immediately created socket. The original socket is returned to the listening state.
bind | Assign a local name to an unnamed socket.
closesocket* | Remove a socket from the per-process object reference table. Only blocks if SO_LINGER is set.
connect* | Initiate a connection on the specified socket.
getpeername | Retrieve the name of the peer connected to the specified socket.
getsockname | Retrieve the current name for the specified socket
getsockopt | Retrieve options associated with the specified socket.
htonl | Convert a 32-bit quantity from host byte order to network byte order.
htons | Convert a 16-bit quantity from host byte order to network byte order.
inet_addr | Converts a character string representing a number in the Internet standard "." notation to an Internet address value.
inet_ntoa | Converts an Internet address value to an ASCII string in "." notation (for example, "a.b.c.d").
ioctlsocket | Provide control for sockets.
listen | Listen for incoming connections on a specified socket.
ntohl | Convert a 32-bit quantity from network byte order to host byte order.
ntohs | Convert a 16-bit quantity from network byte order to host byte order.
recv* | Receive data from a connected socket.
recvfrom* | Receive data from either a connected or unconnected socket.
select* | Perform synchronous I/O multiplexing.
send* | Send data to a connected socket.
sendto* | Send data to either a connected or unconnected socket.
setsockopt | Store options associated with the specified socket.
shutdown | Shut down part of a full-duplex connection.
socket | Create an endpoint for communication and return a socket.
* The function can block if acting on a blocking socket.

+$#A Blocking/Nonblocking and Data Volatility

One major issue in porting applications from a Berkeley sockets environment to a Windows environment involves "blocking"; that is, invoking a function that does not return until the associated operation is completed. The problem arises if the operation takes an arbitrarily long time to complete: an obvious example is a recv that may block until data has been received from the peer system. The default behavior within the Berkeley sockets model is for a socket to operate in a blocking mode unless the programmer explicitly requests that operations be treated as nonblocking.

Note It is strongly recommended that programmers use the nonblocking (asynchronous) operations if at all possible, because they work significantly better within the nonpreemptive Windows environment. Use blocking operations only if absolutely necessary, and carefully read and understand this topic if you must use blocking operations.

Even on a blocking socket, some operations (for example, bind, getsockopt, getpeername) can be completed immediately. For such operations there is no difference between blocking and nonblocking operation. Other operations (for example, recv) may be completed immediately or may take an arbitrary time to complete, depending on various transport conditions. When applied to a blocking socket, these operations are referred to as blocking operations. All functions that can block are listed with an asterisk in the tables in Socket Functions and Socket Database Functions.

Within a Windows Sockets implementation, a blocking operation that cannot be completed immediately is handled as follows. The DLL initiates the operation, and then enters a loop in which it dispatches any Windows messages (yielding the processor to another thread if necessary) and then checks for the completion of the Windows Sockets function. If the function has completed, or if WSACancelBlockingCall has been invoked, the blocking function completes with an appropriate result. Refer to WSASetBlockingHook for a complete description of this mechanism, including pseudocode for the various functions.

If a Windows message is received for a process for which a blocking operation is in progress, there is a risk that the application will attempt to issue another Windows Sockets call. Because of the difficulty of managing this condition safely, the Windows Sockets specification does not support such application behavior. Two functions are provided to assist you in this situation. WSAIsBlocking may be called to determine whether or not a blocking Windows Sockets call is in progress. WSACancelBlockingCall may be called to cancel an in-progress blocking call, if any. Any other Windows Sockets function that is called in this situation will fail with the error WSAEINPROGRESS. It should be emphasized that this restriction applies to both blocking and nonblocking operations.

Although this mechanism is sufficient for simple applications, it cannot support the complex message-dispatching requirements of more advanced applications (for example, those using the MDI model). For such applications, Windows Sockets includes the function WSASetBlockingHook, which allows the programmer to define a special function that will be called instead of the default message dispatch function previously described.

The Windows Sockets DLL calls the blocking hook only if all of the following are true: the function is one that is defined as being able to block, the specified socket is a blocking socket, and the request cannot be completed immediately. (A socket is set to blocking by default, but the IOCTL FIONBIO and WSAAsyncSelect both set a socket to nonblocking mode.) If an application uses only nonblocking sockets and uses the WSAAsyncSelect and/or the WSAAsyncGetXByY routines instead of select and the getXbyY routines, then the blocking hook will never be called and the application does not need to handle reentrancy.

If an application invokes an asynchronous or nonblocking operation that takes a pointer to a memory object (for example, a buffer, or a global variable) as an argument, the application must ensure that the object is available to the Windows Sockets implementation throughout the operation. The application must not invoke any Windows function that might affect the mapping or addressability of the memory involved. In a multithreaded system, the application is also responsible for coordinating access to the object using appropriate synchronization mechanisms. A Windows Sockets implementation cannot, and will not, address these issues. The possible consequences of failing to observe these rules are beyond the scope of this specification.

+$#A Socket Database Functions

The Windows Sockets specification defines the following database functions. As noted earlier, a Windows Sockets supplier may choose to implement these in a manner that does not depend on local database files. The pointer returned by certain database functions such as gethostbyname points to a structure allocated by the Windows Sockets library. The data pointed to is volatile and is good only until the next Windows Sockets function call from that thread. Additionally, the application must never attempt to modify this structure or to free any of its components. Only one copy of this structure is allocated for a thread, and so the application should copy any information it needs before issuing any other Windows Sockets function calls.

gethostbyaddr* | Retrieves the name(s) and address corresponding to a network address.
gethostbyname* | Retrieves the name(s) and address corresponding to a host name.
gethostname | Retrieves the name of the local host.
getprotobyname* | Retrieves the protocol name and number corresponding to a protocol name.
getprotobynumber* | Retrieves the protocol name and number corresponding to a protocol number.
getservbyname* | Retrieves the service name and port corresponding to a service name.
getservbyport* | Retrieves the service name and port corresponding to a port.
* The function can block under some circumstances.

+$#A Microsoft Windows-Specific Extension Functions

Windows Sockets provides a number of extensions to the standard set of Berkeley Sockets functions. Principally, these extended functions allow message-based, asynchronous access to network events. While use of this extended function set is not mandatory for socket-based programming (with the exception of WSAStartup and WSACleanup), it is recommended for conformance with the Microsoft Windows programming paradigm.

The following functions provide asynchronous versions of the standard Berkeley getXbyY functions. For example, the WSAAsyncGetHostByName function provides an asynchronous message-based implementation of the standard Berkeley gethostbyname function.

WSAAsyncGetHostByAddr

WSAAsyncGetHostByName

WSAAsyncGetProtoByName

WSAAsyncGetProtoByNumber

WSAAsyncGetServByName

WSAAsyncGetServByPort

WSAAsyncSelect | Performs asynchronous version of select.
WSACancelAsyncRequest | Cancels an outstanding instance of a WSAAsyncGetXByY function.
WSACancelBlockingCall | Cancels an outstanding "blocking" function call.
WSACleanup | Signs off from the underlying Windows Sockets DLL.
WSAGetLastError | Obtains details of last Windows Sockets function error.
WSAIsBlocking | Determines if the underlying Windows Sockets DLL is already blocking an existing call for this thread.
WSASetBlockingHook | "Hooks" the blocking method used by the underlying Windows Sockets implementation.
WSASetLastError | Sets the error to be returned by a subsequent call to WSAGetLastError.
WSAStartup | Initializes the underlying Windows Sockets DLL.
WSAUnhookBlockingHook | Restores the original blocking function.

+$#A Asynchronous Select Mechanism

The WSAAsyncSelect function allows an application to register an interest in one or many network events. This function is provided to supersede the need to do polled network I/O. Any situation in which select or nonblocking I/O routines (such as send and recv) are either already used or are being considered is usually a candidate for the WSAAsyncSelect function. When declaring interest in such condition(s), you supply a window handle to be used for notification. The corresponding window then receives message-based notification of the conditions in which you declared an interest.

WSAAsyncSelect allows interest to be declared in the following conditions for a particular socket:

• Socket readiness for reading

• Socket readiness for writing

• Out-of-band data ready for reading

• Socket readiness for accepting incoming connection

• Completion of nonblocking connect operation

• Connection closure

+$#A Asynchronous Support Routines

The asynchronous "database" functions allow applications to request information in an asynchronous manner. Some network implementations or configurations perform network-based operations to resolve such requests. The WSAAsyncGetXByY functions allow application developers to request services that would otherwise block the operation of the whole Windows environment if the standard Berkeley function were used. The WSACancelAsyncRequest function allows an application to cancel any outstanding asynchronous request.

+$#A Hooking Blocking Methods

As noted in Blocking/Nonblocking and Data Volatility, Windows Sockets implements blocking operations in such a way that Windows message processing can continue, which may result in the application that issued the call receiving a Windows message. In certain situations, an application may need to influence or change the way in which this pseudo-blocking process is implemented. The WSASetBlockingHook function provides the ability to substitute a named function that the Windows Sockets implementation is to use when relinquishing the processor during a "blocking" operation.

+$#A Error Handling

For compatibility with thread-based environments, details of function errors are obtained through the WSAGetLastError function. Although the accepted Berkeley-style mechanism for obtaining socket-based network errors is by using errno, this mechanism cannot guarantee the integrity of an error ID in a multithreaded environment. WSAGetLastError allows you to retrieve an error code on a per-thread basis.

WSAGetLastError returns error codes that avoid conflict with standard Microsoft C error codes. Certain error codes returned by certain Windows Sockets routines fall into the standard range of error codes as defined by Microsoft C. If you are NOT using an application development environment that defines error codes consistent with Microsoft C, you should use the Windows Sockets error codes prefixed by "WSA" to ensure accurate error code detection.

Note that this specification defines a recommended set of error codes, and lists the possible errors that may be returned as a result of each function. It may be the case in some implementations that other Windows Sockets error codes will be returned in addition to those listed, and applications should be prepared to handle errors other than those enumerated under each function description. However, a Windows Sockets implementation must not return any value that is not enumerated in the table of legal Windows Sockets errors given in Error Codes.

+$# Accessing a Windows Sockets DLL from an Intermediate DLL (16-bit Applications)

A Windows Sockets DLL can be accessed both directly from an application and through an "intermediate" DLL. An example of such an intermediate DLL would be a virtual network API layer that supports generalized network functionality for applications and uses Windows Sockets. Such a DLL could be used by several applications simultaneously, and the DLL must take special precautions with respect to the WSAStartup and WSACleanup calls to ensure that these routines are called in the context of each task that will make Windows Sockets calls. This is because the Windows Sockets DLL will need a call to WSAStartup for each task in order to set up task-specific data structures, and a call to WSACleanup to free any resources allocated for the task.

There are (at least) two ways to accomplish this. The simplest method is for the intermediate DLL to have calls similar to WSAStartup and WSACleanup that applications call as appropriate. The DLL would then call WSAStartup or WSACleanup from within these routines. Another mechanism is for the intermediate DLL to build a table of task handles, which are obtained from the GetCurrentTask Windows function, and at each entry point into the intermediate DLL check whether WSAStartup has been called for the current task, then call WSAStartup if necessary.

If a DLL makes a blocking call and does not install its own blocking hook, then the DLL author must be aware that control may be returned to the application either by an application-installed blocking hook or by the default blocking hook. Thus, it is possible that the application will cancel the DLL's blocking operation via WSACancelBlockingCall. If this occurs, the DLL's blocking operation will fail with the error code WSAEINTR, and the DLL must return control to the calling task as quickly as possible, because the user has likely pressed a Cancel or Close button and the task has requested control of the CPU. It is recommended that DLLs that make blocking calls install their own blocking hooks with WSASetBlockingHook to prevent unforeseen interactions between the application and the DLL.

Note that this is not necessary for DLLs in Windows NT because of its different process and DLL structure. Under Windows NT, the intermediate DLL could simply call WSAStartup in its DLL initialization routine, which is called whenever a new process which uses the DLL starts.

+$# Internal Use of Messages by Windows Sockets Implementations

To implement Windows Sockets purely as a DLL, it may be necessary for the DLL to post messages internally for communication and timing. This is perfectly legal; however, a Windows Sockets DLL must not post messages to a window handle opened by a client application except for those messages requested by the application. A Windows Sockets DLL that needs to use messages for its own purposes must open a hidden window and post any necessary messages to the handle for that window.

+$#A Private API Interfaces

The WINSOCK.DEF file in DLL Ordinal Numbers lists the ordinals defined for the Windows Sockets functions. In addition to the ordinal values listed, all ordinals 999 and below are reserved for future Windows Sockets use. It may be convenient for a Windows Sockets implementation to export additional, private interfaces from the Windows Sockets DLL. This is perfectly acceptable, as long as the ordinals for these exports are above 1000. Note that any application that uses a particular Windows Sockets DLL's private functions will most likely not work on any other vendor's Windows Sockets implementation. Only the functions defined in this document are guaranteed to be present in every Windows Sockets implementation.

If an application uses private interfaces of a particular vendor's Windows Sockets DLL, it is recommended that the DLL not be statically linked with the application but rather dynamically loaded with the Windows routines LoadLibrary and GetProcAddress. This allows the application to give an informative error message if it is run on a system with a Windows Sockets DLL that does not support the same set of extended functionality.

+$#A Error Codes and Header Files

The following topics list the error codes and header files.

Error Codes

Header Files

+$#A Error Codes

The following is a list of possible error codes returned by the WSAGetLastError function, along with their explanations. The error numbers are consistently set across all Windows Sockets-compliant implementations.

Windows Sockets code | Berkeley equivalent | Error | Interpretation
WSAEINTR | EINTR | 10004 | As in standard C
WSAEBADF | EBADF | 10009 | As in standard C
WSAEACCES | EACCES | 10013 | As in standard C
WSAEDISCON | None | 10101 | The message terminated gracefully. Used only for message-oriented protocols.
WSAEFAULT | EFAULT | 10014 | As in standard C
WSAEINVAL | EINVAL | 10022 | As in standard C
WSAEMFILE | EMFILE | 10024 | As in standard C
WSAEWOULDBLOCK | EWOULDBLOCK | 10035 | As in BSD
WSAEINPROGRESS | EINPROGRESS | 10036 | This error is returned if anyWindows Sockets function is called while a blocking function is in progress.
WSAEALREADY | EALREADY | 10037 | As in BSD
WSAENOTSOCK | ENOTSOCK | 10038 | As in BSD
WSAEDESTADDRREQ | EDESTADDRREQ | 10039 | As in BSD
WSAEMSGSIZE | EMSGSIZE | 10040 | As in BSD
WSAEPROTOTYPE | EPROTOTYPE | 10041 | As in BSD
WSAENOPROTOOPT | ENOPROTOOPT | 10042 | As in BSD
WSAEPROTONOSUPPORT | EPROTONOSUPPORT | 10043 | As in BSD
WSAESOCKTNOSUPPORT | ESOCKTNOSUPPORT | 10044 | As in BSD
WSAEOPNOTSUPP | EOPNOTSUPP | 10045 | As in BSD
WSAEPFNOSUPPORT | EPFNOSUPPORT | 10046 | As in BSD
WSAEAFNOSUPPORT | EAFNOSUPPORT | 10047 | As in BSD
WSAEADDRINUSE | EADDRINUSE | 10048 | As in BSD
WSAEADDRNOTAVAIL | EADDRNOTAVAIL | 10049 | As in BSD
WSAENETDOWN | ENETDOWN | 10050 | As in BSD. This error may be reported at any time if the Windows Sockets implementation detects an underlying failure.
WSAENETUNREACH | ENETUNREACH | 10051 | As in BSD
WSAENETRESET | ENETRESET | 10052 | As in BSD
WSAECONNABORTED | ECONNABORTED | 10053 | As in BSD
WSAECONNRESET | ECONNRESET | 10054 | As in BSD
WSAENOBUFS | ENOBUFS | 10055 | As in BSD
WSAEISCONN | EISCONN | 10056 | As in BSD
WSAENOTCONN | ENOTCONN | 10057 | As in BSD
WSAESHUTDOWN | ESHUTDOWN | 10058 | As in BSD
WSAETOOMANYREFS | ETOOMANYREFS | 10059 | As in BSD
WSAETIMEDOUT | ETIMEDOUT | 10060 | As in BSD
WSAECONNREFUSED | ECONNREFUSED | 10061 | As in BSD
WSAELOOP | ELOOP | 10062 | As in BSD
WSAENAMETOOLONG | ENAMETOOLONG | 10063 | As in BSD
WSAEHOSTDOWN | EHOSTDOWN | 10064 | As in BSD
WSAEHOSTUNREACH | EHOSTUNREACH | 10065 | As in BSD
WSASYSNOTREADY | None | 10091 | Returned by WSAStartup, indicating that the network subsystem is unusable.
WSAVERNOTSUPPORTED | None | 10092 | Returned by WSAStartup, indicating that the Windows Sockets DLL cannot support this app.
WSANOTINITIALISED | None | 10093 | Returned by any function except WSAStartup, indicating that a successful WSAStartup, has not yet been performed.
WSAHOST_NOT_FOUND | HOST_NOT_FOUND | 11001 | As in BSD
WSATRY_AGAIN | TRY_AGAIN | 11002 | As in BSD
WSANO_RECOVERY | NO_RECOVERY | 11003 | As in BSD
WSANO_DATA | NO_DATA | 11004 | As in BSD

The first set of definitions is present to resolve contentions between standard C error codes, which may be defined inconsistently between various C compilers.

The second set of definitions provides Windows Sockets versions of regular Berkeley Sockets error codes.

The third set of definitions consists of extended Windows Sockets-specific error codes.

The fourth set of errors are returned by Windows Sockets getXbyY and WSAAsyncGetXByY functions, and correspond to the errors that in Berkeley software would be returned in the h_errno variable. They correspond to various failures that may be returned by the Domain Name Service. If the Windows Sockets implementation does not use the DNS, it will use the most appropriate code. In general, a Windows Sockets application should interpret WSAHOST_NOT_FOUND and WSANO_DATA as indicating that the key (name, address, and so on) was not found, while WSATRY_AGAIN and WSANO_RECOVERY suggest that the name service itself is nonoperational.

The error numbers are derived from the WINSOCK.H header file and are based on the fact that Windows Sockets error numbers are computed by adding 10000 to the "normal" Berkeley error number.

Note that this table does not include all of the error codes defined in WINSOCK.H. This is because it includes only errors that might reasonably be returned by a Windows Sockets implementation. WINSOCK.H, on the other hand, includes a full set of BSD definitions to ensure compatibility with ported software.

+$#A Header Files

+$#A Berkeley Header Files

A Windows Sockets supplier who provides a development kit to support the development of Windows Sockets applications must supply a set of vestigial header files with names that match a number of the header files in the Berkeley software distribution. These files are provided for source code compatibility only, and each consists of three lines:

#ifndef _WINSOCKAPI_

#include <winsock.h>

#endif

The header files provided for compatibility are:

NETDB.H
ARPA/INET.H
SYS/TIME.H
SYS/SOCKET.H
NETINET/IN.H

The file WINSOCK.H contains all of the type and structure definitions, constants, macros, and function prototypes used by the Windows Sockets specification. You may choose to ignore the compatibility headers and include WINSOCK.H in each source file.

+$#A Windows Sockets Header File — WINSOCK.H

The WINSOCK.H header file includes a number of types and definitions from the standard Windows header file WINDOWS.H. The WINDOWS.H file in the Windows 3.0 SDK (Software Developer's Kit) lacks a #include guard, so if you need to include WINDOWS.H as well as WINSOCK.H, you should define the symbol _INC_WINDOWS before #including WINSOCK.H, as follows:

#include <windows.h>

#define _INC_WINDOWS

#include <winsock.h>

Users of the SDK for Windows 3.1 and later need not do this.

A Windows Sockets DLL vendor must not make any modifications to this header file that could impact binary compatibility of Windows Sockets applications. The constant values, function parameters, return codes, and the like must remain consistent across all Windows Sockets DLL vendors.

/* WINSOCK.H--definitions to be used with the WINSOCK.DLL

*

* This header file corresponds to version 1.1 of the Windows Sockets specification.

*

* This file includes parts which are Copyright (c) 1982-1986 Regents

* of the University of California. All rights reserved. The

* Berkeley Software License Agreement specifies the terms and

* conditions for redistribution.

*/

#ifndef _WINSOCKAPI_

#define _WINSOCKAPI_

/*

* Pull in WINDOWS.H if necessary

*/

#ifndef _INC_WINDOWS

#include <windows.h>

#endif /* _INC_WINDOWS */

/*

* Basic system type definitions, taken from the BSD file sys/types.h.

*/

typedef unsigned char u_char;

typedef unsigned short u_short;

typedef unsigned int u_int;

typedef unsigned long u_long;

/*

* The new type to be used in all

* instances which refer to sockets.

*/

typedef u_int SOCKET;

/*

* Select uses arrays of SOCKETs. These macros manipulate such

* arrays. FD_SETSIZE may be defined by the user before including

* this file, but the default here should be >= 64.

*

* CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE

* INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE.

*/

#ifndef FD_SETSIZE

#define FD_SETSIZE 64

#endif /* FD_SETSIZE */

typedef struct fd_set {

u_short fd_count; /* how many are SET? */

SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */

} fd_set;

extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *);

#define FD_CLR(fd, set) do { \

u_int __i; \

for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \

if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \

while (__i < ((fd_set FAR *)(set))->fd_count-1) { \

((fd_set FAR *)(set))->fd_array[__i] = \

((fd_set FAR *)(set))->fd_array[__i+1]; \

__i++; \

} \

((fd_set FAR *)(set))->fd_count--; \

break; \

} \

} \

} while(0)

#define FD_SET(fd, set) do { \

if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) \

((fd_set FAR *)(set))->fd_array[((fd_set FAR *)(set))->fd_count++]=fd;\

} while(0)

#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)

#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)fd, (fd_set FAR *)set)

/*

* Structure used in select call, taken from the BSD file sys/time.h.

*/

struct timeval {

long tv_sec; /* seconds */

long tv_usec; /* and microseconds */

};

/*

* Operations on timevals.

*

* NB: timercmp does not work for >= or <=.

*/

#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)

#define timercmp(tvp, uvp, cmp) \

((tvp)->tv_sec cmp (uvp)->tv_sec || \

(tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)

#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0

/*

* Commands for ioctlsocket, taken from the BSD file fcntl.h.

*

*

* Ioctl's have the command encoded in the lower word,

* and the size of any in or out parameters in the upper

* word. The high 2 bits of the upper word are used

* to encode the in/out status of the parameter; for now

* we restrict parameters to at most 128 bytes.

*/

#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */

#define IOC_VOID 0x20000000 /* no parameters */

#define IOC_OUT 0x40000000 /* copy out parameters */

#define IOC_IN 0x80000000 /* copy in parameters */

#define IOC_INOUT (IOC_IN|IOC_OUT)

/* 0x20000000 distinguishes new &

old ioctl's */

#define _IO(x,y) (IOC_VOID|(x<<8)|y)

#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)

#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)

#define FIONREAD _IOR('f', 127, u_long) /* get # bytes to read */

#define FIONBIO _IOW('f', 126, u_long) /* set/clear non-blocking i/o */

#define FIOASYNC _IOW('f', 125, u_long) /* set/clear async i/o */

/* Socket I/O Controls */

#define SIOCSHIWAT _IOW('s', 0, u_long) /* set high watermark */

#define SIOCGHIWAT _IOR('s', 1, u_long) /* get high watermark */

#define SIOCSLOWAT _IOW('s', 2, u_long) /* set low watermark */

#define SIOCGLOWAT _IOR('s', 3, u_long) /* get low watermark */

#define SIOCATMARK _IOR('s', 7, u_long) /* at oob mark? */

/*

* Structures returned by network database library, taken from the

* BSD file netdb.h. All addresses are supplied in host order, and

* returned in network order (suitable for use in system calls).

*/

struct hostent {

char FAR * h_name; /* official name of host */

char FAR * FAR * h_aliases; /* alias list */

short h_addrtype; /* host address type */

short h_length; /* length of address */

char FAR * FAR * h_addr_list; /* list of addresses */

#define h_addr h_addr_list[0] /* address, for backward compat */

};

/*

* It is assumed here that a network number

* fits in 32 bits.

*/

struct netent {

char FAR * n_name; /* official name of net */

char FAR * FAR * n_aliases; /* alias list */

short n_addrtype; /* net address type */

u_long n_net; /* network # */

};

struct servent {

char FAR * s_name; /* official service name */

char FAR * FAR * s_aliases; /* alias list */

short s_port; /* port # */

char FAR * s_proto; /* protocol to use */

};

struct protoent {

char FAR * p_name; /* official protocol name */

char FAR * FAR * p_aliases; /* alias list */

short p_proto; /* protocol # */

};

/*

* Constants and structures defined by the Internet system,

* Per RFC 790, September 1981, taken from the BSD file netinet/in.h.

*/

/*

* Protocols

*/

#define IPPROTO_IP 0 /* dummy for IP */

#define IPPROTO_ICMP 1 /* control message protocol */

#define IPPROTO_GGP 2 /* gateway^2 (deprecated) */

#define IPPROTO_TCP 6 /* tcp */

#define IPPROTO_PUP 12 /* pup */

#define IPPROTO_UDP 17 /* user datagram protocol */

#define IPPROTO_IDP 22 /* xns idp */

#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */

#define IPPROTO_RAW 255 /* raw IP packet */

#define IPPROTO_MAX 256

/*

* Port/socket numbers: network standard functions

*/

#define IPPORT_ECHO 7

#define IPPORT_DISCARD 9

#define IPPORT_SYSTAT 11

#define IPPORT_DAYTIME 13

#define IPPORT_NETSTAT 15

#define IPPORT_FTP 21

#define IPPORT_TELNET 23

#define IPPORT_SMTP 25

#define IPPORT_TIMESERVER 37

#define IPPORT_NAMESERVER 42

#define IPPORT_WHOIS 43

#define IPPORT_MTP 57

/*

* Port/socket numbers: host specific functions

*/

#define IPPORT_TFTP 69

#define IPPORT_RJE 77

#define IPPORT_FINGER 79

#define IPPORT_TTYLINK 87

#define IPPORT_SUPDUP 95

/*

* UNIX TCP sockets

*/

#define IPPORT_EXECSERVER 512

#define IPPORT_LOGINSERVER 513

#define IPPORT_CMDSERVER 514

#define IPPORT_EFSSERVER 520

/*

* UNIX UDP sockets

*/

#define IPPORT_BIFFUDP 512

#define IPPORT_WHOSERVER 513

#define IPPORT_ROUTESERVER 520

/* 520+1 also used */

/*

* Ports < IPPORT_RESERVED are reserved for

* privileged processes (e.g. root).

*/

#define IPPORT_RESERVED 1024

/*

* Link numbers

*/

#define IMPLINK_IP 155

#define IMPLINK_LOWEXPER 156

#define IMPLINK_HIGHEXPER 158

/*

* Internet address (old style... should be updated)

*/

struct in_addr {

union {

struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;

struct { u_short s_w1,s_w2; } S_un_w;

u_long S_addr;

} S_un;

#define s_addr S_un.S_addr

/* can be used for most tcp & ip code */

#define s_host S_un.S_un_b.s_b2

/* host on imp */

#define s_net S_un.S_un_b.s_b1

/* network */

#define s_imp S_un.S_un_w.s_w2

/* imp */

#define s_impno S_un.S_un_b.s_b4

/* imp # */

#define s_lh S_un.S_un_b.s_b3

/* logical host */

};

/*

* Definitions of bits in internet address integers.

* On subnets, the decomposition of addresses to host and net parts

* is done according to subnet mask, not the masks here.

*/

#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)

#define IN_CLASSA_NET 0xff000000

#define IN_CLASSA_NSHIFT 24

#define IN_CLASSA_HOST 0x00ffffff

#define IN_CLASSA_MAX 128

#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)

#define IN_CLASSB_NET 0xffff0000

#define IN_CLASSB_NSHIFT 16

#define IN_CLASSB_HOST 0x0000ffff

#define IN_CLASSB_MAX 65536

#define IN_CLASSC(i) (((long)(i) & 0xc0000000) == 0xc0000000)

#define IN_CLASSC_NET 0xffffff00

#define IN_CLASSC_NSHIFT 8

#define IN_CLASSC_HOST 0x000000ff

#define INADDR_ANY (u_long)0x00000000

#define INADDR_LOOPBACK 0x7f000001

#define INADDR_BROADCAST (u_long)0xffffffff

#define INADDR_NONE 0xffffffff

/*

* Socket address, internet style.

*/

struct sockaddr_in {

short sin_family;

u_short sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

#define WSADESCRIPTION_LEN 256

#define WSASYS_STATUS_LEN 128

typedef struct WSAData {

WORD wVersion;

WORD wHighVersion;

char szDescription[WSADESCRIPTION_LEN+1];

char szSystemStatus[WSASYS_STATUS_LEN+1];

unsigned short iMaxSockets;

unsigned short iMaxUdpDg;

char FAR * lpVendorInfo;

} WSADATA;

typedef WSADATA FAR *LPWSADATA;

/*

* Options for use with [gs]etsockopt at the IP level.

*/

#define IP_OPTIONS 1 /* set/get IP per-packet options */

/*

* Definitions related to sockets: types, address families, options,

* taken from the BSD file sys/socket.h.

*/

/*

* This is used instead of -1, since the

* SOCKET type is unsigned.

*/

#define INVALID_SOCKET (SOCKET)(~0)

#define SOCKET_ERROR (-1)

/*

* Types

*/

#define SOCK_STREAM 1 /* stream socket */

#define SOCK_DGRAM 2 /* datagram socket */

#define SOCK_RAW 3 /* raw-protocol interface */

#define SOCK_RDM 4 /* reliably-delivered message */

#define SOCK_SEQPACKET 5 /* sequenced packet stream */

/*

* Option flags per-socket.

*/

#define SO_DEBUG 0x0001 /* turn on debugging info recording */

#define SO_ACCEPTCONN 0x0002 /* socket has had listen */

#define SO_REUSEADDR 0x0004 /* allow local address reuse */

#define SO_KEEPALIVE 0x0008 /* keep connections alive */

#define SO_DONTROUTE 0x0010 /* just use interface addresses */

#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */

#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */

#define SO_LINGER 0x0080 /* linger on close if data present */

#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */

#define SO_DONTLINGER (u_int)(~SO_LINGER)

/*

* Additional options.

*/

#define SO_SNDBUF 0x1001 /* send buffer size */

#define SO_RCVBUF 0x1002 /* receive buffer size */

#define SO_SNDLOWAT 0x1003 /* send low-water mark */

#define SO_RCVLOWAT 0x1004 /* receive low-water mark */

#define SO_SNDTIMEO 0x1005 /* send timeout */

#define SO_RCVTIMEO 0x1006 /* receive timeout */

#define SO_ERROR 0x1007 /* get error status and clear */

#define SO_TYPE 0x1008 /* get socket type */

/*

* TCP options.

*/

#define TCP_NODELAY 0x0001

/*

* Address families.

*/

#define AF_UNSPEC 0 /* unspecified */

#define AF_UNIX 1 /* local to host (pipes, portals) */

#define AF_INET 2 /* internetwork: UDP, TCP, etc. */

#define AF_IMPLINK 3 /* arpanet imp addresses */

#define AF_PUP 4 /* pup protocols: e.g. BSP */

#define AF_CHAOS 5 /* mit CHAOS protocols */

#define AF_NS 6 /* XEROX NS protocols */

#define AF_ISO 7 /* ISO protocols */

#define AF_OSI AF_ISO /* OSI is ISO */

#define AF_ECMA 8 /* european computer manufacturers */

#define AF_DATAKIT 9 /* datakit protocols */

#define AF_CCITT 10 /* CCITT protocols, X.25 etc */

#define AF_SNA 11 /* IBM SNA */

#define AF_DECnet 12 /* DECnet */

#define AF_DLI 13 /* Direct data link interface */

#define AF_LAT 14 /* LAT */

#define AF_HYLINK 15 /* NSC Hyperchannel */

#define AF_APPLETALK 16 /* AppleTalk */

#define AF_NETBIOS 17 /* NetBios-style addresses */

#define AF_MAX 18

/*

* Structure used by kernel to store most

* addresses.

*/

struct sockaddr {

u_short sa_family; /* address family */

char sa_data[14]; /* up to 14 bytes of direct address */

};

/*

* Structure used by kernel to pass protocol

* information in raw sockets.

*/

struct sockproto {

u_short sp_family; /* address family */

u_short sp_protocol; /* protocol */

};

/*

* Protocol families, same as address families for now.

*/

#define PF_UNSPEC AF_UNSPEC

#define PF_UNIX AF_UNIX

#define PF_INET AF_INET

#define PF_IMPLINK AF_IMPLINK

#define PF_PUP AF_PUP

#define PF_CHAOS AF_CHAOS

#define PF_NS AF_NS

#define PF_ISO AF_ISO

#define PF_OSI AF_OSI

#define PF_ECMA AF_ECMA

#define PF_DATAKIT AF_DATAKIT

#define PF_CCITT AF_CCITT

#define PF_SNA AF_SNA

#define PF_DECnet AF_DECnet

#define PF_DLI AF_DLI

#define PF_LAT AF_LAT

#define PF_HYLINK AF_HYLINK

#define PF_APPLETALK AF_APPLETALK

#define PF_MAX AF_MAX

/*

* Structure used for manipulating linger option.

*/

struct linger {

u_short l_onoff; /* option on/off */

u_short l_linger; /* linger time */

};

/*

* Level number for (get/set)sockopt to apply to socket itself.

*/

#define SOL_SOCKET 0xffff /* options for socket level */

/*

* Maximum queue length specifiable by listen.

*/

#define SOMAXCONN 5

#define MSG_OOB 0x1 /* process out-of-band data */

#define MSG_PEEK 0x2 /* peek at incoming message */

#define MSG_DONTROUTE 0x4 /* send without using routing tables */

#define MSG_MAXIOVLEN 16

/*

* Define constant based on rfc883, used by gethostbyxxxx calls.

*/

#define MAXGETHOSTSTRUCT 1024

/*

* Define flags to be used with the WSAAsyncSelect call.

*/

#define FD_READ 0x01

#define FD_WRITE 0x02

#define FD_OOB 0x04

#define FD_ACCEPT 0x08

#define FD_CONNECT 0x10

#define FD_CLOSE 0x20

/*

* All Windows Sockets error constants are biased by WSABASEERR from

* the "normal"

*/

#define WSABASEERR 10000

/*

* Windows Sockets definitions of regular Microsoft C error constants

*/

#define WSAEINTR (WSABASEERR+4)

#define WSAEBADF (WSABASEERR+9)

#define WSAEACCES (WSABASEERR+13)

#define WSAEFAULT (WSABASEERR+14)

#define WSAEINVAL (WSABASEERR+22)

#define WSAEMFILE (WSABASEERR+24)

/*

* Windows Sockets definitions of regular Berkeley error constants

*/

#define WSAEWOULDBLOCK (WSABASEERR+35)

#define WSAEINPROGRESS (WSABASEERR+36)

#define WSAEALREADY (WSABASEERR+37)

#define WSAENOTSOCK (WSABASEERR+38)

#define WSAEDESTADDRREQ (WSABASEERR+39)

#define WSAEMSGSIZE (WSABASEERR+40)

#define WSAEPROTOTYPE (WSABASEERR+41)

#define WSAENOPROTOOPT (WSABASEERR+42)

#define WSAEPROTONOSUPPORT (WSABASEERR+43)

#define WSAESOCKTNOSUPPORT (WSABASEERR+44)

#define WSAEOPNOTSUPP (WSABASEERR+45)

#define WSAEPFNOSUPPORT (WSABASEERR+46)

#define WSAEAFNOSUPPORT (WSABASEERR+47)

#define WSAEADDRINUSE (WSABASEERR+48)

#define WSAEADDRNOTAVAIL (WSABASEERR+49)

#define WSAENETDOWN (WSABASEERR+50)

#define WSAENETUNREACH (WSABASEERR+51)

#define WSAENETRESET (WSABASEERR+52)

#define WSAECONNABORTED (WSABASEERR+53)

#define WSAECONNRESET (WSABASEERR+54)

#define WSAENOBUFS (WSABASEERR+55)

#define WSAEISCONN (WSABASEERR+56)

#define WSAENOTCONN (WSABASEERR+57)

#define WSAESHUTDOWN (WSABASEERR+58)

#define WSAETOOMANYREFS (WSABASEERR+59)

#define WSAETIMEDOUT (WSABASEERR+60)

#define WSAECONNREFUSED (WSABASEERR+61)

#define WSAELOOP (WSABASEERR+62)

#define WSAENAMETOOLONG (WSABASEERR+63)

#define WSAEHOSTDOWN (WSABASEERR+64)

#define WSAEHOSTUNREACH (WSABASEERR+65)

#define WSAENOTEMPTY (WSABASEERR+66)

#define WSAEPROCLIM (WSABASEERR+67)

#define WSAEUSERS (WSABASEERR+68)

#define WSAEDQUOT (WSABASEERR+69)

#define WSAESTALE (WSABASEERR+70)

#define WSAEREMOTE (WSABASEERR+71)

/*

* Extended Windows Sockets error constant definitions

*/

#define WSASYSNOTREADY (WSABASEERR+91)

#define WSAVERNOTSUPPORTED (WSABASEERR+92)

#define WSANOTINITIALISED (WSABASEERR+93)

/*

* Error return codes from gethostbyname and gethostbyaddr

* (when using the resolver). Note that these errors are

* retrieved via WSAGetLastError and must therefore follow

* the rules for avoiding clashes with error numbers from

* specific implementations or language run-time systems.

* For this reason the codes are based at WSABASEERR+1001.

* Note also that [WSA]NO_ADDRESS is defined only for

* compatibility purposes.

*/

#define h_errno WSAGetLastError

/* Authoritative Answer: Host not found */

#define WSAHOST_NOT_FOUND (WSABASEERR+1001)

#define HOST_NOT_FOUND WSAHOST_NOT_FOUND

/* Non-Authoritative: Host not found, or SERVERFAIL */

#define WSATRY_AGAIN (WSABASEERR+1002)

#define TRY_AGAIN WSATRY_AGAIN

/* Non recoverable errors, FORMERR, REFUSED, NOTIMP */

#define WSANO_RECOVERY (WSABASEERR+1003)

#define NO_RECOVERY WSANO_RECOVERY

/* Valid name, no data record of requested type */

#define WSANO_DATA (WSABASEERR+1004)

#define NO_DATA WSANO_DATA

/* no address, look for MX record */

#define WSANO_ADDRESS WSANO_DATA

#define NO_ADDRESS WSANO_ADDRESS

/*

* Windows Sockets errors redefined as regular Berkeley error constants

*/

#define EWOULDBLOCK WSAEWOULDBLOCK

#define EINPROGRESS WSAEINPROGRESS

#define EALREADY WSAEALREADY

#define ENOTSOCK WSAENOTSOCK

#define EDESTADDRREQ WSAEDESTADDRREQ

#define EMSGSIZE WSAEMSGSIZE

#define EPROTOTYPE WSAEPROTOTYPE

#define ENOPROTOOPT WSAENOPROTOOPT

#define EPROTONOSUPPORT WSAEPROTONOSUPPORT

#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT

#define EOPNOTSUPP WSAEOPNOTSUPP

#define EPFNOSUPPORT WSAEPFNOSUPPORT

#define EAFNOSUPPORT WSAEAFNOSUPPORT

#define EADDRINUSE WSAEADDRINUSE

#define EADDRNOTAVAIL WSAEADDRNOTAVAIL

#define ENETDOWN WSAENETDOWN

#define ENETUNREACH WSAENETUNREACH

#define ENETRESET WSAENETRESET

#define ECONNABORTED WSAECONNABORTED

#define ECONNRESET WSAECONNRESET

#define ENOBUFS WSAENOBUFS

#define EISCONN WSAEISCONN

#define ENOTCONN WSAENOTCONN

#define ESHUTDOWN WSAESHUTDOWN

#define ETOOMANYREFS WSAETOOMANYREFS

#define ETIMEDOUT WSAETIMEDOUT

#define ECONNREFUSED WSAECONNREFUSED

#define ELOOP WSAELOOP

#define ENAMETOOLONG WSAENAMETOOLONG

#define EHOSTDOWN WSAEHOSTDOWN

#define EHOSTUNREACH WSAEHOSTUNREACH

#define ENOTEMPTY WSAENOTEMPTY

#define EPROCLIM WSAEPROCLIM

#define EUSERS WSAEUSERS

#define EDQUOT WSAEDQUOT

#define ESTALE WSAESTALE

#define EREMOTE WSAEREMOTE

/* Socket function prototypes */

#ifdef __cplusplus

extern "C" {

#endif

SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr,

int FAR *addrlen);

int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen);

int PASCAL FAR closesocket (SOCKET s);

int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen);

int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp);

int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name,

int FAR * namelen);

int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name,

int FAR * namelen);

int PASCAL FAR getsockopt (SOCKET s, int level, int optname,

char FAR * optval, int FAR *optlen);

u_long PASCAL FAR htonl (u_long hostlong);

u_short PASCAL FAR htons (u_short hostshort);

unsigned long PASCAL FAR inet_addr (const char FAR * cp);

char FAR * PASCAL FAR inet_ntoa (struct in_addr in);

int PASCAL FAR listen (SOCKET s, int backlog);

u_long PASCAL FAR ntohl (u_long netlong);

u_short PASCAL FAR ntohs (u_short netshort);

int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags);

int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags,

struct sockaddr FAR *from, int FAR * fromlen);

int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,

fd_set FAR *exceptfds, const struct timeval FAR *timeout);

int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags);

int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags,

const struct sockaddr FAR *to, int tolen);

int PASCAL FAR setsockopt (SOCKET s, int level, int optname,

const char FAR * optval, int optlen);

int PASCAL FAR shutdown (SOCKET s, int how);

SOCKET PASCAL FAR socket (int af, int type, int protocol);

/* Database function prototypes */

struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr,

int len, int type);

struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);

int PASCAL FAR gethostname (char FAR * name, int namelen);

struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto);

struct servent FAR * PASCAL FAR getservbyname(const char FAR * name,

const char FAR * proto);

struct protoent FAR * PASCAL FAR getprotobynumber(int proto);

struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name);

/* Microsoft Windows Extension function prototypes */

int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);

int PASCAL FAR WSACleanup(void);

void PASCAL FAR WSASetLastError(int iError);

int PASCAL FAR WSAGetLastError(void);

BOOL PASCAL FAR WSAIsBlocking(void);

int PASCAL FAR WSAUnhookBlockingHook(void);

FARPROC PASCAL FAR WSASetBlockingHook(FARPROC lpBlockFunc);

int PASCAL FAR WSACancelBlockingCall(void);

HANDLE PASCAL FAR WSAAsyncGetServByName(HWND hWnd, u_int wMsg,

const char FAR * name,

const char FAR * proto,

char FAR * buf, int buflen);

HANDLE PASCAL FAR WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,

const char FAR * proto, char FAR * buf,

int buflen);

HANDLE PASCAL FAR WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,

const char FAR * name, char FAR * buf,

int buflen);

HANDLE PASCAL FAR WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,

int number, char FAR * buf,

int buflen);

HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,

const char FAR * name, char FAR * buf,

int buflen);

HANDLE PASCAL FAR WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,

const char FAR * addr, int len, int type,

const char FAR * buf, int buflen);

int PASCAL FAR WSACancelAsyncRequest(HANDLE hAsyncTaskHandle);

int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,

long lEvent);

#ifdef __cplusplus

}

#endif

/* Microsoft Windows Extended data types */

typedef struct sockaddr SOCKADDR;

typedef struct sockaddr *PSOCKADDR;

typedef struct sockaddr FAR *LPSOCKADDR;

typedef struct sockaddr_in SOCKADDR_IN;

typedef struct sockaddr_in *PSOCKADDR_IN;

typedef struct sockaddr_in FAR *LPSOCKADDR_IN;

typedef struct linger LINGER;

typedef struct linger *PLINGER;

typedef struct linger FAR *LPLINGER;

typedef struct in_addr IN_ADDR;

typedef struct in_addr *PIN_ADDR;

typedef struct in_addr FAR *LPIN_ADDR;

typedef struct fd_set FD_SET;

typedef struct fd_set *PFD_SET;

typedef struct fd_set FAR *LPFD_SET;

typedef struct hostent HOSTENT;

typedef struct hostent *PHOSTENT;

typedef struct hostent FAR *LPHOSTENT;

typedef struct servent SERVENT;

typedef struct servent *PSERVENT;

typedef struct servent FAR *LPSERVENT;

typedef struct protoent PROTOENT;

typedef struct protoent *PPROTOENT;

typedef struct protoent FAR *LPPROTOENT;

typedef struct timeval TIMEVAL;

typedef struct timeval *PTIMEVAL;

typedef struct timeval FAR *LPTIMEVAL;

/*

* Windows message parameter composition and decomposition

* macros.

*

* WSAMAKEASYNCREPLY is intended for use by the Windows Sockets implementation

* when constructing the response to a WSAAsyncGetXByY routine.

*/

#define WSAMAKEASYNCREPLY(buflen,error) MAKELONG(buflen,error)

/*

* WSAMAKESELECTREPLY is intended for use by the Windows Sockets implementation

* when constructing the response to WSAAsyncSelect.

*/

#define WSAMAKESELECTREPLY(event,error) MAKELONG(event,error)

/*

* WSAGETASYNCBUFLEN is intended for use by the Windows Sockets application

* to extract the buffer length from the lParam in the response

* to a WSAGetXByY.

*/

#define WSAGETASYNCBUFLEN(lParam) LOWORD(lParam)

/*

* WSAGETASYNCERROR is intended for use by the Windows Sockets application

* to extract the error code from the lParam in the response

* to a WSAGetXByY.

*/

#define WSAGETASYNCERROR(lParam) HIWORD(lParam)

/*

* WSAGETSELECTEVENT is intended for use by the Windows Sockets application

* to extract the event code from the lParam in the response

* to a WSAAsyncSelect.

*/

#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)

/*

* WSAGETSELECTERROR is intended for use by the Windows Sockets application

* to extract the error code from the lParam in the response

* to a WSAAsyncSelect.

*/

#define WSAGETSELECTERROR(lParam) HIWORD(lParam)

#endif /* _WINSOCKAPI_ */

+$#A Notes for Windows Sockets Suppliers

+$#A Introduction to Notes for Windows Sockets Suppliers

A WINSOCK.DLL or WINSOCK32.DLL must implement ALL the functionality described in the Windows Sockets documentation.

Windows Sockets version 1.1 implementations must support both TCP and UDP type sockets. An implementation may support raw sockets (of type SOCK_RAW), but their use is deprecated.

Certain functions documented in this Help file have special notes for Windows Sockets implementors. A Windows Sockets implementation shoul conform to the function as documented. The special notes are provided for assistance and clarification.

+$#A Windows Sockets Components

+$#A Development Components

The Windows Sockets development components for use by Windows Sockets application developers will be provided by each Windows Sockets supplier. These Windows Sockets development components are:

Component | Description
Windows Sockets Documentation | This document
WINSOCK.LIB file | Windows Sockets API Import Library
WINSOCK.H file | Windows Sockets Header File
NETDB.H file | Berkeley Compatible Header File
ARPA/INET.H file | Berkeley Compatible Header File
SYS/TIME.H file | Berkeley Compatible Header File
SYS/SOCKET.H file | Berkeley Compatible Header File
NETINET/IN.H file | Berkeley Compatible Header File

#Run-Time Components

The run-time component provided by each Windows Sockets supplier is:

Component | Description
WINSOCK.DLL | The Windows Sockets API implementation DLL

+$#A Multithreadedness and Blocking Routines

Data areas returned by, for example, the functions in the getXbyY family (gethostbyaddr, and so on) MUST be on a per-thread basis.

Note that an application must be prevented from making multiple nested Windows Sockets function calls. Only one outstanding function call will be allowed for a particular task. Any Windows Sockets call performed when an existing blocking call is already outstanding will fail with an error code of WSAEINPROGRESS. There are two exceptions to this restriction: WSACancelBlockingCall and WSAIsBlocking may be called at any time. Windows Sockets suppliers should note that although preliminary drafts of this specification indicated that the restriction only applied to blocking function calls, and that it would be permissible to make nonblocking calls while a blocking call was in progress, this is no longer true.

Regarding the implementation of blocking routines, the solution in Windows Sockets is to simulate the blocking mechanism by having each routine call PeekMessage as it waits for the completion of its operation. In anticipation of this, the function WSASetBlockingHook is provided to allow you to define a special routine to be called instead of the default PeekMessage loop.

+$#A Database Files Implementation

The database routines in the getXbyY family (gethostbyaddr, and so on.) were originally designed (in the first Berkeley UNIX releases) as mechanisms for looking up information in text databases. A Windows Sockets supplier may choose to employ local files OR a name service to provide some or all of this information. If local files exist, the format of the files must be identical to that used in BSD UNIX, allowing for the differences in text file formats.

+$#A FD_ISSET

It is necessary to implement the FD_ISSET Berkeley macro using a supporting function: __WSAFDIsSet. It is the responsibility of a Windows Sockets implementation to make this available as part of the Windows Sockets API. Unlike the other functions exported by a Windows Sockets DLL, however, this function is not intended to be invoked directly by Windows Sockets applications: It should be used only to support the FD_ISSET macro. The source code for this function is:

int FAR

__WSAFDIsSet(SOCKET fd, fd_set FAR *set)

{

int i = set->fd_count;

while (i--)

if (set->fd_array[i] == fd)

return 1;

return 0;

}

+$#A Error Codes Returned by WSAGetLastError

To avoid conflict between various compiler environments, Windows Sockets implementations MUST return the error codes listed in the API specification, using the manifest constants beginning with "WSA". The Berkeley-compatible error code definitions are provided solely for compatibility purposes for applications being ported from other platforms.

+$#A DLL Ordinal Numbers

The WINSOCK.DEF file for use by every Windows Sockets implementation is as follows. Ordinal values starting at 1000 are reserved for Windows Sockets implementors to use for exporting private interfaces to their DLLs. A Windows Sockets implementation must not use any ordinals 999 and below except for those functions listed following. An application that is to work with any Windows Sockets DLL must use only those routines listed follwoing; using a private export makes an application dependent on a particular Windows Sockets implementation.

;

; File: winsock.def

; System: MS-Windows 3.x

; Summary: Module definition file for Windows Sockets DLL.

;

LIBRARY WINSOCK ; Application's module name

DESCRIPTION 'BSD Socket API for Windows'

EXETYPE WINDOWS ; required for all windows applications

STUB 'WINSTUB.EXE' ; generates error message if application

; is run without Windows

;CODE can be FIXED in memory because of potential upcalls

CODE PRELOAD FIXED

;DATA must be SINGLE and at a FIXED location since this is a DLL

DATA PRELOAD FIXED SINGLE

HEAPSIZE 1024

STACKSIZE 16384

; All functions that will be called by any Windows routine

; must be exported. Any additional exports beyond those defined

; here must have ordinal numbers 1000 or above.

EXPORTS

accept @1

bind @2

closesocket @3

connect @4

getpeername @5

getsockname @6

getsockopt @7

htonl @8

htons @9

inet_addr @10

inet_ntoa @11

ioctlsocket @12

listen @13

ntohl @14

ntohs @15

recv @16

recvfrom @17

select @18

send @19

sendto @20

setsockopt @21

shutdown @22

socket @23

gethostbyaddr @51

gethostbyname @52

getprotobyname @53

getprotobynumber @54

getservbyname @55

getservbyport @56

gethostname @57

WSAAsyncSelect @101

WSAAsyncGetHostByAddr @102

WSAAsyncGetHostByName @103

WSAAsyncGetProtoByNumber @104

WSAAsyncGetProtoByName @105

WSAAsyncGetServByPort @106

WSAAsyncGetServByName @107

WSACancelAsyncRequest @108

WSASetBlockingHook @109

WSAUnhookBlockingHook @110

WSAGetLastError @111

WSASetLastError @112

WSACancelBlockingCall @113

WSAIsBlocking @114

WSAStartup @115

WSACleanup @116

__WSAFDIsSet @151

WEP @500 RESIDENTNAME

;eof

+$#A Validation Suite

The Windows Sockets API Tester (WSAT) to ensure Windows Sockets compatibility between Windows Sockets DLL implementations is currently in beta test. This beta version includes functionality testing of the Windows Sockets interface and is supported by a comprehensive scripting language. The final version of WSAT will be available in Spring 1993. If you wish to receive the tester or more information on the beta, send email to wsat@microsoft.com.

+$#A For Further Reference

This documentation is intended to cover the Windows Sockets interface to TCP/IP in detail. Many details of TCP/IP and Windows, however, are intentionally omitted in the interest of brevity, and this specification often assumes background knowledge of these topics. For more information, the following references may be helpful:

Braden, R.[1989], RFC 1122, Requirements for Internet Hosts ⎯ Communication Layers, Internet Engineering Task Force.

Comer, D. [1991], Internetworking with TCP/IP Volume I: Principles, Protocols, and Architecture, Prentice Hall, Englewood Cliffs, New Jersey.

Comer, D. and Stevens, D. [1991], Internetworking with TCP/IP Volume II: Design, Implementation, and Internals, Prentice Hall, Englewood Cliffs, New Jersey.

Comer, D. and Stevens, D. [1991], Internetworking with TCP/IP Volume III: Client-Server Programming and Applications, Prentice Hall, Englewood Cliffs, New Jersey.

Leffler, S. et al., An Advanced 4.3BSD Interprocess Communication Tutorial.

Petzold, C. [1992], Programming Windows 3.1, Microsoft Press, Redmond, Washington.

Stevens, W.R. [1990], Unix Network Programming, Prentice Hall, Englewood Cliffs, New Jersey.

+$#A Background Information

+$#A Legal Status of Windows Sockets

The copyright for the Windows Sockets specification is owned by the specification authors listed on the title page. Permission is granted to redistribute this specification in any form, provided that the contents of the specification are not modified. Windows Sockets implementors are encouraged to include this specification with their product documentation.

The Windows Sockets logo on the title page of this document is meant for use on both Windows Sockets implementations and for applications that use the Windows Sockets interface. Use of the logo is encouraged on packaging, documentation, collateral, and advertising. The logo is available on microdyne.com in pub/winsock as WINSOCK.BMP. The suggested color for the logo's title bar is blue, the electrical socket grey, and the text and outline black.

UNIX is a trademark of Unix System Laboratories, Inc.

VAX is a trademark of Digital Equipment Corporation.

Windows is a trademark of Microsoft Corporation

68000 is a trademark of Motorola, Inc.

NT and Windows/NT are trademarks of Microsoft Corporation.

+$#A Windows Sockets for Windows NT

Microsoft® Windows NT™ fully supports the Windows Sockets functions as defined in the Win32 SDK (Software Development Kit). However, Windows NT has some additional features available through its Windows Sockets interface that are not described in the Windows Sockets specification. This document describes the extended features of the Windows Sockets interface in Windows NT, a superset of the Windows Sockets functionality.

Some of the features described in this document were not implemented in the initial release of Windows NT but are available in Windows NT version 3.5. The most notable new features are the SO_SNDTIMEO, SO_RCVTIMEO, and SO_OPENTYPE socket options and support for NetBIOS providers.

+$#A Support for 32-bit Applications

Included with the Windows NT release disks is the WINSOCK.DLL file, which provides the functions necessary for 16-bit applications to use Windows Sockets through 16-bit subsystem of Windows NT. Win32-based applications use a different DLL, WSOCK32.DLL, which exports routines that take exclusively 32-bit arguments.

The 32-bit versions of the Windows Sockets functions take all the same parameters, with the notable exception that all int and SOCKET parameters are widened from 16 bits to 32 bits. This widening is of no concern if you are writing applications to be portable between 16-bit and 32-bit environments, since the compiler performs the necessary steps automatically.

+$#A Windows NT Socket Handles

From a developer's standpoint, perhaps the most notable aspect of the Windows Sockets support in Windows NT is that the handles returned from the socket and accept functions are native Windows NT system handles that have been opened, by default, as "asynchronous" or "overlapped" handles. This allows an application to use the handles as arguments to system routines in addition to the Windows Sockets routines and also to share the handles between threads and processes.

+$#A Sharing Sockets Between Threads

Because a handle value is global to all threads in a process, a handle can be accessed in any thread, even threads that did not open the handle. For example, one thread can call the socket function, which returns a socket handle. A different thread in the same process can make other Windows Sockets calls with the returned socket handle. No special action is required to enable the second thread to use the handle; it just needs to obtain the handle value.

It is also possible for multiple threads to access a single socket simultaneously, but an application that does this must synchronize important actions. For example, if one thread performs a bind operation on a socket at the same time another thread is performing a getsockname operation, then because of the preemptive scheduling of Windows NT there are no guarantees that the getsockname operation will execute before the bind operation. It could happen that getsockname returns WSAEINVAL because the socket is not yet bound.

Another important example of simultaneous access in multiple threads to a single socket is data transfer. An application can have one thread calling send on a socket and another thread calling recv with no adverse effects whatsoever. However, if multiple threads call send on a socket, there are no guarantees as to the order of data delivery. The only guarantee is that the data in each send operation will be sent as a contiguous chunk ⎯ there is no possibility of intermixing data from different send calls. The analagous situation exists for multiple simultaneous recv calls ⎯ there is no guarantee as to which will finish first, but the data returned to any given recv operation is contiguous.

Making multiple simultaneous calls from different threads will not in any way damage the internal state of the socket maintained by Windows Sockets. Windows Sockets internally synchronizes the socket state whenever it changes in any way. Therefore, you need not worry about synchronizing calls for the sake of preserving the internal state of Windows Sockets.

+$#A Sharing Sockets Between Processes

Handles in Windows NT can also be shared between processes. However, since each process has its own handle table, it is more work to share handles between processes than between threads. There are two mechanisms for sharing handles between processes: inheritance and duplication.

In inheritance, a process (called the "parent process") creates another process (called the "child process") using the Win32 function CreateProcess with the fInheritHandles parameter set to TRUE. This causes the system to map into the child's process table all the inheritable handles of the parent process. Note that while not all Win32 handles are inheritable (inheritance requires opening a handle in a special manner), all socket handles are inheritable.

Once you create the child process, it must be given the values of the handles it needs to access. When it has the handle values, it can pass the handles to Windows Sockets or other functions.

In duplication, a process can pass a handle to another existing process with the Win32 function DuplicateHandle. This function allows one process to map a handle into the process table of another process, although the specific handle value may be different in the receiving process.

When sharing sockets between processes, the same synchronization issues exist as when sharing sockets between threads. In addition, you must be aware of two other special properties of sharing sockets between processes.

First, since both processes have a view of the socket handle, both processes must call closesocket before the system closes the socket. The process that first calls closesocket only takes the socket out of its internal handle table. The first closesocket call does not affect the second process's access to the socket in any way. The second closesocket does the actual cleanup of the socket, including initiating the protocol-level virtual circuit termination if the socket is connected. If the socket sharing is really a "hand-off" from one process to another, it is best to close the socket in the first process immediately after the CreateProcess or DuplicateHandle call.

Second, some socket options, such as SO_LINGER, are kept on a per-process basis. Therefore, after a socket handle has been passed to a second process, changes in socket options by one process may not be reflected in the other process. For this reason it is advisable to set up any socket options that both processes will need before sharing the socket handle.

Many Unix® services that use sockets make use of child process creation and inheritance to handle multiple simultaneous clients. This is typically done by a "master" process creating a socket, calling listen on the socket, and using accept to accept incoming connections. Then this process creates a "slave" process that actually services the client while the master immediately closes the accepted socket. This is useful in Unix, where multiple threads are generally not available, and this algorithm will work in Windows NT. However, processes use more memory than threads. Context switches between processes use about twice the CPU instructions used in context switches between threads within a single process. In addition, threads can share information much more easily than processes, so it is usually easier in the long run to use threads rather than processes. For all these reasons, it is strongly recommended that you write a service that handles multiple clients simultaneously so that it uses one thread per client rather than one process per client. It is even better to use multiple clients per thread. This technique is discussed in Accessing Sockets Through the Win32 I/O Functions.

+$#A Using Sockets as Standard I/O Handles

A special case of sharing sockets between processes is using sockets as standard input, output, or errors for a child process created with CreateProcess. This is useful for causing a process to unwittingly read input from a socket and to write output to a socket. Such a child process may "know" absolutely nothing about sockets or even networks; it just reads from its standard input and writes to its standard output.

There are two key steps in making this possible. First, a socket must be opened for "synchronous" access (that is, not overlapped) with the SO_OPENTYPE socket option described following. Second, the socket must be connected with either the connect or accept Windows Sockets function calls. After these steps, making a socket handle accessible as a standard I/O handle is just like doing so for other handles: Specify the handle value in the hStdInput, hStdOutput, and/or hStdError fields of the STARTUPINFO structure passed to CreateProcess Win32 function. The created process will read from and write to the socket without ever knowing it is a socket.

+$#A Accessing Sockets Through the Win32 I/O Functions

Since Windows NT socket handles are system socket handles, it is possible to use Windows NT socket handles as parameters to several of the Win32 functions. The most useful Win32 functions for sockets are the ReadFile and WriteFile functions since these allow an application to perform aynchronous I/O on a handle by means of the lpOverlapped parameter. In overlapped I/O, the request is started with the initial call but the call does not block waiting for the request to be completed. For example, if an application makes an overlapped ReadFile call when no data exists to be read on a socket, the calling thread will regain control immediately rather than waiting for data to arrive on the socket. The application will be notified when the request has been completed via the lpOverlapped parameter.

Overlapped I/O differs from the socket concept of nonblocking I/O in that overlapped I/O actually performs the I/O eventually if it cannot be completed immediately. In nonblocking I/O, if the I/O cannot be performed immediately, then it fails with the error code WSAEWOULDBLOCK and the application must reissue the request at a later point.

Overlapped I/O is especially useful in a service that handles multiple sockets. A single thread can be used to perform I/O on all of the sockets without the overhead of the select call. Also, the use of overlapped I/O allows an application to mix different types of I/O in a single thread; for example, I/O on named pipes and I/O on sockets.

Because Windows NT socket handles are, by default, opened as overlapped handles, an application must specify the lpOverlapped parameter on calls to ReadFile and WriteFile. However, an application can also use the SO_OPENTYPE socket option to open sockets that are not overlapped.

+$#A Accessing Sockets Through the C Run-time Libraries

The C run-time libraries (CRTs) provide useful functionality for input and output formatting through routines such as fprintf, fscanf, and so on. It may also be useful to access socket handles through these routines in existing code that uses C run-time calls such as _read and _write.

The C run-time libraries take two different inputs to identify a file: a CRT handle (different from a system handle such as a socket) and a file pointer. The low-level I/O routines, such as _read, take CRT handles, while the more sophisticated formatting routines, such as fprintf, take file pointers.

To access sockets through the C run-time routines, an application must perform several steps:

1. Use the SO_OPENTYPE socket option described following so that any opened socket handles are synchronous.

2. Open a connected socket by calling either socket and connect or by calling accept. It is legal to use datagram sockets, although their inherent unreliability may make them unsuitable for C run-time access in many applications.

3. Obtain a CRT socket handle with the _open_osf_handle C run-time API. If the application intends to use only the low-level C run-time calls, the handle returned by this call is sufficient and the application is ready to make C run-time calls.

4. If the application wants to use the higher-level C run-time calls that do data formatting, the application must obtain a file pointer (FILE *) by using the _fdopen C run-time call.

+$#A Socket Options Specific to Windows NT

Windows NT supplies several socket options beyond those included in the Windows Sockets specification. Because these options are not currently part of the formal Windows Sockets specification, an application that uses these options is not compatible with Windows Sockets. However, for applications written exclusively for Windows NT, these options can be very useful.

+$#A SO_SNDTIMEO and SO_RCVTIMEO

These two options set up timeouts for the send, sendto, recv, and recvfrom functions. You can obtain the same functionality by calling select with a timeout just before the I/O call, but these options offer a significant improvement in performance by avoiding a kernel transition and the other overhead of the select call. For any code whose performance is very critical, applications should use these timeout options rather than select.

You can set these options on any type of socket in any state. The default value for these options is zero, which refers to an infinite timeout. Any other setting is the timeout, in milliseconds. It is legal to set the timeout to any value, but values less than 500 milliseconds (half a second) are interpreted to be 500 milliseconds.

To set a send timeout, use

int timeout = TIMEOUT_VALUE;

int err;

SOCKET s;

s = socket( ... );

err = setsockopt(

s,

SOL_SOCKET,

SO_SNDTIMEO,

(char *)&timeout,

sizeof(timeout));

if (err != NO_ERROR) {

/* failed for some reason... */

}

where TIMEOUT_VALUE is the needed timeout in milliseconds. To set a receive timeout, substitute SO_RCVTIMEO for SO_SNDTIMEO in the preceding example.

After setting one of these options to a nonzero value, I/O through the Windows Sockets calls fails with the error WSAETIMEDOUT if the request cannot be satisfied within the specified timeout. If a request times out, an application has no guarantees as to how much data was actually sent or received in the I/O call.

+$#A SO_OPENTYPE

By default, all socket handles are opened as overlapped handles so that asynchronous I/O can be performed on them. However, in many situations it is preferable to have nonoverlapped (synchronous) socket handles. For example, only nonoverlapped handles can be used with the C run-time libraries or used as standard I/O handles for a process. The SO_OPENTYPE socket option allows an application to open nonoverlapped socket handles.

The SO_OPENTYPE option must be set on a per-thread basis since it affects how each thread opens sockets, and it must be set before a socket handle is opened in order to affect the socket. The default value for this option is zero, which opens sockets as overlapped handles. Other possible values for this option are SO_SYNCHRONOUS_ALERT and SO_SYNCHRONOUS_NONALERT. SO_SYNCHRONOUS_ALERT allows operations performed on the socket handle to be alerted by an APC routine (software interrupt), while SO_SYNCHRONOUS_NONALERT blocks APCs from being delivered to threads waiting on requests on socket handles. Typically, it does not matter which of these options is chosen: either makes socket handles nonoverlapped.

Use the following to open a nonoverlapped socket handle:

SOCKET s;

int optionValue = SO_SYNCHRONOUS_NONALERT;

int err;

err = setsockopt(

INVALID_SOCKET,

SOL_SOCKET,

SO_OPENTYPE,

(char *)&optionValue,

sizeof(optionValue));

if (err != NO_ERROR) {

/* failed for some reason... */

}

s = socket( ... );

Note that the socket handle passed to setsockopt is ignored for this option.

The SO_OPENTYPE socket option affects both the socket and accept Windows Sockets calls. The setting of SO_OPENTYPE is preserved throughout the lifetime of the thread in which it is set, so it is not necessary to set its value before each socket opens. However, it is necessary to set this option for every thread that opens sockets if the nondefault behavior is desired. Also, if different layers of an application open sockets and have different expectations of the type of handles opened, it is advisable to use getsockopt before the setsockopt call to obtain the original setting of SO_OPENTYPE, then reset SO_OPENTYPE to the original value after opening the socket. For example,

SOCKET s;

int originalValue;

int optionValue = SO_SYNCHRONOUS_ALERT;

int err;

int optionLength = sizeof(originalValue);

/* Get the original setting of SO_OPENTYPE. */

err = getsockopt(

INVALID_SOCKET,

SOL_SOCKET,

SO_OPENTYPE,

(char *)&originalValue,

optionLength);

if (err != NO_ERROR) {

/* failed for some reason... */

}

/* Set SO_OPENTYPE so that we get a synchronous socket handle. */

err = setsockopt(

INVALID_SOCKET,

SOL_SOCKET,

SO_OPENTYPE,

(char *)&optionValue,

sizeof(optionValue));

if (err != NO_ERROR) {

/* failed for some reason... */

}

s = socket( ... );

/* Reset the SO_OPENTYPE setting to its original value. */

err = setsockopt(

INVALID_SOCKET,

SOL_SOCKET,

SO_OPENTYPE,

(char *)&optionValue,

sizeof(optionValue));

if (err != NO_ERROR) {

/* failed for some reason... */

}

This technique is especially useful in DLLs that use synchronous sockets.

There are some Windows Sockets features that you cannot use with synchronous sockets. The WSAAsyncSelect call cannot be used with synchronous sockets and will fail with the error WSAEINVAL. It is also not possible to set the SO_SNDTIMEO and SO_RCVTIMEO socket options on synchronous sockets; setsockopt with these options on synchronous sockets fails with WSAEINVAL as well.

+$#A Multiple Protocol Support

In addition to TCP/IP, Windows NT supports several additional transport protocols through its Windows Sockets interface. Although these protocols are not explicitly addressed in the Windows Sockets specification, the interface to Windows Sockets over different transport protocols is quite feasible. This is because the fundamental job of a transport protocol is data transfer, along with the fact that Windows Sockets provides a rich interface for low-level data transfer in applications. Having a common, well-known interface allows applications to be ported quickly between different transport protocols, and there are several successful examples of this.

Because of the architecture of the Windows Sockets component of Windows NT, all supported transport protocols are accessed through the same DLL, WSOCK32.DLL. In fact, a single application can simultaneously use sockets of different transport protocols.

An application uses the parameters to the socket function to specify the protocol it will use. For example, a TCP socket is opened with

s_tcp = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

while an SPX socket is opened with

s_spx = socket( AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX );

After opening a socket for a specific protocol, all actions performed on the socket go through the specified protocol. In the preceding example, if the application did a listen on the s_tcp socket but not on the s_spx socket, it would receive incoming connects through TCP but not receive incoming connects through SPX.

+$#A Protocol Characteristics

It is important to understand the different characteristics of transport protocols when designing an application that uses them. While all transport protocols share the same fundamental purpose of bidirectional data transfer, many details of the data transfer differ. The following sections list key characteristics of transport protocols that apply to Windows Sockets applications.

+$#A Addressing

The most obvious difference between different transport protocols is the way each uses transport addresses, called "socket addresses" in Windows Sockets terms. Virtually all transport protocols expose different address formats to applications, the one exception being NetBIOS protocols, which all use the same address format.

A number of Windows Sockets functions take socket addresses as input or output parameters, including bind, accept, sendto, recvfrom, getsockname, and getpeername. For all these functions a socket address is really two arguments: a pointer to the socket address structure itself and a length integer that gives the number of bytes in the socket address. For many transport protocols the length of a socket address is always the same, but socket address lengths can differ between transport protocols. For example, a TCP/IP socket address has 16 bytes and an IPX/SPX socket address has 14 bytes.

The format of socket address structures used by a transport protocol is specified in the af, or "address family" argument to the socket function. All TCP/IP sockets (including both TCP and UDP sockets) are opened with the AF_INET address family, while IPX/SPX sockets are opened with AF_IPX. Other transport protocols use different address family values as defined in the header file WINSOCK.H.

An application must fill in a socket address when binding to a local address or connecting to a remote address, and also to interpret the socket addresss returned from the accept, sendto, recvfrom, getpeername, and getsockname functions.

+$#A Connection-Oriented vs. Connectionless Transport

In a connection-oriented transport protocol, applications are required to establish a virtual circuit (sometimes abbreviated to "VC") before data transfer can take place. Virtual circuit establishment is assymetrical. One side, the server, must indicate to the transport protocol that it can receive incoming connections via the listen function. The other side, the client, initiates the circuit with the connect function, and the server can obtain a socket for the circuit with the accept function. Once the circuit is established, data transfer takes place with the send and recv functions. Protocol-level activity results from this circuit establishment, and more protocol activity tears down the circuit when the sockets are closed. TCP and SPX are examples of connection-oriented transport protocols.

In a connectionless transport protocol, circuit establishment is not required for data transfer. An application need only open and bind a socket, after which it can use the sendto and recvfrom functions to send and receive data. Of course, in order to specify the remote address for sending data or the address from which received data was sent, you must specify a socket address to these routines. UDP and IPX are examples of connectionless transport protocols.

It is possible to use the connect function on sockets opened for connectionless protocols. This is merely an application convenience, allowing the application to use the send and recv functions, and this does not result in any protocol activity. If a socket is connected in this manner, it will receive only packets sent from the connected address; other packets destined for the socket are silently discarded.

+$#A Reliable vs. Unreliable Data Delivery

Most physical networks are inherently unreliable. They can drop data, corrupt it, deliver it out of order, and deliver multiple copies of it. Some transport protocols hide this unreliability from applications by using protocol-level mechanisms to ensure that data is delivered in the correct order, without loss, corruption, or duplication. Other transport protocols do not make these guarantees; they simply make a best effort to get the data to its intended recipient, but physical network failures can lead to data integrity problems.

Because they require fewer system resources and less processing, unreliable protocols often provide better performance, especially for applications that send small amounts of data in a request-response (transaction) format. However, applications that use connectionless protocols must guarantee reliability, which makes applications more complicated. Reliable protocols are usually simpler for the application at the expense of a higher cost in system resources and performance.

Typically, connection-oriented protocols are reliable, while connectionless protocols are unreliable. While this is true of all the transport protocols supported by Windows NT, there are exceptions.

+$#A Terminating a Virtual Circuit

For a connection-oriented transport protocol, there are two ways to terminate a virtual circuit: orderly and abortive. In an orderly release, both sides get a chance to indicate that they have sent all the data they intend to send, and only when both sides have finished is the actual circuit terminated. For such a release, one side can use the shutdown function to indicate that it has finished sending, while the other side continues sending. In an abortive termination of a virtual circuit, one side decides that it is time to terminate the circuit and unilaterally ends the connection. If the other side attempts to use the connected socket, the request fails.

By default, closesocket also attempts an orderly release of the connection. However, under several circumstances, such as pending unreceived, outstanding send calls, and more, a closesocket will result in an abortive close.

All transport protocols support the concept of an abortive release, but not all protocols support orderly release. If an application attempts an orderly release on a transport protocol that does not support orderly release, the connection is terminated abortively. TCP supports orderly release, while SPX supports only abortive release.

+$#A Message-Oriented vs. Stream-Oriented

Connection-oriented transport protocols can be either message-oriented or stream-oriented. In a message-oriented transport protocol, each block of data is sent and received as a single block. An individual message is never broken down when received, although a transport protocol can be forced to break down a message if it is larger than the physical network's maximum transmission unit (MTU). However, this is transparent to the application.

In a stream-oriented transport protocol, data is sent and received as a continuous stream, without any message boundaries. The transport protocol is free to coalesce data from discrete send calls and deliver it to a single recv call, as well as to break down the data buffer given to a single send call and deliver it to several recv calls. An application cannot control how these breakdowns occur, and gets no notification of how data was sent. If an application needs to communicate in discrete messages, it must frame them, for example, by preceding message data with the length of the message until the specified length arrives.

Some message-oriented protocols support stream semantics in addition to message semantics. If an application opens a socket with SOCK_STREAM on such a transport protocol, then the transport ignores all message boundaries when transmitting and receiving data. It simply delivers data as it becomes available, regardless of whether the data is a complete message. This feature is handy for maximizing application portability. Note, however, that stream-oriented transport protocols cannot implement message-oriented semantics due to lack of information.

+$#A Expedited Data

Some connection-oriented transport protocols support expedited data, also referred to as "urgent data" or "out-of-band data." Data sent with the MSG_OOB flag in the send function is sent as expedited data, and the transport protocol makes every effort to deliver the data as quickly as possible. Often the data is delivered out of order so that it can get to the other application as quickly as possible. For maximum portability, avoid expedited data since it is not supported by all transport protocols.

+$#A Connect and Disconnect Data

A few transport protocols support connect and disconnect data, in which a small amount of data is sent by each side when a virtual circuit is established or terminated. This data can be anything, but is often negotiation information about the version of software connecting, and so on. Again, for protocol portability, avoid this feature since it is not universally supported.

+$#A Broadcasts

Most connectionless transport protocols support broadcasts in the same fashion, in which any bound socket can send a broadcast if the SO_BROADCAST option is set, and broadcasts sent to the appropriate local endpoint are received without any additional work on the part of the application. NetBIOS transport protocols, however, handle broadcasts somewhat differently. In order to receive broadcasts, an application must bind to the NetBIOS broadcast address, which is an asterisk ("*") followed by 15 blank spaces (ASCII character 0x20). This means two things: A socket must be specially bound to receive broadcasts, and applications cannot depend on receiving broadcasts intended only for a specific application, since all NetBIOS broadcasts are delivered to this address. In other protocols such as UDP/IP and IPX, broadcasts are delivered to a socket only if the broadcast was sent to the same port to which the socket was bound.

+$#A Extended Support for Message-Oriented Protocols

The Berkeley Software Distribution (BSD) sockets interface was originally designed to support primarily TCP/IP, and the Windows Sockets interface is based on BSD sockets. This interface is not sufficiently rich for message-oriented transport protocols, however, so Windows NT includes some minor extensions of the interface so that applications can fully exploit message-oriented transport protocols.

+$#A MSG_PARTIAL Flag

Under some circumstances, applications may not have available in a single, contiguous data buffer all the data that make up a single message to be sent. The message can be very large, for example, or can be composed of discrete parts in an application's virtual address space. To allow an application to send this message without having to allocate a buffer and copy all the data into the buffer, Windows NT allows the application to specify the MSG_PARTIAL bit in the flags parameter of the send function. This bit tells the transport protocol that the data in the send operation is just part of a larger message, and that the protocol should take whatever action is necessary to deliver the data as a partial message.

+$#A WSARecvEx Function

By default, for message-oriented transport protocols the Windows Sockets recv function receives a single message in each call to the API. This works fine for most cases, but there are two situations in which this is insufficient: when the application's data buffer is smaller than the message, and when the message is large and arrives in several pieces.

When the buffer is smaller than the data, as much of the message as will fit is copied into the user's buffer and recv returns with the error code WSAEMSGSIZE. A subsequent call to recv will get the next part of the message. Applications written for message-oriented transport protocols should be coded for this possibility if message sizing is not guaranteed by the application's data transfer protocol. If an application gets this error code, then the entire data buffer is filled with data.

It is more complicated when a very large message arrives a little at a time. For example, if an application sends a 1-megabyte message, the transport protocol must break up the message in order to send it over the physical network. It is theoretically possible for the transport protocol on the receiving side to buffer all the data in the message, but this would be quite expensive in terms of resources.

It would be better to allow a recv call to complete with only a partial message and some indication to the application that the data is only a partial message. However, the Windows Sockets recv function has only one output parameter: the number of bytes received. Therefore, Windows NT supplies the WSARecvEx function, which is identical to the recv function except that the flags parameter is an in-out parameter:

int

PASCAL

FAR

WSARecvEx (

SOCKET s,

char FAR * buf,

int len,

int *flags

);

On return from WSARecvEx, if a partial message was received, the MSG_PARTIAL bit is set in the flags parameter. If a complete message was received, MSG_PARTIAL is not set in flags. For a stream-oriented transport protocol, MSG_PARTIAL is never set on return from WSARecvEx; for stream transport protocols, this function behaves identically to the Windows Sockets recv function.

+$#A WSAEDISCON Error Code

After a virtual circuit is gracefully terminated on a stream-oriented transport protocol and all data has been received, the recv and WSARecvEx functions return zero to indicate that there will never be any more data to read on the connection. However, most message-oriented transport procotols support the concept of a zero-length message. Applications therefore need a mechanism to distinguish a zero-length message and graceful circuit termination.

The Windows Sockets interface in Windows NT uses the WSAEDISCON error code to solve this problem. When the circuit is gracefully terminated, this error code is returned to the application, and a return value of zero is used to indicate a message of length zero.

Following is an example of client-side code that establishes an SPX connection, and receives and processes messages until the connection is terminated.

SOCKET s;

SOCKADDR_IPX sockaddrIpx;

INT err;

INT bytesReceived;

INT flags;

/* open the SPX socket handle */

s = socket( AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX );

/* initialize the sockaddr for the remote machine */

sockaddrIpx.sa_family = AF_IPX;

sockaddrIpx.sa_netnum = 0;

sockaddrIpx.sa_nodenum = 123; /* substitute real nodenum here */

sockaddrIpx.sa_socket = 1; /*substitute real socket number here */

/* connect the socket */

err = connect(

s,

(PSOCKADDR)&sockaddrIpx,

sizeof(sockaddrIpx) );

if ( err != 0 ) {

/* indicate connection failure */

}

/* receive data until the connection terminates */

while ( TRUE ) {

/* use WSARecvEx to receive data */

flags = 0;

bytesReceived = WSARecvEx( s, buffer, buflen, &flags );

/* check for error or graceful termination */

if ( bytesReceived < 0 ) {

if ( WSAGetLastError( ) == WSAEDISCON ) {

/* graceful termination */

break;

}

/* some other error, indicate to user */

break;

}

/* check for partial message */

if ( (flags & MSG_PARTIAL) != 0 ) {

/* a partial message; handle specially */

}

/* check for 0-length message */

if ( bytesReceived == 0 ) {

/* 0-length message, process */

} else {

/* message contained data */

}

}

/* circuit terminated or error, close socket */

closesocket( s );

+$#A Windows NT Support for Connect and Disconnect Data

Some transport protocols, such as DECNet and OSI TP4, support the concept of connect and disconnect data. This is additional data, not in the normal data stream, that is sent on the wire along with connect or disconnect requests and is typically used for things like application-level version negotiation. The TDI interface provides support for connect and disconnect data through the TDI_CONNECTION_INFORMATION structure, which has fields for UserDataLength, UserData, OptionsLength, and Options and is passed on TDI connect, accept, and disconnect requests.

The sockets interface, however, does not provide input parameters for connect data on the connect function or for disconnect data on the shutdown and closesocket functions. However, it is relatively natural to use the setsockopt function to specify connect and disconnect data to send, and to use the getsockopt function to retrieve connect and disconnect data sent by the remote side. To support this, the following socket options are included with the Windows NT version of the Windows Sockets header file (WINSOCK.H):

/*

* Options for connect and disconnect data and options. Used only by

* non-TCP/IP transports such as DECNet, OSI TP4, etc.

*/

#define SO_CONNDATA 0x7000

#define SO_CONNOPT 0x7001

#define SO_DISCDATA 0x7002

#define SO_DISCOPT 0x7003

#define SO_CONNDATALEN 0x7004

#define SO_CONNOPTLEN 0x7005

#define SO_DISCDATALEN 0x7006

#define SO_DISCOPTLEN 0x7007

How an application uses these options in conjunction with getsockopt and setsockopt depends on the type of application, server or client, and how it will use connect data. Each case is explained following.

Connect and disconnect options are semantically the same as connect and disconnect data, from the standpoint of the Windows Sockets interface. The "options" are really just a buffer of data that is passed to the transport protocol prior to the connect or disconnect, and a buffer that is retrieved following a connect or disconnect. Therefore, an application uses connect or disconnect options in much the same manner it uses connect or disconnect data. The only difference is the option names used on the getsockopt and setsockopt calls; for example, use SO_CONNOPT in place of SO_CONNDATA.

+$#A Client Applications and Connect Data

A Windows Sockets client application written for a transport protocol that supports connect data should call setsockopt with SOL_SOCKET as the level, SO_CONNDATA as the option name, and a buffer in the option value field that contains the connect data it intends to send. This call must be made before calling the connect function. The specified connect data is then sent on the connect request.

Two steps are required to retrieve response connect data. First, before the connect call, the application must inform the Windows Sockets DLL how much space to reserve for the response connect data. This is done by calling setsockopt with option level SOL_SOCKET, option name SO_CONNDATALEN, and having the option value parameter point to an integer that contains the maximum number of connect data bytes expected. The Windows Sockets DLL will set aside a buffer of this size to contain connect data sent by the server application.

After the connect has completed successfully, the application can call getsockopt with an option level of SOL_SOCKET, an option name of SO_CONNDATA, and a buffer in the option value parameter large enough to hold the response connect data. The Windows Sockets DLL then copies the response connect data into the caller's buffer.

Note that the Windows Sockets DLL keeps the response connect data buffer valid for the lifetime of the socket. Therefore, an application can retrieve the response connect data for the socket at any time after the socket is connected and before it is closed.

+$#A Server Applications and Connect Data

A server application's use of connect data implicitly differs in two important ways from a client application's use. First, a server application receives the client's connect data before it sends its own. Second, because the socket's interface does not support the concept of delayed accept, a server application is limited to sending the same response connect data to all clients.

A server application that is to receive connect data should use setsockopt with the SO_CONNDATALEN option to force the Windows Sockets DLL to reserve buffer space into which to receive connect data for incoming connect requests. The specified amount of connect data buffer space is used for all incoming connects, but each connect gets a separate buffer. After a new connected socket is accepted with accept the application calls getsockopt with the SO_CONNDATA option and a buffer large enough to hold expected response data. The Windows Sockets DLL fills in this buffer with the connect data sent by the client.

To send connect data, a server application calls setsockopt with the SO_CONNDATA option and a buffer of data before incoming connections are accepted (typically before listen). Thereafter, any client that connects to the server will receive the specified connect data. The connect data sent by a server can be changed at any time with another setsockopt call, which discards the previous data and replaces it with new connect data.

+$#A Disconnect Data

Client and server applications send and receive disconnect data in a manner similar to the way a client application sends and receives connect data. To send disconnect data the application calls setsockopt with the SO_DISCDATA option and a buffer of disconnect data to send. The data will then be sent either when the application calls shutdown to disconnect the send side of the socket or when the application calls closesocket on the socket.

To receive disconnect data, an application must call setsockopt with the SO_DISCDATALEN option before a remote closes the connection. This option specifies the amount of disconnect data the application expects to receive.

After the remote has disconnected, the SO_DISCDATA option on getsockopt retrieves the disconnect data sent by the remote. The application passes in a buffer large enough to hold all the disconnect data, and the Windows Sockets DLL copies the received disconnect data into the buffer.

+$#A Additional Items

The following topics provide details on additional items in Windows Sockets.

• Blocking Sockets Calls

• WSAGetLastError vs. GetLastError

• Closing Sockets: Graceful vs. Abortive

• Notes on Performance

• Windows NT Extensions to WINSOCK.H

+$#A Blocking Sockets Calls

The Windows Sockets specification defines a "blocking hook" mechanism, accessed through the WSASetBlockingHook, WSAUnhookBlockingHook, WSAIsBlocking, and WSACancelBlockingCall functions. The blocking hook works by polling for Windows messages while a Windows Sockets function call is blocking on network activity and calling back into the application when a message arrives. This mechanism allows a measure of multitasking to occur within the nonpreemptive Windows environment. In fact, an application that makes blocking calls gets a "default blocking hook" installed, which can result in reentrance to the application if the application makes any calls that must block.The blocking hook is useful for promoting multitasking, but Windows NT, being a fully preemptive, multitasking operating system, doesn't need any help from applications to do multitasking. Additionally, programming Windows applications that use the blocking is complex, requiring special attention to reentrance issues. Therefore, 32-bit applications for Windows NT do not get a default blocking hook installed. If a thread makes a blocking call, the thread simply blocks, and the system schedules other threads as appropriate, waking up the blocked thread when the I/O completes.

If a 32-bit application really does require the blocking hook semantics, such an application can use WSASetBlockingHook to install any blocking hook on a per-thread basis. This blocking hook will operate exactly as specified in the Windows Sockets specification. Note that the fact that the blocking hook is per-thread means two things for multithreaded applications: If they need blocking hooks, they must install a blocking hook for every thread that makes blocking Windows Sockets calls; and they can install a different blocking hook in different threads of their process, allowing different blocking hook behavior in different threads.

If a 16-bit application is ported to Win32 and depends on the behavior of the default blocking hook, the application can install as a blocking hook code that performs the same actions as the default blocking hook:

BOOL

DefaultBlockingHook (

VOID

)

{

MSG msg;

BOOLEAN retrievedMessage;

//

// Get the next message, if any.

//

retrievedMessage = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

//

// Process the message if we got one.

//

if (retrievedMessage) {

TranslateMessage((CONST MSG *)&msg);

DispatchMessage((CONST MSG *)&msg);

}

//

// If we got a message, indicate that we want to be called again.

//

return retrievedMessage;

} // DefaultBlockingHook

Note that 16-bit Windows Sockets applications running in the 16-bit subsystem of Windows NT do get the default blocking hook semantics as described in the Windows Sockets specification, so there is no need to modify 16-bit applications to take advantage of the blocking hook behavior.

+$#A WSAGetLastError vs. GetLastError

Windows Sockets applications use the WSAGetLastError function to retrieve specific error information on failed function calls. For 32-bit applications under Windows NT, the WSAGetLastError function is just a call through to theWin32 GetLastError call, which provides identical functionality. Therefore, if an application makes a Windows Sockets call that fails, then a Win32 function call that also fails, the result of WSAGetLastError will give the error code for the Win32 function failure. In addition, some C run-time calls, specifically printf, reset the thread's last error to zero, so a call to WSAGetLastError after one of these function calls can return the value zero rather than the actual failure code. It is strongly recommended that applications be written to call WSAGetLastError (or GetLastError) immediately after a failed Windows Sockets call is detected without any intervening function calls.

Note also that WSASetLastError is just a call through to SetLastError, so calling WSASetLastError will reset the error obtained by GetLastError.

+$#A Closing Sockets: Graceful vs. Abortive

For transport protocols that support graceful close, Windows NT has been carefully designed to perform graceful closes when appropriate, and to perform abortive close operations when appropriate. By default, closing a connected socket with closesocket results in a graceful close of the virtual circuit. In addition, using shutdown on the send side of a socket initiates a graceful close. If an application is to force an abortive close, it should set the SO_LINGER option on the socket to have a timeout of zero.

Windows NT uses an abortive close to indicate to the remote end that a connection was terminated in an ungraceful manner. There are several causes for this:

• Closing a socket when there is unreceived data buffered on the socket.

• Exiting a process without calling closesocket on all connected sockets.

• Data arrives after the graceful close has been initiated.

• The SO_LINGER option is nonzero, but some data remains unsent after the timeout expires.

To ensure a graceful close on a virtual circuit, an application must receive all data on the socket and manually call closesocket on the socket. To do this properly, some application protocols require the shutdown function to initiate graceful close in conjunction with recv or WSARecvEx to ensure that all data sent by the remote is received locally.

+$#A Notes on Performance

Application performance can be tuned so that data can be transferred as quickly as possible with minimal burden on the system CPU. There are several general guidelines that can help you tune your code. Many of these simple guidelines will seem obvious, but it is surprising how often they are overlooked.

First, send and receive data in chunks as large as possible, up to approximately 64 kilobytes. There is a certain amount of unavoidable overhead in each send or recv call, and sending and receiving large chunks of data spreads the overhead over fewer calls.

Second, do as little in performance paths as possible. Do not include any extraneous calls, either Windows Sockets or Win32. If you need to send a lot of data, do it in a tight loop without any calls other than send. If you must specify a timeout on the send, do not use the select function, which has the inherent overhead of a kernel transition. Instead, set the SO_SNDTIMEO option on the socket and use send only. If you're handling several sockets in your application, use threads so that you can avoid calls to select (but be careful not to use too many threads ⎯ threads cost memory and introduce context switching overhead).

It is possible to achieve very high data transfer rates with the Windows Sockets interface in Windows NT. With TCP/IP on a 10-megabit-per-second ethernet, we have seen rates of more than a megabyte per second, which is close to the theoretical maximum for the medium, and the system CPUs were not fully saturated. This demonstrates that in a carefully coded and tuned application it is possible to achieve excellent performance through Windows Sockets.

+$#A Windows NT Extensions to WINSOCK.H

To support some of the features unique to Windows NT described in this document, the WINSOCK.H header file that is included with the Win32 SDK contains some additional information, as shown:

/*

* Options for connect and disconnect data and options. Used only by

* non-TCP/IP transports such as DECNet, OSI TP4, etc.

*/

#define SO_CONNDATA 0x7000

#define SO_CONNOPT 0x7001

#define SO_DISCDATA 0x7002

#define SO_DISCOPT 0x7003

#define SO_CONNDATALEN 0x7004

#define SO_CONNOPTLEN 0x7005

#define SO_DISCDATALEN 0x7006

#define SO_DISCOPTLEN 0x7007

/*

* Option for opening sockets for synchronous access.

*/

#define SO_OPENTYPE 0x7008

#define SO_SYNCHRONOUS_ALERT 0x10

#define SO_SYNCHRONOUS_NONALERT 0x20

#define WSAEDISCON (WSABASEERR+101)

int PASCAL FAR WSARecvEx (SOCKET s, char FAR * buf, int len, int *flags);

+$#A Windows Sockets Service Registration and Name Resolution

The service registration and resolution functionality helps you write client server applications. Using this functionality, a service can register its existence with a name space (registration), and a client can find a service within a name space (resolution).

+$#A Service Registration and Resolution Definitions

The following functions and structures let you implement service registration and resolution functionality.

+$#A Service Registration and Resolution Functions

Use these functions to implement service registration and resolution functionality:

EnumProtocols
GetAddressByName
GetNameByType
GetService
GetTypeByName
SetService

+$#A Service Registration and Resolution Structures

Use these structures to implement service registration and resolution functionality:

BLOB
CSADDR_INFO
NS_SERVICE_INFO
PROTOCOL_INFO
SERVICE_ADDRESS
SERVICE_ADDRESSES
SERVICE_INFO
SERVICE_TYPE_INFO_ABS
SERVICE_TYPE_VALUE_ABS

+$#A Introduction to Audio

Microsoft® Windows® provides several different types and levels of audio services. You should become familiar with these types and levels so you can choose the services appropriate for your application. This overview assumes you have a basic knowledge of digital audio and Musical Instrument Digital Interface (MIDI).

+$#A About Audio Services

This overview covers the following topics:

• Types of audio services

• Levels of audio services

• Windows audio architecture

• Audio file formats

• Further reading

+$#A Types of Audio Services

Different types of audio services require different formats for the audio information and different technologies to reproduce audio. Windows offers the following types of audio services:

• Waveform audio services provide playback and recording support for digital audio hardware. Waveform audio is useful for reproducing nonmusical audio material such as sound effects and voice-over narration. Waveform audio has moderate storage-space and data-transfer rate requirements ⎯ as low as 11 kilobytes per second of audio.

• MIDI audio services provide support for MIDI-file and MIDI-event playback (through internal or external synthesizers) and MIDI-event recording. MIDI audio is useful with music-related applications such as music composition and MIDI sequencer programs. Because they require less storage space and a lower data-transfer rate than waveform audio, you may want to use MIDI audio services to provide introductory and background music in your applications.

• Compact disc (CD) audio services provide support for playback of Red Book audio information on CDs by means of the CD-ROM drive on multimedia computers. CD audio offers the highest quality reproduction of musical material but has the highest storage-space requirements ⎯ 176 kilobytes per second of audio. You cannot read from the CD-ROM drive while playing CD audio.

+$#A Levels of Audio Services

The different levels of audio services let you choose services appropriate to the requirements of your application and to your programming ability. Windows offers two levels of audio services:

• High-level audio services allow you to play and record audio files by using as little as one function call. Compared with low-level audio services, high-level services are easier to use and require less programming. High-level services also meet the audio requirements of most applications.

• Low-level audio services allow you to communicate directly with audio device drivers to manage playback and recording. Low-level audio services require more programming than high-level services but give you more control over audio playback and recording.

The easiest way to play and record audio is to use high-level audio services. If the high-level audio services do not meet the audio needs of your application, you can use low-level audio services to write your own routines to manage audio playback and recording.

+$#A Windows Audio Architecture

Windows provides audio services through high-level audio functions, Media Control Interface (MCI) device drivers, low-level audio functions, the MIDI Mapper, and low-level audio device drivers. The following illustration shows the relationship between an application and the elements of Windows that provide audio support.

+$#A High-Level Audio Support

MCI functions, along with MCI device drivers, provide high-level audio support for MIDI, waveform, and CD audio devices. MCI drivers can play disk-resident (hard disk and CD-ROM) audio files in the background while an application runs in the foreground. In addition to MCI, Windows provides high-level audio support with three special-purpose functions that play waveform sounds: MessageBeep, PlaySound, and sndPlaySound. You can also use these functions to play waveform sounds associated with system alerts and sounds identified by entries in the registry.

High-level audio functions should meet the audio requirements of most applications. For information about using MCI and the MessageBeep, PlaySound, and sndPlaySound functions, see High-Level Audio.

+$#A Low-Level Audio Support

Low-level audio functions provide a device-independent interface to audio hardware in multimedia computers. These functions supply low-level audio services by letting applications communicate directly with the audio device drivers. MCI drivers and the MessageBeep, PlaySound, and sndPlaySound functions use these low-level functions to provide high-level audio services.

Low-level audio functions are provided for applications ⎯ such as audio tools programs ⎯ that have special-purpose audio requirements. For example, a MIDI sequencer application would use low-level audio functions to record and play MIDI data. For information about using low-level audio services, see Low-Level Audio.

+$#A The MIDI Mapper

The MIDI Mapper provides standard patch services for device-independent playback of MIDI files. Standard patch services ensure that different MIDI synthesizers use the same instrument sounds to reproduce the music in a MIDI file.

The MIDI Mapper translates and redirects messages sent to it by low-level MIDI output functions. Because the high-level MIDI services use low-level MIDI output functions, the MIDI Mapper can be used with high-level as well as low-level MIDI services. For information about the architecture of the MIDI Mapper and how to use the MIDI Mapper with high-level audio services, see High-Level Audio. For information about using the MIDI Mapper with low-level audio services, see Low-Level Audio. MIDI files must follow certain authoring guidelines to use the standard patch services provided by the MIDI Mapper. For details about these authoring guidelines, see High-Level Audio.

+$#A Audio File Formats

Windows supports a tagged file structure called the Resource Interchange File Format (RIFF). There are two RIFF file formats currently defined for audio files.

RIFF type | Filename extension | Description
RMID | .RMI | MIDI audio file
WAVE | .WAV | Waveform audio file

The WAVE file format supports a number of digital audio data formats. All multimedia computers support pulse code modulated (PCM) formats of 8-bit monaural data at sample rates of 11.025 kilohertz (kHz) and 22.05 kHz.

In addition to Resource Interchange File Format MIDI (RMID) files, the MCI MIDI sequencer plays standard MIDI files in the format defined by the International MIDI Association in the "Standard MIDI Files 1.0" specification. For information about how to obtain this specification, see Further Reading. RMID files are standard MIDI files with a RIFF header.

+$#A Using RIFF Files

The multimedia file input and output (I/O) services include functions for working with RIFF files. For information about using these functions, see Multimedia File Input/Output.

+$#A Further Reading

For more information on digital audio, digital-signal processing, and computer music, see the following books:

Boom, Michael. Music Through MIDI. Redmond: Microsoft Press, 1987.

Chamberlin, Hal. Musical Applications of Microprocessors. Hasbrouk Heights: Hayden Book Company, Inc., 1985.

Pohlmann, Ken. Principles of Digital Audio. Indianapolis: Howard W. Sams & Co., 1985.

Roads, Curtis, and John Strawn. Foundations of Computer Music. Cambridge: The MIT Press, 1985.

Strawn, John F. Digital Audio Engineering, An Anthology. Los Altos: William Kaufmann, Inc., 1985.

Strawn, John F. Digital Audio Signal Processing, An Anthology. Los Altos: William Kaufmann, Inc., 1985.

For information about MIDI and standard MIDI files, see the following MIDI specifications:

MIDI 1.0 Detailed Specification

Standard MIDI Files 1.0 Specification

These specifications are available from the International MIDI Association at the following address:

International MIDI Association
5316 West 57th Street
Los Angeles, CA 90056

+$#A High-Level Audio

This overview explains how to use the high-level audio services of Microsoft® Windows® to add sound to applications. For an overview of the audio services, see Introduction to Audio.

This overview covers the following topics:

• Playing waveform sounds

• Playing and Recording Audio Using the Media Control Interface

• Opening waveform and MIDI sequencer devices

• Authoring MIDI files

The topics in this overview assume you have a basic knowledge of digital audio and MIDI. For additional information on these subjects, see Further Reading.

+$#A Function Prefixes

High-level audio function names begin with the following prefixes:

Prefix | Function group
snd | System-alert sound functions
mci | MCI functions

+$#A Playing Waveform Sounds

One basic way to use sound is to associate a sound with a user action or with a warning or alert message. These sounds tend to be short in duration and are often played repeatedly. Windows provides three functions to play waveform sounds.

Function | Description
MessageBeep | Plays the sound that corresponds to a given system-alert level.
PlaySound | Plays the sound that corresponds to the given filename or registry entry.
sndPlaySound | Plays the sound that corresponds to the given filename or registry entry.

These functions provide methods of playing the following waveform sounds:

• WAVE files stored on a hard disk or CD-ROM

• In-memory WAVE resources

• WAVE files specified by entries in the registry

• WAVE files associated with system-alert levels

+$#A Restrictions in Playing Waveform Sounds

There are some restrictions to using the MessageBeep, PlaySound, and sndPlaySound function to play waveform sounds:

• The entire sound must fit in available physical memory.

• The sound must be in a data format playable by one of the installed waveform audio device drivers.

Use MessageBeep, PlaySound, and sndPlaySound to play WAVE files that are relatively small in size ⎯ up to about 100 kilobytes. For larger sound files, use the MCI services. For information about using MCI, see Playing and Recording Audio Using the Media Control Interface.

+$#A Using the PlaySound and sndPlaySound Functions

The PlaySound and sndPlaySound functions can be used to perform the following tasks:

• Playing WAVE files

• Looping High-Level Audio Sounds

• Playing High-Level Audio WAVE Resources

• Playing High-Level Audio Sounds Specified in the Registry

Although the examples shown in these topics use the sndPlaySound function, most of the sample code works with the PlaySound function as well.

+$#A Playing WAVE Files

The following statement plays the C:\SOUNDS\BELLS.WAV file.

sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_SYNC);

If the specified file does not exist or does not fit into the available physical memory, sndPlaySound plays the default system sound specified by the SystemDefault entry in the [sounds] section of the registry. If there is no SystemDefault entry, sndPlaySound fails without producing any sound. If you don't want the default system sound to play, specify the SND_NODEFAULT flags in the following example.

sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_SYNC | SND_NODEFAULT);

+$#A Looping High-Level Audio Sounds

If you specify the SND_LOOP and SND_ASYNC flags for the wFlags parameter of the sndPlaySound function, as in the following example, the sound continues to play repeatedly.

sndPlaySound("C:\\SOUNDS\\BELLS.WAV", SND_LOOP | SND_ASYNC);

If you want to "loop" a sound, you must play it asynchronously. You cannot use the SND_SYNC flag with the SND_LOOP flag. A looped sound continues to play until sndPlaySound is called to play another sound. To stop playing a looped or asynchronous sound without playing another sound, use the following statement.

sndPlaySound(NULL, 0);

+$#A Playing High-Level Audio WAVE Resources

You can also build WAVE files into an application as resources and use the sndPlaySound function to play these sounds by specifying the SND_MEMORY flag. The SND_MEMORY flag indicates that the lpszSoundName parameter is a pointer to an in-memory image of the WAVE file. To include a WAVE file as a resource in an application, add the following entry to the application's resource script (.RC) file.

soundName WAVE c:\sounds\bells.wav

The name soundName is a placeholder for a name that you supply to refer to this WAVE resource. WAVE resources are loaded and accessed just like other application-defined Windows resources. The function in the following example plays a specified WAVE resource.

/* Plays a specified WAVE resource. */

BOOL PlayResource(LPSTR lpName)

{

BOOL bRtn;

LPSTR lpRes;

HANDLE hResInfo, hRes;

/* Find the WAVE resource. */

hResInfo = FindResource(hInst, lpName, "WAVE");

if (hResInfo == NULL)

return FALSE;

/* Load the WAVE resource. */

hRes = LoadResource(hInst, hResInfo);

if (hRes == NULL)

return FALSE;

/* Lock the WAVE resource and play it. */

lpRes = LockResource(hRes);

if (lpRes != NULL) {

bRtn = sndPlaySound(lpRes, SND_MEMORY | SND_SYNC |

SND_NODEFAULT);

UnlockResource(hRes);

}

else

bRtn = 0;

/* Free the WAVE resource and return success or failure. */

FreeResource(hRes);

return bRtn;

}

To play a WAVE resource by using this function, pass to the function a pointer to a string containing the name of the resource, as shown in the following example.

PlayResource("soundName");

+$#A Playing High-Level Audio Sounds Specified in the Registry

The sndPlaySound function also plays waveform sounds referred to by a key-name in the [sounds] section of the registry. This enables users to assign their own sounds to system alerts and warnings, or to user actions such as mouse-button click. For example, the [sounds] section of the registry might look like the following.

[sounds]

SystemDefault=C:\SOUNDS\BUMMER.WAV, Says BUMMER

SystemAsterisk=C:\SOUNDS\WHALES.WAV, Whale sounds

SystemExclamation=C:\SOUNDS\LASER.WAV, Laser gun sounds

SystemHand=C:\SOUNDS\OHOH.WAV, Says OH-OH

SystemQuestion=C:\SOUNDS\JIBERISH.WAV, Person mumbling

SystemStart=C:\SOUNDS\CHORD.WAV, Plays a chord

MouseClick=C:\SOUNDS\CLICK.WAV, Recording of a clicking sound

To play a sound identified by a registry entry, call sndPlaySound with the lpszSoundName parameter pointing to a string that contains the name of the entry that identifies the sound. For example, to play the sound associated with the MouseClick entry in the [sounds] section of the registry and wait for the sound to finish before returning, use the following statement.

sndPlaySound("MouseClick", SND_SYNC);

If the specified registry entry or the waveform file it identifies does not exist, or if the sound does not fit into the available physical memory, sndPlaySound plays the default system sound specified by the SystemDefault entry. If there is no SystemDefault entry, sndPlaySound fails without producing any sound. If you don't want the default system sound to play, specify the SND_NODEFAULT flag, as in the following example.

sndPlaySound("MouseClick", SND_SYNC | SND_NODEFAULT);

Note The sndPlaySound function always searches the [sounds] section of the registry for a keyname matching the lpszSoundName parameter before attempting to load a file with this name.

The [sounds] section of the registry includes an optional string following the waveform filename for descriptive text about the sound in the file. This descriptive text is displayed in the Sound option of the Control Panel.

+$#A Playing System-Alert Sounds

The MessageBeep function uses the waveform audio hardware to produce a variety of user-selectable sounds.

When you call MessageBeep, it searches the [sounds] section of the registry for the WAVE file that corresponds to the specified alert level. Following are the registry entries for system-alert sounds.

Registry Entry | Description
SystemDefault | Identifies the sound produced when the MessageBeep function is called with the wAlert parameter set to an invalid alert level or when the requested alert sound can't be found. This is called the default system sound.
SystemAsterisk | Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONASTERISK.
SystemExclamation | Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONEXCLAMATION.
SystemHand | Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONHAND.
SystemQuestion | Identifies the sound produced when MessageBeep is called with wAlert set to MB_ICONQUESTION.

If MessageBeep is called with wAlert set to zero, it plays the default system sound. If wAlert is set to −1, MessageBeep uses the computer speaker to produce a standard beep sound. If MessageBeep can't play the requested sound, it plays the default system sound. If it can't play the default system sound, then it produces a beep sound using the computer speaker.

The Sound option of the Control Panel enables users to set these registry entries to customize their environment for system-alert sounds. It also enables users to disable these alert sounds.

+$# Playing and Recording Audio Using the Media Control Interface

The Media Control Interface (MCI) provides a high-level interface for controlling both internal and external media devices. It provides support for playing wave- form, MIDI, and compact disc audio and for recording waveform audio. MCI is the easiest way for multimedia applications to play and record audio.

MCI uses device drivers to interpret and execute high-level MCI commands. MCI device drivers can stream digital audio and MIDI data directly from a storage device to the appropriate device driver, allowing applications to play files too large to fit in available physical memory. This data streaming takes place in the background while an application is running. The application is responsible only for setting up MCI and telling it to start playing or recording.

MCI provides two types of interface: command-message and command-string. This section explains how to use the command-message interface for waveform, MIDI, and compact disc audio devices. For an introduction to MCI and for information about the command-string interface, see Media Control Interface.

+$#A MCI Audio Data Types

The MMSYSTEM.H header file defines data types and function prototypes for MCI. You must include this header file in any source-code module that uses MCI functions.

Following are the data types for structures used with extended MCI commands related to audio.

Type | Description
MCI_SEQ_SET_PARMS | Specifies a structure for the MCI_SET command message for MIDI sequencer devices.
MCI_WAVE_DELETE_PARMS | Specifies a structure for the MCI_DELETE command message for waveform audio devices.
MCI_WAVE_OPEN_PARMS | Specifies a structure for the MCI_OPEN command message for waveform audio devices.
MCI_WAVE_SET_PARMS | Specifies a structure for the MCI_SET command message for waveform audio devices.

+$#A MCI Audio Commands

MCI provides a standard set of commands applying to all types of media devices. Some of these commands can be extended to accommodate unique features of a particular type of device.

Following are the MCI command messages that perform common audio playback and recording tasks.

MCI command message | Audio task
MCI_OPEN | Open an audio device.
MCI_CLOSE | Close an audio device.
MCI_PLAY | Play all or part of audio selection; resume playback from pause.
MCI_STOP | Stop playback.
MCI_PAUSE | Pause playback.
MCI_SEEK | Change current location.
MCI_CUE | Cue a device so playback or recording begins with minimum delay.
MCI_RECORD | Begin recording on a waveform audio device.
MCI_SAVE | Save a recorded waveform audio file.
MCI_INFO | Query device information such as the product name and the name of the device element currently associated with the device (returns information in string format).
MCI_GETDEVCAPS | Query device capabilities such as the device name and number of inputs and outputs (if the device can record).
MCI_STATUS | Query device status such as current playback position, media length, media format, time format, record level, CD audio track, and MIDI sequencer tempo.
MCI_SET | Set device parameters such as time format, waveform data format, and MIDI sequencer tempo.

+$#A Opening MCI Audio Devices

MCI requires that audio devices be opened before they can be accessed. Use the MCI_OPEN command message along with the MCI_OPEN_PARMS structure to open an audio device and obtain an MCI device identifier. Subsequent commands use this device identifier to identify the device to receive the command.

The MCI device identifier is returned in the wDeviceID member. Always check the return value of the mciSendCommand function after sending an MCI_OPEN command message before using this device identifier. A nonzero return value indicates that there was an error in opening the device and that the returned device identifier is not valid.

For waveform audio devices, you can use an extended structure, MCI_WAVE_OPEN_PARMS. This structure has a dwBufferSeconds member to specify the number of seconds of buffering used by the MCI waveform device driver. If you use this member, you must specify the MCI_WAVE_OPEN_BUFFER flag for the dwParam1 parameter of mciSendCommand to validate the member.

If you do not want to specify the number of seconds of buffering for the driver to use, you can use the MCI_WAVE_OPEN_PARMS structure when you open waveform audio devices.

When you open a simple device, such as a compact disc audio device, you must specify the device name. When you open a compound device, such as a waveform or MIDI sequencer device, the device name is optional. Use the lpstrDeviceType member of the MCI_OPEN_PARMS structure to specify the device name. MCI lets you use a string or a constant for this member. Following are the strings and constants for MCI audio devices.

Device | String | Constant
Compact disc | cdaudio | MCI_DEVTYPE_CD_AUDIO
Waveform | waveaudio | MCI_DEVTYPE_WAVEFORM_AUDIO
MIDI sequencer | sequencer | MCI_DEVTYPE_SEQUENCER

A string is the default format for specifying device names. If you use a constant to specify the device name, you must specify the MCI_OPEN_TYPE_ID flag in addition to the MCI_OPEN_TYPE flag.

+$#A Opening Waveform and MIDI Sequencer Devices

Waveform and MIDI sequencer devices are compound devices. Compound devices require an associated device element ⎯ a WAVE or MIDI file. There are three ways to open compound devices:

• Specify only the device name.

• Specify only the device element and let MCI select the device associated with the file extension of the device element.

• Specify both the device name and the device element.

Specify only the device name when opening a device to query its capabilities with the MCI_GETDEVCAPS command message and when you plan to use the device to play more than one device element.

If you don't specify a device name when you open a compound device, MCI chooses an appropriate device type by comparing the file extension of the device element with entries in the [mci extensions] section of the registry. The following example uses this technique to open a MIDI sequencer device to play a MIDI file named CHOPIN.RMI.

UINT wDeviceID;

MCI_OPEN_PARMS mciOpenParms;

.ifdef REF

.

.

.

.endif

/* Open the device by specifying only the device element. */

mciOpenParms.lpstrElementName = "CHOPIN.RMI";

if (mciSendCommand(0, /* device ID */

MCI_OPEN, /* command */

MCI_OPEN_ELEMENT, /* flags */

(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */

/* Error, unable to open device. */

return FALSE;

else

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

Instead of letting MCI choose the device, you can specify the device name when you open the device. The following example uses this technique to open a waveform audio device to play the BELLS.WAV file. This example uses a string to specify the device name. For an example using a constant to specify the device name, see Opening Compact Disc Devices.

UINT wDeviceID;

MCI_OPEN_PARMS mciOpenParms;

.ifdef REF

.

.

.

.endif

/*

* Open the device by specifying both the device

* element and the device name.

*/

mciOpenParms.lpstrDeviceType = "waveaudio";

mciOpenParms.lpstrElementName = "C:\\SOUNDS\\BELLS.WAV";

if (mciSendCommand(0, /* device ID */

MCI_OPEN, /* command */

MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, /* flags */

(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */

/* Error, unable to open device. */

return FALSE;

else

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

+$#A Using the MIDI Mapper With the MCI Sequencer

The MIDI Mapper is the default output device for the MCI MIDI sequencer. It provides standard patch services for device-independent playback of MIDI files. Applications that use the MCI sequencer to play MIDI files should use the MIDI Mapper. For details about the MIDI Mapper, see The MIDI Mapper. For information about authoring device-independent MIDI files, see Authoring MIDI Files.

When you open the MCI MIDI sequencer, MCI attempts to select the MIDI Mapper as the output device. If the MIDI Mapper is unavailable because it is already in use, MCI selects another MIDI output device.

Note The MIDI Mapper currently supports only one client at a time in the current versions of Microsoft Windows NT.

After opening the sequencer, you should check to see if the MIDI Mapper was available and was selected as the output device. The following example uses the MCI_STATUS command message to verify that the MIDI Mapper is the output device for the MCI sequencer.

UINT wDeviceID;

DWORD dwReturn;

MCI_STATUS_PARMS mciStatusParms;

.ifdef REF

.

.

.

.endif

/* Make sure the opened device is the MIDI Mapper. */

mciStatusParms.dwItem = MCI_SEQ_STATUS_PORT;

if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,

MCI_STATUS_ITEM, (DWORD)(LPVOID) &mciStatusParms)) {

/* Error sending MCI_STATUS command. */

}

if (LOWORD(mciStatusParms.dwReturn) == MIDI_MAPPER) {

/* The MIDI Mapper is the output device. */

}

else {

/* The MIDI Mapper is not the output device. */

}

MCI also provides a command message that explicitly selects the MIDI Mapper as the output device for the sequencer. The following example uses the MCI_SET command message to select the MIDI Mapper as the output device for the MCI sequencer.

UINT wDeviceID;

DWORD dwReturn;

MCI_SEQ_SET_PARMS mciSeqSetParms;

.ifdef REF

.

.

.

.endif

/* Set the MIDI Mapper as the output port for the open device. */

mciSeqSetParms.dwPort = MIDI_MAPPER;

if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SEQ_SET_PORT,

(DWORD)(LPVOID) &mciSeqSetParms)) {

/* Error, unable to set Mapper as output port. */

}

Before querying the sequencer or setting an output port, you must successfully open the sequencer. Both of the previous examples assume that the wDeviceID parameter contains a valid device identifier for the sequencer.

+$#A Opening Compact Disc Devices

Because a compact disc audio device is a simple device, you need only specify the device name when opening it by using either a string or a constant device identifier. The following example opens a compact disc device by using a constant device identifier to specify the device name.

UINT wDeviceID;

MCI_OPEN_PARMS mciOpenParms;

.ifdef REF

.

.

.

.endif

/* Open the device by specifying a constant device ID. */

mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;

if (mciSendCommand(0, /* device ID */

MCI_OPEN, /* command */

MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, /* flags */

(DWORD) (LPVOID) &mciOpenParms)) /* parameter block */

/* Error, unable to open device. */

return FALSE;

else

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

+$#A Handling High-Level Audio MCI Errors

Always check the return value of the mciSendCommand function. If it indicates an error, you can use the mciGetErrorString function to get a textual description of the error. You can also interpret the error code yourself; MMSYSTEM.H defines constants for MCI error return codes.

Note To interpret an error return value from mciSendCommand yourself, mask the high-order word; the low-order word contains the error code. However, if you pass the error return value to the mciGetErrorString function, you must pass the entire doubleword value.

The following example function takes the MCI error code specified by dwError, passes it to mciGetErrorString, and displays the resulting textual error description by using the MessageBox function.

/*

* Uses mciGetErrorString to get a

* text description of an MCI error.

* Displays the error description by using MessageBox.

*/

void showError(DWORD dwError)

{

char szErrorBuf[MAXERRORLENGTH];

MessageBeep(MB_ICONEXCLAMATION);

if(mciGetErrorString(dwError, (LPSTR) szErrorBuf,

MAXERRORLENGTH))

MessageBox(hMainWnd, szErrorBuf,

"MCI Error", MB_ICONEXCLAMATION);

else

MessageBox(hMainWnd, "Unknown Error", "MCI Error",

MB_ICONEXCLAMATION);

}

+$#A Starting Playback

Once you successfully open an MCI audio device, you can use the MCI_PLAY command message along with the MCI_PLAY_PARMS structure to begin playback. MMSYSTEM.H defines MCI_PLAY_PARMS as follows.

typedef struct {

DWORD dwCallback; /* callback for MCI_NOTIFY flag */

DWORD dwFrom; /* playback begin position */

DWORD dwTo; /* playback end position */

} MCI_PLAY_PARMS;

Playback begins at the current position in the device element. When a device element is opened, the current position is set to the beginning of the medium. After a device element is played, the current position is at the end of the medium. You can use the MCI_SEEK command message to change the current position. For more information about MCI_SEEK, see Changing the Current Position.

You can also set beginning and ending positions for playback by specifying the MCI_FROM and MCI_TO flags with the MCI_PLAY command message. If you specify one of these flags, you must fill in the corresponding dwFrom or dwTo member in the MCI_PLAY_PARMS structure with the desired beginning or ending time. If you are using a time format other than the default time format, you must set the time format before specifying a beginning or ending time with MCI_PLAY.

+$#A Example: Playing a WAVE File

The following example function opens a waveform device and plays the WAVE file specified by the lpszWAVEFileName parameter.

/*

* Plays a given WAVE file by using MCI_OPEN, MCI_PLAY.

* Returns when playback

* begins. Returns 0L on success;

* otherwise returns an MCI error code.

*/

DWORD playWAVEFile(HWND hWndNotify, LPSTR lpszWAVEFileName)

{

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

MCI_PLAY_PARMS mciPlayParms;

/*

* Open the device by specifying the

* device name and device element.

* MCI will choose a device capable

* of playing the given file.

*/

mciOpenParms.lpstrDeviceType = "waveaudio";

mciOpenParms.lpstrElementName = lpszWAVEFileName;

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

(DWORD)(LPVOID) &mciOpenParms)) {

/*

* Failed to open device;

* don't close it, just return error.

*/

return dwReturn;

}

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

/*

* Begin playback. The window-procedure function

* for the parent window is notified with an

* MM_MCINOTIFY message when playback is

* complete. The window procedure then

* closes the device.

*/

mciPlayParms.dwCallback = (DWORD) hWndNotify;

if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,

MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

return 0;

}

+$#A Example: Playing a MIDI File

The following example function opens a MIDI sequencer device, verifies that the MIDI Mapper was selected as the output port, plays the MIDI file specified by the lpszMIDIFileName parameter, and closes the device after playback is complete.

/*

* Plays a given MIDI file using MCI_OPEN, MCI_PLAY.

* Returns as soon as playback begins. The window

* procedure function for the given window is notified

* when playback is complete. Returns 0L on success;

* otherwise returns an MCI error code.

*/

DWORD playMIDIFile(HWND hWndNotify, LPSTR lpszMIDIFileName)

{

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

MCI_PLAY_PARMS mciPlayParms;

MCI_STATUS_PARMS mciStatusParms;

/*

* Open the device by specifying the

* device name and device element.

* MCI will attempt to choose the

* MIDI Mapper as the output port.

*/

mciOpenParms.lpstrDeviceType = "sequencer";

mciOpenParms.lpstrElementName = lpszMIDIFileName;

if (dwReturn = mciSendCommand(NULL, MCI_OPEN,

MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

(DWORD)(LPVOID) &mciOpenParms)) {

/*

* Failed to open device;

* don't close it, just return error.

*/

return dwReturn;

}

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

/* See if the output port is the MIDI Mapper. */

mciStatusParms.dwItem = MCI_SEQ_STATUS_PORT;

if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,

MCI_STATUS_ITEM,

(DWORD)(LPVOID) &mciStatusParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

/*

* The output port is not the MIDI Mapper.

* Ask if user wants to continue.

*/

if (LOWORD(mciStatusParms.dwReturn) != MIDI_MAPPER) {

if (MessageBox(hMainWnd,

"The MIDI Mapper is not available. Continue?",

"", MB_YESNO) == IDNO) {

/*

* User does not want to continue. Not an error;

* just close the device and return.

*/

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return 0;

}

}

/*

* Begin playback. The window procedure function

* for the parent window is notified with an

* MM_MCINOTIFY message when playback is complete.

* The window procedure then closes the device.

*/

mciPlayParms.dwCallback = (DWORD) hWndNotify;

if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,

MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

return 0;

}

+$#A Example: Playing a Compact Disc Track

The following example function opens a compact disc device, plays the track specified by the wTrack parameter, and closes the device after playback is complete.

/*

* Plays a given compact disc track using

* MCI_OPEN, MCI_PLAY. Returns as

* soon as playback begins. The window procedure

* function for the given window will be notified

* when playback is complete. Returns 0L on

* success; otherwise, it returns an MCI error code.

*/

DWORD playCDTrack(HWND hWndNotify, BYTE bTrack)

{

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

MCI_SET_PARMS mciSetParms;

MCI_PLAY_PARMS mciPlayParms;

/*

* Open the compact disc device by specifying

* the device name.

*/

mciOpenParms.lpstrDeviceType = "cdaudio";

if (dwReturn = mciSendCommand(NULL, MCI_OPEN,

MCI_OPEN_TYPE, (DWORD)(LPVOID) &mciOpenParms)) {

/*

* Failed to open device;

* don't close it, just return error.

*/

return dwReturn;

}

/* Device opened successfully, get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

/* Set the time format to track/minute/second/frame. */

mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;

if (dwReturn = mciSendCommand(wDeviceID, MCI_SET,

MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

/*

* Begin playback from the given track and play

* until the beginning of the next track. The window

* procedure function for the parent window will be

* notified with an MM_MCINOTIFY message when playback

* is complete. Unless the play command fails, the

* window procedure closes the device.

*/

mciPlayParms.dwFrom = MCI_MAKE_TMSF(bTrack, 0, 0, 0);

mciPlayParms.dwTo = MCI_MAKE_TMSF(bTrack + 1, 0, 0, 0);

mciPlayParms.dwCallback = (DWORD) hWndNotify;

if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,

MCI_FROM | MCI_TO | MCI_NOTIFY,

(DWORD)(LPVOID) &mciPlayParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

return 0;

}

To specify a track-relative position with a compact disc device, you must use the track/minute/second/frame time format. For details, see Setting the Time Format.

+$#A Changing the Current Position

To change the current position in a device element, use the MCI_SEEK command message along with the MCI_TO flag and the MCI_SEEK_PARMS structure. MMSYSTEM.H defines MCI_SEEK_PARMS as follows.

typedef struct {

DWORD dwCallback; /* callback for MCI_NOTIFY flag */

DWORD dwTo; /* seek position */

} MCI_SEEK_PARMS;

If you use the dwTo member to specify a seek position with MCI_SEEK, you should query the time format and set it, if necessary.

In addition to specifying a position with the dwTo member, you can specify the MCI_SEEK_TO_START or MCI_SEEK_TO_END flags for the dwParam1 parameter of the mciSendCommand function to find the starting and ending positions of the device element. If you use one of these flags, don't specify the MCI_TO flag.

+$#A Setting the Time Format

Use the MCI_SET command message along with the MCI_SET_PARMS structure to set the time format for an open device. Set the dwTimeFormat member to one of the following constants.

Constant | Time format
MCI_FORMAT_BYTES | Bytes (in pulse code modulated [PCM] format files)
MCI_FORMAT_MILLISECONDS | Milliseconds
MCI_FORMAT_MSF | Minute/second/frame
MCI_FORMAT_SAMPLES | Samples
MCI_FORMAT_SMPTE_24 | SMPTE, 24 frame
MCI_FORMAT_SMPTE_25 | SMPTE, 25 frame
MCI_FORMAT_SMPTE_30 | SMPTE, 30 frame
MCI_FORMAT_SMPTE_30DROP | SMPTE, 30 frame drop
MCI_FORMAT_TMSF | Track/minute/second/frame
MCI_SEQ_FORMAT_SONGPTR | MIDI song pointer

The following example sets the time format to milliseconds on the device specified by wDeviceID:

UINT wDeviceID;

MCI_SET_PARMS mciSetParms;

.ifdef REF

.

.

.

.endif

/* Set time format to milliseconds. */

mciSetParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;

if (mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT,

(DWORD) &mciSetParms))

/* Error, unable to set time format. */

return FALSE;

else

/* Time format set successfully. */

return TRUE;

+$#A Using the Minute/Second/Frame Time Format

In the minute/second/frame time format specified with the MCI_FORMAT_MSF constant, the time is relative to the beginning of the medium. The time is packed into a double as follows.

Word | Byte | Contents
High-order word | High-order byte | Unused
 | Low-order byte | Frames
Low-order word | High-order byte | Seconds
 | Low-order byte | Minutes

The MMSYSTEM.H header file defines the following macros to get and set elements of a minute/second/frame packed doubleword.

Macro | Description
MCI_MSF_MINUTE | Gets minute value in a minute/second/frame doubleword.
MCI_MSF_SECOND | Gets second value in a minute/second/frame doubleword.
MCI_MSF_FRAME | Gets frame value in a minute/second/frame doubleword.
MCI_MAKE_MSF | Sets minute, second, and frame values in a minute/second/frame doubleword.

Note The first few seconds of audio compact discs contain table-of-contents data. To play from the beginning of a disc using the minute/second/frame time format, use the MCI_STATUS command message to get the position of the first track and play from that position.

+$#A Using the Track/Minute/Second/Frame Time Format

In the track/minute/second/frame time format specified with the MCI_FORMAT_TMSF constant, the time is relative to the beginning of the specified track. The time is packed into a doubleword, as follows.

Word | Byte | Contents
High-order word | High-order byte | Frames
 | Low-order byte | Seconds
Low-order word | High-order byte | Minutes
 | Low-order byte | Track

The MMSYSTEM.H header file defines the following macros to get and set elements of a track/minute/second/frame packed doubleword.

Macro | Description
MCI_TMSF_TRACK | Gets track value in a track/minute/second/frame doubleword.
MCI_TMSF_MINUTE | Gets minute value in a track/minute/second/frame doubleword.
MCI_TMSF_SECOND | Gets second value in a track/minute/second/frame doubleword.
MCI_TMSF_FRAME | Gets frame value in a track/minute/second/frame doubleword.
MCI_MAKE_TMSF | Sets track, minute, second, and frame values in a track/minute/second/frame doubleword.

+$#A Using the SMPTE Time Formats

Society of Motion Picture and Television Engineers (SMPTE) time formats are based on standard time formats developed for the motion picture and television industries. For SMPTE time formats, the time is packed into a doubleword, as shown in the following table follows.

Word | Byte | Contents
High-order word | High-order byte | Frames
 | Low-order byte | Seconds
Low-order word | High-order byte | Minutes
 | Low-order byte | Hours

+$#A Closing MCI Audio Devices

After you finish using an MCI device, you must use the MCI_CLOSE command messages to close the device. If you are playing a compound device, such as a waveform or MIDI device, and want to play a different device element using the same device, close the device and reopen it using the new device element.

Note If you're playing multiple device elements with the same device, you'll get better performance if you open the device by explicitly specifying its name, then open, play, and close the individual device elements separately. Don't close the device until you have finished playing all of the device elements. Otherwise, the driver is reloaded each time you open the device.

+$#A Getting Information About Devices and Media

Use the MCI_STATUS command message along with the MCI_STATUS_PARMS structure to get information about the status of an open device and its associated device element.

Before using the MCI_STATUS command message, you must identify the status item to query for by putting a constant in the dwItem member of the structure. Select a topic below to view the constants and their corresponding status items for the different audio device types.

All Audio Devices
Waveform Devices
Compact Disc Devices
MIDI Sequencer Devices

+$#A All Audio Devices

Constant | Status item
MCI_STATUS_LENGTH | Length of the medium
MCI_STATUS_POSITION | Current position
MCI_STATUS_MODE | Current mode
MCI_STATUS_TIME_FORMAT | Time format
MCI_STATUS_READY | Ready state

+$#A Waveform Devices

Constant | Status item
MCI_WAVE_STATUS_BLOCKALIGN | Block alignment
MCI_WAVE_STATUS_FORMATTAG | Format tag
MCI_WAVE_STATUS_CHANNELS | Number of channels
MCI_WAVE_STATUS_SAMPLESPERSEC | Sample rate
MCI_WAVE_STATUS_AVGBYTESPERSEC | Average bytes per second
MCI_WAVE_STATUS_BITSPERSAMPLE | Bits per sample (in PCM format files)
MCI_WAVE_STATUS_LEVEL | Record level

+$#A Compact Disc Devices

Constant | Status item
MCI_STATUS_NUMBER_OF_TRACKS | Number of tracks
MCI_STATUS_MEDIA_PRESENT | Media present
MCI_STATUS_CURRENT_TRACK | Current track

+$#A MIDI Sequencer Devices

Constant | Status item
MCI_SEQ_STATUS_TEMPO | Tempo
MCI_SEQ_STATUS_PORT | Port
MCI_SEQ_STATUS_OFFSET | SMPTE offset
MCI_SEQ_STATUS_DIVTYPE | Division type of file

+$#A Getting Track-Relative Information for Compact Disc Devices

For compact disc devices, you can get the starting position and length of a track by specifying the MCI_TRACK flag and setting the dwTrack member of the MCI_STATUS_PARMS structure to the desired track number. To get the starting position of a track, set the dwItem member to MCI_STATUS_POSITION. To get the length of a track, set dwItem to MCI_STATUS_LENGTH. The following function gets the number of tracks on the disc and the starting position of each track. It then uses the MessageBox function to report the starting positions of the tracks.

/*

* Uses the MCI_STATUS command to get and

* display the starting times for the tracks

* on a compact disc. Returns 0L on success;

* otherwise, it returns an MCI error code.

*/

DWORD getCDTrackStartTimes(void)

{

UINT wDeviceID;

int i, iNumTracks;

DWORD dwReturn;

DWORD *pMem;

char szTempString[64];

char szTimeString[512] = "\0"; /* enough for 20 tracks */

MCI_OPEN_PARMS mciOpenParms;

MCI_SET_PARMS mciSetParms;

MCI_STATUS_PARMS mciStatusParms;

/* Open the compact disc device by specifying the device name.

*/

mciOpenParms.lpstrDeviceType = "cdaudio";

if (dwReturn = mciSendCommand(NULL, MCI_OPEN,

MCI_OPEN_TYPE,

(DWORD)(LPVOID) &mciOpenParms)) {

/*

* Failed to open device.

* Don't have to close it; just return error.

*/

return dwReturn;

}

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

/* Set the time format to minute/second/frame format. */

mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;

if (dwReturn = mciSendCommand(wDeviceID, MCI_SET,

MCI_SET_TIME_FORMAT,

(DWORD)(LPVOID) &mciSetParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

/* Get the number of tracks; limit to number

* we can display (20).

*/

mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;

if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,

MCI_STATUS_ITEM,

(DWORD)(LPVOID) &mciStatusParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

iNumTracks = mciStatusParms.dwReturn;

iNumTracks = min(iNumTracks, 20);

/* Allocate memory to hold starting positions. */

pMem = (DWORD *)LocalAlloc(LPTR, iNumTracks * sizeof(DWORD));

if (pMem == NULL) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return 0xFFFFFFFF;

}

/*

* For each track, get and save the starting position and

* build a string containing starting positions.

*/

for(i = 1; i <= iNumTracks; i++) {

mciStatusParms.dwItem = MCI_STATUS_POSITION;

mciStatusParms.dwTrack = i;

if (dwReturn = mciSendCommand(wDeviceID, MCI_STATUS,

MCI_STATUS_ITEM | MCI_TRACK,

(DWORD)(LPVOID) &mciStatusParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

pMem[i-1] = mciStatusParms.dwReturn;

wsprintf(szTempString, "Track %2d - %02d:%02d:%02d\n",

i, MCI_MSF_MINUTE(pMem[i-1]),

MCI_MSF_SECOND(pMem[i-1]),

MCI_MSF_FRAME(pMem[i-1]));

lstrcat(szTimeString, szTempString);

}

/* Use MessageBox to display starting times. */

MessageBox(hMainWnd, szTimeString, "Track Starting Position",

MB_ICONINFORMATION);

/* Free memory and close the device. */

LocalFree((HANDLE) pMem);

if (dwReturn = mciSendCommand(wDeviceID,

MCI_CLOSE, 0, NULL)) {

return dwReturn;

}

return 0;

}

+$#A Recording With Waveform Audio Devices

MCI supports recording with waveform audio devices. You can insert or overwrite recorded information in an existing file or record into a new file. To open an existing file for recording, open a waveform device and device element as you would open them for playback. To record into a new file, open the device but specify a zero-length filename for the device element.

When MCI creates a new file for recording, the waveform data format is set to a default format specified by the device driver. To use a format other than the default format, you can use the MCI_SET command message to change the format.

To begin recording, use the MCI_RECORD command message along with the MCI_RECORD_PARMS structure.

If you record to an existing file, you can use the MCI_TO and MCI_FROM flags to specify beginning and ending points for recording. For example, if you record to an existing file 20 seconds long, and you begin recording at 5 seconds and end recording at 10 seconds, you will have a recording 25 seconds long. It will contain 5 seconds of the original recording followed by the 5 seconds of new recording, and will end with the remaining 15 seconds of the original recording.

If you don't specify an ending position, recording continues until you send an MCI_STOP command message, or until the driver runs out of free disk space. If you record to a new file, you can omit the MCI_FROM flag or set it to 0 to start recording at the beginning of a new file. You can specify an ending position to terminate recording when recording to a new file.

If you record in overwrite mode to an existing file, you can use the MCI_TO and MCI_FROM flags to specify beginning and ending points of the waveform section that is to be overwritten. For example, if you record to an existing file 20 seconds long, and you begin recording at 5 seconds and end recording at 10 seconds, you still have a recording 20 seconds long. However, the section beginning at 5 seconds and ending at 10 seconds has been replaced.

+$#A Saving a Recorded File

When recording is complete, use the MCI_SAVE command message along with the MCI_SAVE_PARMS structure to save the recording before closing the device.

If you close the device without saving, the recorded data is lost.

+$#A Checking Input Levels on PCM Devices

To get the level of the input signal before recording on a pulse code modulated (PCM) waveform input device, use the MCI_STATUS command message. Specify the MCI_STATUS_ITEM flag and set the dwItem member in the MCI_STATUS_PARMS structure to MCI_WAVE_STATUS_LEVEL. The average input signal level is returned in the dwReturn member of the MCI_STATUS_PARMS structure. The left-channel value is in the high-order word, and the right- or mono-channel value is in the low-order word.

The input level is represented as an unsigned value. For 8-bit samples, this value is in the range 0 through 127 (0x7F). For 16-bit samples, it is in the range 0 through 32,767 (0x7FFF).

+$#A Example of Recording With a Waveform Audio Device

The following example function opens a waveform audio device with a new file, records for the specified time, plays the recording, and prompts the user to specify whether the recording should be saved as a file.

/*

* Uses the MCI_OPEN, MCI_RECORD, MCI_SAVE commands to record and

* save a waveform audio file. Returns 0L on success; otherwise,

* returns an MCI error code.

*/

DWORD recordWAVEFile(DWORD dwMilliSeconds)

{

UINT wDeviceID;

DWORD dwReturn;

MCI_OPEN_PARMS mciOpenParms;

MCI_RECORD_PARMS mciRecordParms;

MCI_SAVE_PARMS mciSaveParms;

MCI_PLAY_PARMS mciPlayParms;

/*

* Open a waveform device with a new file for recording.

*/

mciOpenParms.lpstrDeviceType = "waveaudio";

mciOpenParms.lpstrElementName = "";

if (dwReturn = mciSendCommand(0, MCI_OPEN,

MCI_OPEN_ELEMENT | MCI_OPEN_TYPE,

(DWORD)(LPVOID) &mciOpenParms)) {

/*

* Failed to open device. Don't close it; just return

* error.

*/

return dwReturn;

}

/* Device opened successfully. Get the device ID. */

wDeviceID = mciOpenParms.wDeviceID;

/*

* Begin recording and record for the specified number

* of milliseconds. Wait for recording to complete before

* continuing. Assume the default time format for the

* waveform device (milliseconds).

*/

mciRecordParms.dwTo = dwMilliSeconds;

if (dwReturn = mciSendCommand(wDeviceID, MCI_RECORD,

MCI_TO | MCI_WAIT, (DWORD)(LPVOID) &mciRecordParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

/* Play the recording and query user to save the file. */

mciPlayParms.dwFrom = 0;

if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,

MCI_FROM | MCI_WAIT,

(DWORD)(LPVOID) &mciPlayParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

if (MessageBox(hMainWnd, "Do you want to save this recording?",

"", MB_YESNO) == IDNO) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return 0;

}

/*

* Save the recording to a file named TEMPFILE.WAV. Wait for

* the operation to finish before continuing.

*/

mciSaveParms.lpfilename = "tempfile.wav";

if (dwReturn = mciSendCommand(wDeviceID, MCI_SAVE,

MCI_SAVE_FILE | MCI_WAIT,

(DWORD)(LPVOID) &mciSaveParms)) {

mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);

return dwReturn;

}

return 0;

}

+$#A Using the MCI MIDI Sequencer with High-Level Audio

The MCI MIDI sequencer plays standard MIDI files and Resource Interchange File Format (RIFF) MIDI files (RMID files). Standard MIDI files conform to the "Standard MIDI Files 1.0" specification. For information about this specification, see Introduction to Audio. Because RMID files are standard MIDI files with a RIFF header, information about standard MIDI files also applies to RMID files.

There are currently three variations of standard MIDI files. The MCI sequencer plays only two of these: format 0 and format 1 MIDI files.

Note When the MCI MIDI sequencer encounters a MIDI file not marked as being authored for Windows, it displays a dialog box informing the user. MIDI files authored for Windows should be marked with the MARKMIDI utility program. For additional information about MARKMIDI, see Using the MARKMIDI Utility.

• Querying for sequence division types

• Querying and Setting the MIDI Tempo

• Changing sequencer synchronization

+$#A Querying for Sequence Division Types

The division type of a MIDI sequence refers to the technique used to represent the time between MIDI events in the sequence. Use the MCI_STATUS command message and set the dwItem member of the MCI_STATUS_PARMS structure to MCI_SEQ_STATUS_DIVTYPE to determine the division type of a sequence.

If the MCI_STATUS command message succeeds, the dwReturn member of the MCI_STATUS_PARMS structure contains one of the following values to indicate the division type.

Value | Division type
MCI_SEQ_DIV_PPQN | PPQN (parts per quarter-note)
MCI_SEQ_DIV_SMPTE_24 | SMPTE, 24 frames per second (fps)
MCI_SEQ_DIV_SMPTE_25 | SMPTE, 25 fps
MCI_SEQ_DIV_SMPTE_30 | SMPTE, 30 fps
MCI_SEQ_DIV_SMPTE_30DROP | SMPTE, 30 fps drop frame

You must know the division type of a sequence to change or query its tempo. You can't change the division type of a sequence by using the MCI sequencer.

+$#A Querying and Setting the MIDI Tempo

Use the MCI_STATUS command message and set the dwItem member of the MCI_STATUS_PARMS structure to MCI_SEQ_STATUS_TEMPO to get the tempo of a sequence. If the MCI_STATUS command message succeeds, the dwReturn member of the MCI_STATUS_PARMS structure contains the current tempo.

To change tempo, use the MCI_SET command message with the MCI_SEQ_SET_PARMS structure. Specify the MCI_SEQ_SET_TEMPO flag and set the value of the dwTempo member of the structure to the desired tempo.

The way tempo is represented depends on the division type of the sequence. If the division type is PPQN, the tempo is represented in beats per minute. If the division type is one of the SMPTE division types, the tempo is represented in frames per second. For information about determining the division type of a sequence, Querying for Sequence Division Types.

+$#A Changing Sequencer Synchronization

To change the synchronization mode of a sequencer device, use the MCI_SET command message with the MCI_SEQ_SET_MASTER and MCI_SEQ_SET_SLAVE flags. Two members in the MCI_SEQ_SET_PARMS structure, dwMaster and dwSlave, are used to specify the master and slave synchronization modes.

The master synchronization mode controls synchronization information sent by the sequencer to an output port. The slave synchronization mode controls where the sequencer gets its timing information to play a MIDI file. Following are the constants for the dwMaster member and their corresponding master synchronization modes.

Constant | Synchronization mode
MCI_SEQ_MIDI | MIDI Synchronization. Send timing information to output port using MIDI timing clock messages.
MCI_SEQ_SMPTE | SMPTE Synchronization. Send timing information to output port using MIDI quarter-frame messages.
MCI_SEQ_NONE | No Synchronization. Send no timing information.

Following are the constants for the dwSlave member and their corresponding slave synchronization modes.

Constant | Synchronization mode
MCI_SEQ_FILE | File Synchronization. Get timing information from MIDI file.
MCI_SEQ_MIDI | MIDI Synchronization. Get timing information from input port using MIDI timing clock messages.
MCI_SEQ_SMPTE | SMPTE Synchronization. Get timing information from input port using MIDI quarter-frame messages.
MCI_SEQ_NONE | No Synchronization. Get timing information from MCI commands only and ignore timing information (such as tempo changes) that are in the MIDI file.

Note Currently, for master synchronization, the MCI MIDI sequencer supports only the No Synchronization mode (MCI_SEQ_NONE). For slave synchronization, it supports only the File Synchronization mode (MCI_SEQ_FILE) and the No Synchronization mode (MCI_SEQ_NONE).

+$#A The MIDI Mapper

The MIDI Mapper's standard patch services provide device-independent MIDI file playback for applications. You don't need to understand exactly how the MIDI Mapper works to use these services. The MIDI Mapper can be used with the MCI MIDI sequencer or with low-level MIDI output services.

To learn more about the MIDI Mapper or about authoring standard MIDI files for Windows, press the browse button (>>) to view more topics about how the MIDI Mapper works. For specific information about how to use the MIDI Mapper, see Using the MCI MIDI Sequencer or Low-Level Audio.

+$#A MIDI Notational Conventions

Unless stated otherwise, all references to MIDI channel numbers use the logical channel numbers 1 through 16. These logical channel numbers correspond to the physical channel numbers 0 through 15 that are actually part of the MIDI message. All references to MIDI program-change and key values use the physical values 0 through 127. All numbers are decimal unless preceded by 0x prefix, in which case they are hexadecimal.

In the discussion of the MIDI Mapper, the term source refers to the input side of the MIDI Mapper. The term destination refers to the output side of the MIDI Mapper. For example, a source channel is the MIDI channel of a message sent to the MIDI Mapper, and a destination channel is the MIDI channel of a message sent from the MIDI Mapper to an output device.

+$#A The MIDI Mapper and Windows

The MIDI Mapper is part of the system software. The following illustration shows how the MIDI Mapper relates to other elements of the audio services.

From the viewpoint of an application, the MIDI Mapper looks like another MIDI output device. The MIDI Mapper receives messages sent to it by the low-level MIDI output functions midiOutShortMsg and midiOutLongMsg. The MIDI Mapper modifies these messages and redirects them to a MIDI output device according to the current MIDI setup map. The current MIDI setup map is selected by the user by means of the MIDI Control Panel option. Only the user can select the current setup map; applications cannot change the current setup map.

+$#A The MIDI Mapper Architecture

The MIDI Mapper uses a MIDI setup map to determine how to translate and redirect messages it receives. A MIDI setup map consists of the following types of maps.

• Channel map

• Patch map

• Key map

The following illustration shows the roles of channel, patch, and key maps in a MIDI setup map.

+$#A The Channel Map

The channel map affects all MIDI channel messages. MIDI channel messages include note-on, note-off, polyphonic-key-aftertouch, control-change, program- change, channel-aftertouch, and pitch-bend-change messages. The MIDI Mapper uses a single channel map with an entry for each of the 16 MIDI channels. Each channel-map entry specifies the following:

• A destination channel for the MIDI message

• A destination output device for the MIDI message

• An optional patch map specifying other possible modifications for the MIDI message

The destination channel is set to one of the 16 MIDI channels. MIDI messages are modified to reflect each new channel assignment. For example, if the destination channel entry for MIDI channel 4 is set to 6, all MIDI messages sent to channel 4 will be mapped to channel 6, as shown in the following illustration.

In this example, the MIDI status byte 0x93 is mapped to 0x95. The low-order of a MIDI status byte specify the channel number. Source channels are set to either active or inactive. Messages sent to inactive source channels are ignored, so an inactive channel is in effect muted or turned off.

The destination output device is set to one of the available MIDI output devices. A MIDI output device can be an internal synthesizer or a physical MIDI output port.

MIDI system messages are MIDI messages (with status bytes) from 0xF0 to 0xFF. There is no channel associated with MIDI system messages, so they can't be mapped. MIDI system messages are sent to all MIDI output devices listed in a channel map.

+$#A Patch Maps

Each channel map entry can have an associated patch map. Patch maps affect MIDI program-change and volume-controller messages. Program-change messages tell a synthesizer to change the instrument sound (patch) for a specified channel. Volume-controller messages set the volume for a channel.

A patch map has a translation table with an entry for each of the 128 program-change values. Each patch map specifies the following:

• A destination program-change value

• A volume scalar

• An optional key map

When program-change messages are received by the MIDI Mapper, the destination program-change value is substituted for the program-change value in the message. For example, if the destination program-change value for program-change 16 is 18, the MIDI Mapper modifies the MIDI program-change message as shown in the following illustration.

+$#A The Volume Scalar

The purpose of the volume scalar is to allow adjustments between the relative output levels of different patches on a synthesizer. For example, if the bass patch on a synthesizer is too loud compared with its piano patch, you can change the setup map to scale the bass volume down or the piano volume up.

The volume scalar specifies a percentage value for changing all MIDI main-volume controller messages that follow an associated program-change message. For example, if the volume scalar value is 50%, the MIDI Mapper modifies MIDI main-volume controller messages as shown in the following illustration:

+$#A Key Maps

Each entry in the patch-map translation table can have an associated key map. Key maps affect note-on, note-off, and polyphonic-key-aftertouch messages. A key map has a translation table with an entry for each of the 128 MIDI key values. For example, if the entry for key value 60 is 72, the MIDI Mapper modifies MIDI note-on messages as shown in the following illustration.

Key maps are useful with synthesizers that have key-based percussion instruments with a particular percussion sound assigned to each key. Key maps are usually assigned to the first patch in the patch maps on the percussion channels (10 and 16).

+$#A Summary of Maps and MIDI Messages

Following are the status bytes for MIDI messages and the map types that affect each message.

MIDI status | Description | Map types
0x80−0x8F | Note-off | Channel maps, key maps
0x90−0x9F | Note-on | Channel maps, key maps
0xA0−0xAF | Polyphonic-key-aftertouch | Channel maps, key maps
0xB0−0xBF | Control-change | Channel maps, patch maps
0xC0−0xCF | Program-change | Channel maps, patch maps
0xD0−0xDF | Channel-aftertouch | Channel maps
0xE0−0xEF | Pitch-bend-change | Channel maps
0xF0−0xFF | System | Not mapped

• The high-order four bits represent the status value. The low-order four bits represent the channel information.

• Patch maps affect only controller 7 (main volume).

• System messages are sent to all devices listed in a channel map.

+$#A Authoring MIDI Files

The "MIDI 1.0 Detailed Specification" does not define any standard patch assignments for synthesizers. Therefore, when you create a MIDI file, it won't be reproduced correctly unless it is played back on the same MIDI synthesizer setup used to create it. For example, if you create a piano concerto for your Yamaha® DX7 and try to play it back on a Roland® LAPC-1, it may be played with a flute instead of a piano.

To enable MIDI files to be a viable format for representing music in multimedia computing, Windows provides MIDI authoring guidelines. These guidelines include a list of standard patch assignments and standard key assignments for percussion instruments. Using the MIDI Mapper, MIDI files authored in accordance with these guidelines can be played on any multimedia computer with internal or external MIDI synthesizers.

+$#A About Base-Level and Extended Synthesizers

Although it is difficult to clearly quantify distinctions between synthesizers, it is important to have some guidelines so you can create MIDI files that will play on all multimedia computers. The terms used to distinguish synthesizers for the purpose of authoring MIDI files are base-level synthesizer and extended synthesizer.

The distinctions between base-level and extended synthesizers are made solely on the number of instruments and notes the synthesizer can play, not on the quality or the cost of the synthesizer. Following are the minimum capabilities of base-level and extended synthesizers.

Synthesizer | Melodic instruments |  | Percussive instruments |
 | Number | Polyphony | Number | Polyphony
Base-level | 3 instruments | 6 notes | 3 instruments | 3 notes
Extended | 9 instruments | 16 notes | 8 instruments | 16 notes

Polyphony is the number of notes the synthesizer can play simultaneously. The polyphony expressed above applies to each group of instruments ⎯ melodic and percussive. For example, a base-level synthesizer is capable of playing six notes distributed among three melodic instruments and three notes distributed among three percussive instruments. Each melodic instrument is on a different MIDI channel; the percussive instruments are key-based ⎯ all on a single MIDI channel.

All multimedia computers provide at least a base-level synthesizer. Users can enhance their computer by adding internal or external synthesizers, which can be either base-level or extended synthesizers. When adding a synthesizer, the user must configure the MIDI Mapper to use the new device. If the MIDI Mapper is not configured, the instrument sounds will be incorrect when MIDI files are played, unless the application has the ability to send output directly to an internal synthesizer or output port. The MIDI Control Panel option allows a user to configure the MIDI Mapper as needed.

+$#A Authoring Guidelines for MIDI Files

Follow these guidelines to author device-independent MIDI files for Windows:

• Author for both base-level and extended synthesizer setups.

• Use MIDI channels 13 through 16 for base-level synthesizer data (reserve channel 16 for key-based percussion instruments).

• Use MIDI channels 1 through 10 for extended synthesizer data (reserve channel 10 for key-based percussion instruments).

• Prioritize MIDI data by putting crucial data in the lower-numbered channels.

• Limit the polyphony of nonpercussive channels to a total of 6 notes for base-level data and 16 notes for extended data.

• Limit the polyphony of percussive channels to a total of 3 notes for base-level data and 16 notes for extended data.

• Use the standard MIDI patch assignments and key assignments.

• Always send a program-change message to a channel to select a patch before sending other messages to that channel. For the two percussion channels (10 and 16), select program number 0.

• Always follow a MIDI program-change message with a MIDI main-volume controller message (controller number 7) to set the relative volume of the patch.

• Use a value of 80 (0x50) for the main-volume controller for normal listening levels. For quieter or louder levels, you can use lower or higher values.

• Use only the following MIDI messages: note-on with velocity, note-off, program change, pitch bend, main volume (controller 7), and damper pedal (controller 64). Internal synthesizers are required to respond to these messages and most MIDI musical instruments respond to them as well.

• Use the MARKMIDI utility program to mark MIDI files authored for Windows.

The following illustration summarizes the use of the 16 MIDI channels in a standard MIDI file authored for Windows.

+$#A Prioritizing MIDI Data

Synthesizers don't always fall cleanly into the base-level and extended designations defined earlier. It's up to the user to determine how to use synthesizers that are capable of more than the base-level requirements but do not fully meet the extended requirements. For this reason, it's important to prioritize the melodic data by putting the most critical data in lower-numbered channels. For example, a user may have a synthesizer capable of playing six melodic instruments with 12-note polyphony. The user can use this device as an extended synthesizer by setting up the MIDI Mapper to play only the first six melodic channels and ignore any information on channels 7, 8, and 9.

+$#A Standard MIDI Patch Assignments

The standard MIDI patch assignments for authoring MIDI files for use with Windows are based on the MIDI Manufacturers Association (MMA) General MIDI Mode specification. Following are the standard MIDI patch assignments.

Piano | Chromatic Percussion | Organ
0 Acoustic grand piano | 8 Celesta | 16 Hammond organ
1 Bright acoustic piano | 9 Glockenspiel | 17 Percussive organ
2 Electric grand piano | 10 Music box | 18 Rock organ
3 Honky-tonk piano | 11 Vibraphone | 19 Church organ
4 Rhodes piano | 12 Marimba | 20 Reed organ
5 Chorused piano | 13 Xylophone | 21 Accordion
6 Harpsichord | 14 Tubular bells | 22 Harmonica
7 Clavinet | 15 Dulcimer | 23 Tango accordion
Guitar | Bass | Strings
24 Acoustic guitar (nylon) | 32 Acoustic bass | 40 Violin
25 Acoustic guitar (steel) | 33 Electric bass (finger) | 41 Viola
26 Electric guitar (jazz) | 34 Electric bass (pick) | 42 Cello
27 Electric guitar (clean) | 35 Fretless bass | 43 Contrabass
28 Electric guitar (muted) | 36 Slap bass 1 | 44 Tremolo strings
29 Overdriven guitar | 37 Slap bass 2 | 45 Pizzicato strings
30 Distortion guitar | 38 Synth bass 1 | 46 Orchestral harp
31 Guitar harmonics | 39 Synth bass 2 | 47 Timpani
Ensemble | Brass | Reed
48 String ensemble 1 | 56 Trumpet | 64 Soprano sax
49 String ensemble 2 | 57 Trombone | 65 Alto sax
50 Synth. strings 1 | 58 Tuba | 66 Tenor sax
51 Synth. strings 2 | 59 Muted trumpet | 67 Baritone sax
52 Choir Aahs | 60 French horn | 68 Oboe
53 Voice Oohs | 61 Brass section | 69 English horn
54 Synth voice | 62 Synth. brass 1 | 70 Bassoon
55 Orchestra hit | 63 Synth. brass 2 | 71 Clarinet
Pipe | Synth Lead | Synth Pad
72 Piccolo | 80 Lead 1 (square) | 88 Pad 1 (new age)
73 Flute | 81 Lead 2 (sawtooth) | 89 Pad 2 (warm)
74 Recorder | 82 Lead 3 (calliope lead) | 90 Pad 3 (polysynth)
75 Pan flute | 83 Lead 4 (chiff lead) | 91 Pad 4 (choir)
76 Bottle blow | 84 Lead 5 (charang) | 92 Pad 5 (bowed)
77 Shakuhachi | 85 Lead 6 (voice) | 93 Pad 6 (metallic)
78 Whistle | 86 Lead 7 (fifths) | 94 Pad 7 (halo)
79 Ocarina | 87 Lead 8 (brass + lead) | 95 Pad 8 (sweep)
Sound Effects |  |
120 Guitar fret noise |  |
121 Breath noise |  |
122 Seashore |  |
123 Bird tweet |  |
124 Telephone ring |  |
125 Helicopter |  |
126 Applause |  |
127 Gunshot |  |

+$#A Standard MIDI Key Assignments

The standard MIDI key assignments for percussion instruments are based on the MMA General MIDI Mode specification. The following illustration shows the standard key assignments for MIDI files authored for Windows.

+$#A Using the MARKMIDI Utility

The MARKMIDI utility program marks MIDI files as following the authoring guidelines for Windows MIDI files. The program does not verify that the file is authored correctly; it only marks the file by adding a sequencer-specific meta-event to the first track chunk. MARKMIDI has the following command syntax.

MARKMIDI src-filename dest-filename

If the MCI MIDI sequencer is used to play a MIDI file not marked by MARKMIDI, it displays a dialog box informing the user that the file is not authored for Windows.

Note Do not use MARKMIDI to mark MIDI files unless the files were authored according to the guidelines presented in this guide. If your application uses MIDI files not authored to these guidelines, the application must instruct users to install or create the proper maps by using the MIDI Mapper Control Panel application. For example, if your application uses MIDI files following the General MIDI channel recommendations, your users should create or install a General MIDI map for their synthesizers.

+$#A High-Level Audio Functions

Following are the functions used with high-level audio services.

mciGetCreatorTask
mciGetDeviceID
mciGetErrorString
mciGetYieldProc
mciSendCommand
mciSendString
mciSetYieldProc
PlaySound
sndPlaySound

+$#A Low-Level Audio

This overview explains how to use the low-level audio services of Microsoft® Windows® to manage playback and recording of waveform and Musical Instrument Digital Interface (MIDI) audio.

For an overview of Windows audio services, see Introduction to Audio. For a discussion of high-level audio services, see High-Level Audio.

This overview covers the following topics:

• Using low-level audio services

• Playing waveform audio

• Recording waveform audio

• Playing MIDI audio

• Recording MIDI audio

• Using auxiliary audio devices

• Using audio clipboard formats

You should have a basic knowledge of digital audio and MIDI before reading this overview. If you need additional information on these subjects, see Introduction to Audio.

+$#A Function Prefixes

Low-level audio function names begin with the following prefixes.

Prefix | Description
aux | Auxiliary audio functions
midi | MIDI audio functions
wave | Waveform audio functions

+$#A Using Low-Level Audio Services

Low-level audio services control different types of audio devices, including waveform, MIDI, and auxiliary audio devices. Many of the concepts for using low-level services apply to more than one type of device. The following topics present general information on using low-level audio services:

• Querying audio devices

• Opening and closing device drivers

• Allocating and preparing audio data blocks

• Managing audio data blocks

• Using the MMTIME structure

• Handling errors with audio functions

Subsequent topics in this overview discuss the use of specific types of audio devices.

+$#A Querying Audio Devices

Before playing or recording audio, you must determine the capabilities of the audio hardware present in the system. Audio capability can vary from one multimedia computer to the next; applications should not make assumptions about the audio hardware present in a given system.

+$#A Getting the Number of Devices

The Win32 application programming interface (API) provides the following functions to determine how many devices of a certain type are available in a given system.

Function | Description
auxGetNumDevs | Retrieves the number of auxiliary audio devices present in the system.
midiInGetNumDevs | Retrieves the number of MIDI input devices present in the system.
midiOutGetNumDevs | Retrieves the number of MIDI output devices present in the system.
waveInGetNumDevs | Retrieves the number of waveform input devices present in the system.
waveOutGetNumDevs | Retrieves the number of waveform output devices present in the system.

Audio devices are identified by a device identifier. The device identifier is determined implicitly from the number of devices present in a given system. Device identifiers range from zero to one less than the number of devices present. For example, if there are two MIDI output devices in a system, valid device identifiers are 0 and 1.

+$#A Getting the Capabilities of a Device

After you determine how many devices of a certain type are present in a system, you can inquire about the capabilities of each device. The Win32 API provides the following functions to determine the capabilities of audio devices.

Function | Description
auxGetDevCaps | Retrieves the capabilities of a given auxiliary audio device.
midiInGetDevCaps | Retrieves the capabilities of a given MIDI input device.
midiOutGetDevCaps | Retrieves the capabilities of a given MIDI output device.
waveInGetDevCaps | Retrieves the capabilities of a given waveform input device.
waveOutGetDevCaps | Retrieves the capabilities of a given waveform output device.

Each of these functions takes a pointer to a structure that it fills with information on the capabilities of a specified device. Following are the structures that correspond to each of the device-inquiry functions.

Function | Structure
auxGetDevCaps | AUXCAPS
midiInGetDevCaps | MIDIINCAPS
midiOutGetDevCaps | MIDIOUTCAPS
waveInGetDevCaps | WAVEINCAPS
waveOutGetDevCaps | WAVEOUTCAPS

+$#A Opening and Closing Low-Level Audio Device Drivers

After getting the capabilities of an audio device, you must open the device before you can use it. Audio devices aren't guaranteed to be shareable, so a particular device might not be available when you request it. If this happens, you should notify the user and allow the user to try to open the device again. When you open an audio device, be sure to close it as soon as you finish using it.

The Win32 API provides the following functions to open and close different types of audio devices.

Function | Description
midiInOpen | Opens a specified MIDI input device for recording.
midiInClose | Closes a specified MIDI input device.
midiOutOpen | Opens a MIDI output device for playback.
midiOutClose | Closes a specified MIDI output device.
waveInOpen | Opens a waveform input device for recording.
waveInClose | Closes a specified waveform input device.
waveOutOpen | Opens a waveform output device for playback.
waveOutClose | Closes a specified waveform output device.

These functions are discussed in detail later in this section.

+$#A About Device Handles

Each function that opens an audio device takes as parameters a device identifier, a pointer to a memory location, and some parameters unique to each type of device. The memory location is filled with a device handle. Use this device handle of identify the open audio device when calling other audio functions.

The distinction between audio-device identifiers and audio-device handles is subtle, but very important. Don't confuse the two in your application. Following are the differences between device identifiers and device handles.

• Device identifiers are determined implicitly from the number of devices present in a system, which is obtained by using the device-numbering functions (auxGetNumDevs, midiInGetNumDevs, and so on).

• Device handles are returned when device drivers are opened by using the device-opening functions (midiInOpen, midiOutOpen, and so on)

• The only functions that take device identifiers as parameters are the device-capabilities functions (waveOutGetDevCaps, midiInGetDevCaps, and so on), the device-opening functions, and volume functions (waveOutGetVolume, waveOutSetVolume, and so on). All other functions take device handles.

There are no functions for opening and closing auxiliary audio devices. Auxiliary audio devices don't need to be opened and closed like MIDI and waveform devices because there is no continuous data transfer associated with them. All auxiliary audio functions take device identifiers to identify devices.

+$#A Allocating and Preparing Audio Data Blocks

Some low-level audio functions require applications to allocate data blocks to pass to the device drivers for playback or recording purposes. Each of these functions uses a structure (or header) to describe its data block. The following table identifies these functions and their associated header structures. (The MMSYSTEM.H file defines the data structures for these headers.)

Function | Header structure | Purpose
waveOutWrite | WAVEHDR | Waveform playback
waveInAddBuffer | WAVEHDR | Waveform recording
midiOutLongMsg | MIDIHDR | MIDI system-exclusive playback
midiInAddBuffer | MIDIHDR | MIDI system-exclusive recording

Before you use one of the functions listed above to pass a data block to a device driver, you must allocate memory for the data block according to the guidelines discussed in the following topics.

+$#A Allocating Memory for Audio Data Blocks

Before preparing a data block, you must allocate memory for the data block and the header structure that describes the data block.

To allocate memory, use the GlobalAlloc function with the GMEM_MOVEABLE flag to get a handle of the memory object. Then pass this handle of the GlobalLock function to get a pointer to the memory object.

To free a data block, use GlobalUnlock and GlobalFree.

+$#A Preparing Audio Data Blocks

Before you pass an audio data block to a device driver, you must prepare the data block by passing it to a header-preparation function. When the device driver is finished with the data block and returns it, you must clean up this preparation by passing the data block to a header clean-up function before any allocated memory can be freed.

Windows provides the following functions for preparing and cleaning up audio data blocks.

Function | Description
midiInPrepareHeader | Prepares a MIDI-input data block.
midiInUnprepareHeader | Cleans up the preparation on a MIDI-input data block.
midiOutPrepareHeader | Prepares a MIDI-output data block.
midiOutUnprepareHeader | Cleans up the preparation on a MIDI-output data block.
waveInPrepareHeader | Prepares a waveform-input data block.
waveInUnprepareHeader | Cleans up the preparation on a waveform-input data block.
waveOutPrepareHeader | Prepares a waveform-output data block.
waveOutUnprepareHeader | Cleans up the preparation on a waveform-output data block.

+$#A Managing Audio Data Blocks

Unless the audio data is small enough to be contained in a single data block, applications must continually supply the device driver with data blocks until playback or recording is complete. This is true for waveform input and output, and for MIDI system-exclusive input messages. Regular MIDI channel messages don't require data blocks for input or output.

Even if a single data block is used, applications must be able to determine when a device driver is finished with the data block so the application can free the memory associated with the data block and header structure. There are three ways to determine when a device driver is finished with a data block:

• Specify a window to receive a message sent by the driver when it is finished with a data block.

• Specify a callback function to receive a message sent by the driver when it is finished with a data block.

• Poll a bit in the dwFlags member of the WAVEHDR or MIDIHDR data structure sent with each data block.

If an application doesn't get a data block to the device driver when needed, there can be an audible gap in playback or a loss of incoming recorded information. Use a double-buffering scheme to stay at least one data block ahead of the device driver.

Note To get time-stamped MIDI input data, you must use a callback function.

+$#A Using a Window to Process Driver Messages

The easiest type of callback function to use to process driver messages is a window callback. To use a window callback, specify the CALLBACK_WINDOW flag in the dwFlags parameter and a window handle as the dwCallback parameter of the device-opening function. Driver messages will be sent to the window-procedure function for the window identified by the handle in dwCallback.

Messages sent to the window function are specific to the audio device type used. For details on these messages, see the topics later in this section on using window messages for each specific audio device type.

+$# Using a Callback Function to Process Low-Level Audio Driver Messages

You can also write your own low-level callback function to process messages sent by the device driver. To use a low-level callback function, specify the CALLBACK_FUNCTION flag in the dwFlags parameter and the address of the callback in the dwCallback parameter of the device-opening function.

Messages sent to a callback function are similar to messages sent to a window, except that they have two doubleword parameters instead of one unsigned-integer and one doubleword parameter. For details on these messages, see the sections later in this section about using low-level callbacks for each specific audio device type.

Callback functions for the low-level audio services are often called from another thread running asynchronously from the application's thread. It is therefore important to use critical sections to protect data shared between the callback routine and the rest of the application. For more information about critical sections, see Synchronization.

There are several differences between callback functions for the low-level audio services under Windows NT and under Windows 3.1:

• Under Windows 3.1, a low-level audio callback function is called at interrupt time. The call to a low-level audio callback function occurs on a separate thread under Windows NT.

• Under Windows 3.1, a low-level audio callback function has to be in a dynamic-link library. Under Windows NT, there is no such restriction.

• Under Windows 3.1, a low-level audio callback function must be exported in a .DEF file. Under Windows NT, that is not necessary.

If source code must be compatible with Windows 3.1, it should follow the Windows 3.1 rules as noted briefly above and specified more fully in the Windows 3.1 documentation.

One other item should be noted carefully. There is a limited set of APIs that a Windows 3.1 low-level audio callback function may call. In Win32, that strict limitation has been removed. However, it is strongly recommended that a Windows NT low-level audio callback function restrain its calls to a similar set of APIs. Here is the Windows NT recommended list:

EnterCriticalSection | ReleaseSemaphore
LeaveCriticalSection | SetEvent
midiOutLongMsg | timeGetSystemTime
midiOutShortMsg | timeGetTime
OutputDebugString | timeKillEvent
PostMessage | timeSetEvent
PostThreadMessage |

If an application's low-level audio callback function does stray from this list, it should be careful not to call APIs that take a long time to complete.

In particular, calling wave and midi APIs may result in a deadlock.

If a Win32 low-level audio callback shares data with other code, a Critical Section or similar mutual exclusion mechanism should be used to protect the integrity of the data.

Use one of the following techniques to pass instance data from an application to a low-level callback function residing in a dynamic link library:

• Pass the instance data using the dwInstance parameter of the function that opens the device driver.

• Pass the instance data using the dwUser member of the WAVEHDR and MIDIHDR structures that identify an audio data block being sent to a device driver.

If you need more than 32 bits of instance data, pass a pointer to a structure containing the additional information.

+$#A Managing Low-Level Audio Data Blocks by Polling

In addition to using a callback function, you can poll the dwFlags member of a WAVEHDR or MIDIHDR structure to determine when an audio device is finished with a data block. There are times when it's better to poll dwFlags rather than wait for a window to receive messages from the drivers. For example, immediately after you call the waveOutReset function to release pending data blocks, you can poll to be sure that the data blocks are indeed done before proceeding to call the waveOutUnprepareHeader function and free the memory for the data block.

MMSYSTEM.H defines two flags for testing the dwFlags member: WHDR_DONE for a WAVEHDR structure, and MHDR_DONE for a MIDIHDR structure.

+$#A Using the MMTIME Structure

Windows uses a structure called MMTIME to represent time. Low-level audio functions that use MMTIME include waveInGetPosition and waveOutGetPosition. The timeGetSystemTime function also uses MMTIME to represent system time.

+$#A Setting the Time Format

MMTIME can represent time in one or more different formats including milliseconds, samples, Society of Motion Picture and Television Engineers (SMPTE), and MIDI song-pointer formats. The wType member specifies the format used to represent time. Before calling a function that uses the MMTIME structure, you must set the wType member to indicate your requested time format. Be sure to check wType after the call to see if the requested time format is supported. If the requested time format is not supported, the time is specified in an alternate time format selected by the device driver and the wType member is changed to indicate the selected time format. MMSYSTEM.H defines the following flags for the wType member of the MMTIME structure.

Flag | Description
TIME_MS | Milliseconds
TIME_SAMPLES | Number of waveform audio samples
TIME_BYTES | Number of waveform audio bytes
TIME_SMPTE | SMPTE time
TIME_MIDI | MIDI song-position pointer

For details on using MMTIME with the waveOutGetPosition function, seeGetting the Current Playback Position.

+$#A Getting the System Time

Use the timeGetSystemTime or timeGetTime function to get the system time. System time is defined as the time (in milliseconds) since Windows was started. For more information on timeGetSystemTime and timeGetTime, see Multimedia Timer and Joystick Services.

Créé avec HelpNDoc Personal Edition: Outil de création d'aide complet


Static Controls.rtf

+$#AK Static Controls

The Microsoft® Windows® operating system provides dialog boxes and controls to support communication between an application and the user. A static control is a control that enables an application to provide the user with certain types of text and graphics that typically require no response.

+$#A About Static Controls

Applications often use static controls to label other controls or to separate a group of controls. Although static controls are child windows, they cannot be selected. Therefore, they cannot receive the keyboard focus and cannot have a keyboard interface. A static control that has the SS_NOTIFY style receives mouse input, notifying the parent window when the user clicks or double clicks the control. Static controls belong to the STATIC window class.

Although static controls can be used in overlapped, pop-up, and child windows, they are designed for use in dialog boxes, where Windows standardizes their behavior. By using static controls outside dialog boxes, a developer increases the risk that the application might behave in a nonstandard fashion. Typically, a developer either uses static controls in dialog boxes or uses the SS_OWNERDRAW style to create customized static controls.

For more information about dialog boxes, see Dialog Boxes. For more information about window classes, see Window Classes. For more information about window subclassing, see Window Procedures.

+$#A Static Control Types and Styles

The Microsoft® Win32® application programming interface (API) provides four types of static controls: simple graphics, text, images, and owner-drawn. Each type has one or more styles.

+$#A Simple Graphics

A simple graphics static control displays a frame or a filled rectangle. A frame can be drawn in a number of styles, included black, gray, or white. In addition, a frame and can drawn with an etched style to give it a three-dimensional appearance. The frame styles include SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME, SS_ETCHEDHORZ, SS_ETCHEDVERT, and SS_ETCHEDFRAME.

A rectangle can be filled with color in one of three styles: black, gray, or white. These styles are defined by the constants SS_BLACKRECT, SS_GRAYRECT, and SS_WHITERECT.

+$#A Text

A text static control displays text in a rectangle in one of five styles: left-aligned without wordwrap, left aligned with wordwrap, centered, right-aligned, or "simple." These styles are defined by the constants SS_LEFTNOWORDWRAP, SS_LEFT, SS_CENTER, SS_RIGHT, and SS_SIMPLE, respectively. Windows rearranges the text in these controls in predefined ways, except for "simple" text, which is not rearranged.

An application can change the text in a text static control at any time by using the SetWindowText function or the WM_SETTEXT message.

The system displays as much text as it can in the static control and clips whatever does not fit. To calculate an appropriate size for the control, retrieve the font metrics for the text. For more information about fonts and font metrics, see Fonts and Text.

+$#A Images

An image static control can display bitmaps, icons (including animated icons), or enhanced metafiles. The type of graphic that a particular static control displays depends on the control's style: SS_BITMAP, SS_ICON, or SS_ENHMETAFILE. An application specifies the style when it creates the control and also specifies the handle of the bitmap, icon, or metafile for the control to display. After the control is created, an application can associate a different graphic with the control by sending it an STM_SETIMAGE message, specifying the handle of the new graphic object. An application can retrieve the handle of the graphic object currently associated with a static control by sending it an STM_GETIMAGE message. An application sends messages to a static control by using the SendDlgItemMessage function.

+$#A Owner-Drawn

By using the SS_OWNERDRAW style, an application can take responsibility for painting a static control. The parent window of an owner-drawn static control (its owner) receives a WM_DRAWITEM message whenever the static control needs to be painted. The message includes the address of a DRAWITEMSTRUCT structure that contains information that the owner window uses when drawing the control.

+$#A Static Control Styles

Many developers create dialog boxes by using standalone tools and so may not need to specify static control styles. However, if a developer uses the CreateWindow or CreateWindowEx function to create a static control for an application, the developer must use the following constants to define the style.

Style

Description

SS_BITMAP

Specifies a bitmap is to be displayed in the static control. The given text is the name of a bitmap (not a filename) defined elsewhere in the resource file. The style ignores the nWidth and nHeight parameters; the control automatically sizes itself to accommodate the bitmap.

SS_BLACKFRAME

Specifies a box with a frame drawn in the same color as the window frames. This color is black in the default Windows color scheme.

SS_BLACKRECT

Specifies a rectangle filled with the current window frame color. This color is black in the default Windows color scheme.

SS_CENTER

Specifies a simple rectangle and centers the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next centered line.

SS_CENTERIMAGE

Specifies that, if the bitmap or icon is smaller than the client area of the static control, the rest of the client area is filled with the color of the pixel in the top left corner of the bitmap or icon. If the static control contains a single line of text, the text is centered vertically in the client area of the control.

SS_ENHMETAFILE

Specifies an enhanced metafile is to be displayed in the static control. The given text is the name of a metafile. An enhanced metafile static control has a fixed size; the metafile is scaled to fit the static control's client area.

SS_ETCHEDFRAME

Draws the frame of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge function.

SS_ETCHEDHORZ

Draws the top and bottom edges of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge function.

SS_ETCHEDVERT

Draws the left and right edges of the static control using the EDGE_ETCHED edge style. For more information, see the DrawEdge function.

SS_GRAYFRAME

Specifies a box with a frame drawn with the same color as the screen background (desktop). This color is gray in the default Windows color scheme.

SS_GRAYRECT

Specifies a rectangle filled with the current screen background color. This color is gray in the default Windows color scheme.

SS_ICON

Specifies an icon is to be displayed in the dialog box. The given text is the name of an icon (not a filename) defined elsewhere in the resource file. The icon can be an animated cursor. The style ignores the nWidth and nHeight parameters; the control automatically sizes itself to accommodate the icon.

SS_LEFT

Specifies a simple rectangle and left-aligns the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next left-aligned line.

SS_LEFTNOWORDWRAP

Specifies a simple rectangle and left-aligns the given text in the rectangle. Tabs are expanded, but words are not wrapped. Text that extends past the end of a line is clipped.

SS_NOPREFIX

Prevents interpretation of any ampersand (&) characters in the control's text as accelerator prefix characters. These are displayed with the ampersand removed and the next character in the string underlined. This static control style may be included with any of the defined static controls.

An application can combine SS_NOPREFIX with other styles by using the bitwise OR (|) operator. This can be useful when filenames or other strings that may contain an ampersand (&) must be displayed in a static control in a dialog box.

SS_NOTIFY

Sends the parent window STN_CLICKED, STN_DBLCLK, STN_DISABLE, and STN_ENABLE notification messages when the user clicks or double-clicks the control.

SS_OWNERDRAW

Specifies that the owner of the static control is responsible for drawing the control. The owner window receives a WM_DRAWITEM message whenever the control needs to be drawn.

SS_REALSIZEIMAGE

Prevents a static icon or bitmap control (that is, static controls that have the SS_ICON or SS_BITMAP style) from being resized as it is loaded or drawn. If the icon or bitmap is larger than the destination area, the image is clipped.

SS_RIGHT

Specifies a simple rectangle and right-aligns the given text in the rectangle. The text is formatted before it is displayed. Words that extend past the end of a line are automatically wrapped to the beginning of the next right-aligned line.

SS_RIGHTJUST

Specifies that the lower right corner of a static control with the SS_BITMAP or SS_ICON style is to remain fixed when the control is resized. Only the top and left sides are adjusted to accommodate a new bitmap or icon.

SS_SIMPLE

Specifies a simple rectangle and displays a single line of left-aligned text in the rectangle. The text line cannot be shortened or altered in any way. The control's parent window or dialog box must not process the WM_CTLCOLORSTATIC message.

SS_SUNKEN

Draws a half-sunken border around a static control.

SS_WHITEFRAME

Specifies a box with a frame drawn with the same color as the window background. This color is white in the default Windows color scheme.

SS_WHITERECT

Specifies a rectangle filled with the current window background color. This color is white in the default Windows color scheme.

+$#A Static Control Default Message Processing

The window procedure for the predefined static control window class performs default processing for all messages that the static control procedure does not process. When the static control returns FALSE for any message, the predefined window procedure checks the messages and carries out the default action described in the following table. In the table, a text static control is a static control with the style SS_LEFTNOWORDWRAP, SS_LEFT, SS_CENTER, SS_RIGHT, or SS_SIMPLE.

Message | Default action
WM_CREATE | Loads the graphic object and sizes the window to the object's size, for graphic static controls. Takes no action for other static controls.
WM_DESTROY | Frees and destroys any graphic object, for graphic static controls. Takes no action for other static controls.
WM_ENABLE | Repaints visible static controls.
WM_ERASEBKGND | Returns TRUE, indicating the control erases the background.
WM_GETDLGCODE | Returns DLGC_STATIC.
WM_GETFONT | Returns the handle of the font for text static controls.
WM_GETTEXTLENGTH | Returns the length, in characters, of the text for a text static control.
WM_LBUTTONDBLCLK | Sends the parent window an STN_DBLCLK notification message if the control style is SS_NOTIFY.
WM_LBUTTONDOWN | Sends the parent window an STN_CLICKED notification message if the control style is SS_NOTIFY.
WM_NCLBUTTONDBLCLK | Sends the parent window an STN_DBLCLK notification message if the control style is SS_NOTIFY.
WM_NCLBUTTONDOWN | Sends the parent window an STN_CLICKED notification message if the control style is SS_NOTIFY.
WM_NCHITTEST | Returns HTCLIENT if the control style is SS_NOTIFY; otherwise, returns HTTRANSPARENT.
WM_PAINT | Repaints the control.
WM_SETFONT | Sets the font and repaints for text static controls.
WM_SETTEXT | Sets the text and repaints for text static controls.

The predefined window procedure passes all other messages to DefWindowProc for default processing.

+$#A Using Static Controls

The following example uses a timer and the STM_SETIMAGE message to animate an icon in a dialog box. The icon handles and the icon identifier (IDI_ICON) are defined in a global header file.

LRESULT APIENTRY StaticProc(hDlg, message, wParam, lParam)

HWND hDlg; /* window handle of dialog box */

UINT message; /* type of message */

UINT wParam; /* message-specific information */

LONG lParam;

{

UINT idTimer = 1;

static UINT i;

static HICON aIcons[11];

switch (message) {

case WM_INITDIALOG: /* initialize dialog box */

i = 0;

/* Load icons("hinst" is defined globally). */

hiconOne = LoadIcon(hinst, "OneIco");

aIcons[i] = hiconOne;

hiconTwo = LoadIcon(hinst, "TwoIco");

aIcons[++i] = hiconTwo;

.

. /* Continue with the remaining icons. */

.

i = 0;

/*

* Use STM_SETIMAGE to associate an icon with the

* IDI_ICON identifier.

*/

SendDlgItemMessage(hDlg, /* dialog box window handle */

IDI_ICON, /* icon identifier */

STM_SETIMAGE, /* message to send */

(WPARAM) IMAGE_ICON, /* image type */

(LPARAM) aIcons[i++]); /* icon handle */

/* Set a timer for 50-millisecond intervals. */

SetTimer(hDlg, idTimer, 50, (TIMERPROC) NULL);

return TRUE;

case WM_TIMER:

/*

* Use STM_SETIMAGE to associate a new icon with

* the IDI_ICON identifier whenever a WM_TIMER

* message is received.

*/

SendDlgItemMessage(hDlg, /* dialog box window handle */

IDI_ICON, /* icon identifier */

STM_SETIMAGE, /* message to send */

(WPARAM) IMAGE_ICON, /* image type */

(LPARAM) aIcons[i++]); /* new icon handle */

if (i == 10)

i = 0;

break;

case WM_COMMAND:

if (wParam == IDOK

|| wParam == IDCANCEL) {

EndDialog(hDlg, TRUE);

return TRUE;

}

return 0;

case WM_DESTROY: /* clean up */

KillTimer(hDlg, idTimer);

DeleteObject(hiconOne);

DeleteObject(hiconTwo);

.

. /* Continue with the remaining icons. */

.

return 0;

}

return FALSE;

UNREFERENCED_PARAMETER(lParam);

}

+$#A Static Control Messages

Following are the messages used with static controls. (STM_GETICON and STM_SETICON are used only with icons.)

STM_GETICON
STM_SETICON
WM_CTLCOLORSTATIC

Créé avec HelpNDoc Personal Edition: Générer des livres électroniques EPub facilement


Status Windows.rtf

+$#AK Status Windows

A status window is a horizontal window at the bottom of a parent window in which an application can display various kinds of status information. The status window can be divided into parts to display more than one type of information. The following illustration shows the status bar in the Microsoft® Windows® Paint application.

{ewc msdncd, EWGraphic, group10604 0 /a "SDK_01.bmp"}

+$#A About Status Windows

You can create a status window by using the CreateStatusWindow function or by using the CreateWindowEx function and specifying the STATUSCLASSNAME window class. To ensure that the common control dynamic-link library (DLL) is loaded, use the InitCommonControls function first. After you create the status window, you can divide it into parts, set the text for each part, and control the appearance of the window by using status window messages.

+$#A Types and Styles

The default position of a status window is along the bottom of the parent window, but you can specify the CCS_TOP style to have it appear at the top of the parent window's client area.

You can specify the SBARS_SIZEGRIP style to include a sizing grip at the right end of the status window. A sizing grip is similar to a sizing border; it is a rectangular area that the user can click and drag to resize the parent window.

Note You can combine the CCS_TOP and SBARS_SIZEGRIP styles. Doing so, however, is not recommended, because the resulting sizing grip is not functional even though the system draws it in the status window.

+$#A Size and Height

The window procedure for the status window automatically sets the initial size and position of the window, ignoring the values specified in the CreateWindowEx function. The width is the same as that of the parent window's client area. The height is based on the metrics of the font that is currently selected into the status window's device context and on the width of the window's borders.

The window procedure automatically adjusts the size of the status window whenever it receives a WM_SIZE message. Typically, when the size of the parent window changes, the parent sends a WM_SIZE message to the status window.

An application can set the minimum height of a status window's drawing area by sending the window an SB_SETMINHEIGHT message, specifying the minimum height, in pixels. The drawing area does not include the window's borders. A minimum height is useful for drawing in an owner-drawn status window. For more information about owner-drawn status windows, see Owner-Drawn Status Windows.

You retrieve the widths of the borders of a status window by sending the window an SB_GETBORDERS message. The message includes the pointer to a three element array that receives the widths.

+$#A Multiple-Part Status Windows

A status window can have many different parts, each displaying a different line of text. You divide a status window into parts by sending the window an SB_SETPARTS message, specifying the number of parts to create and the pointer to an integer array. The array contains one element for each part, and each element specifies the client coordinate of the right edge of a part.

A status window can have a maximum of 255 parts, although applications typically use far fewer than that. You retrieve a count of the parts in a status window, as well as the coordinate of the right edge of each part, by sending the window an SB_GETPARTS message.

+$#A Status Window Text Operations

You set the text of any part of a status window by sending the SB_SETTEXT message, specifying the zero-based index of a part, a pointer to the string to draw in the part, and the technique for drawing the string. The drawing technique determines whether the text has a border and, if it does, the style of the border. It also determines whether the parent window is responsible for drawing the text. For more information, see the following topic.

By default, text is left-aligned within the specified part of a status window. You can embed tab characters (\ t) in the text to center or right-align it. Text to the right of a single tab character is centered, and text to the right of a second tab character is right-aligned.

To retrieve text from a status window, use the SB_GETTEXTLENGTH and SB_GETTEXT messages.

If your application uses a status window that has only one part, you can use the WM_SETTEXT, WM_GETTEXT, and WM_GETTEXTLENGTH messages to perform text operations. These messages deal only with the part that has an index of zero, allowing you to treat the status window much like a static text control.

To display a line of status without creating a status window, use the DrawStatusText function. The function uses the same techniques to draw the status as the window procedure for the status window, but it does not automatically set the size and position of the status information. When calling the function, you must specify the size and position of the status information, as well as the device context of the window in which to draw it.

+$#A Owner-Drawn Status Windows

You can define individual parts of a status window to be owner-drawn parts. Using this technique gives you more control than you would otherwise have over the appearance of the window part. For example, you can display a bitmap rather than text in a part or draw text using a different font.

To define a window part as owner-drawn, send the SB_SETTEXT message to the status window, specifying the part and the SBT_OWNERDRAW drawing technique. When SBT_OWNERDRAW is specified, the lParam parameter is a 32-bit application-defined value that the application can use when drawing the part. For example, you can specify a font handle, a bitmap handle, a pointer to a string, and so on.

When a status window needs to draw an owner-drawn part, it sends the WM_DRAWITEM message to the parent window. The wParam parameter of the message is the child window identifier of the status window, and the lParam parameter is a pointer to a DRAWITEMSTRUCT structure. The parent window uses the information in the structure to draw the part. For an owner-drawn part of a status window, DRAWITEMSTRUCT contains the following information.

Member | Description
CtlType | Undefined; do not use.
CtlID | Child-window identifier of the status window.
itemID | Zero-based index of the part to be drawn.
itemAction | Undefined; do not use.
itemState | Undefined; do not use.
hwndItem | Handle to the status window.
hDC | Handle to the device context of the status window.
rcItem | Coordinates of the window part to be drawn. The coordinates are relative to the upper left corner of the status window.

itemData

Application-defined 32-bit value specified in the lParam parameter of the SB_SETTEXT message.

+$#A Simple Mode Status Windows

You put a status window into "simple mode" by sending it an SB_SIMPLE message. A simple mode status window displays only one part. When the text of the window is set, the window is invalidated, but it is not redrawn until the next WM_PAINT message. Waiting for the message reduces screen flicker by minimizing the number of times the window is redrawn. A simple mode status window is useful for displaying help text for menu items while the user is scrolling through a menu.

The string that a status window displays while in simple mode is maintained separately from the strings that it displays while not in simple mode. This means that you can put the window in simple mode, set its text, and switch back to nonsimple mode without the text being used in either mode.

When setting the text of a simple mode status window, you can specify any drawing technique except SBT_OWNERDRAW. A simple mode status window does not support owner drawing.

+$#A Default Status Window Message Processing

This section describes the window messages handled by the window procedure for the predefined STATUSCLASSNAME window class.

Message | Default processing
WM_CREATE | Initializes the status window.
WM_DESTROY | Frees resources allocated for the status window.
WM_GETFONT | Returns the handle to the current font with which the status window draws its text.
WM_GETTEXT | Copies the text from the first part of a status window to a buffer. It returns a 32-bit value that specifies the length, in characters, of the text and the technique used to draw the text.
WM_GETTEXTLENGTH | Returns a 32-bit value that specifies the length, in characters, of the text in the first part of a status window and the technique used to draw the text.
WM_NCHITTEST | Returns the HTBOTTOMRIGHT value if the mouse cursor is in the sizing grip, causing the system to display the sizing cursor. If the mouse cursor is not in the sizing grip, the status window passes this message to the DefWindowProc function.
WM_PAINT | Paints the invalid region of the status window.
WM_SETFONT | Selects the font handle into the device context for the status window.
WM_SETTEXT | Copies the specified text into the first part of a status window, using the default drawing operation (specified as zero). It returns TRUE if successful or FALSE otherwise.
WM_SIZE | Resizes the status window based on the current width of the parent window's client area and the height of the current font of the status window.

+$#A Using Status Windows

The following example creates a status window that has a sizing grip and divides the window into four equal parts based on the width of the parent window's client area.

// DoCreateStatusWindow - creates a status window and divides it into

// the specified number of parts.

// Returns the handle to the status window.

// hwndParent - parent window for the status window

// nStatusID - child window identifier

// hinst - handle to the application instance

// nParts - number of parts into which to divide the status window

HWND DoCreateStatusWindow(HWND hwndParent, int nStatusID,

HINSTANCE hinst, int nParts)

{

HWND hwndStatus;

RECT rcClient;

HLOCAL hloc;

LPINT lpParts;

int i, nWidth;

// Ensure that the common control DLL is loaded.

InitCommonControls();

// Create the status window.

hwndStatus = CreateWindowEx(

0, // no extended styles

STATUSCLASSNAME, // name of status window class

(LPCTSTR) NULL, // no text when first created

SBARS_SIZEGRIP | // includes a sizing grip

WS_CHILD, // creates a child window

0, 0, 0, 0, // ignores size and position

hwndParent, // handle to parent window

(HMENU) nStatusID, // child window identifier

hinst, // handle to application instance

NULL); // no window creation data

// Get the coordinates of the parent window's client area.

GetClientRect(hwndParent, &rcClient);

// Allocate an array for holding the right edge coordinates.

hloc = LocalAlloc(LHND, sizeof(int) * nParts);

lpParts = LocalLock(hloc);

// Calculate the right edge coordinate for each part, and

// copy the coordinates to the array.

nWidth = rcClient.right / nParts;

for (i = 0; i < nParts; i++) {

lpParts[i] = nWidth;

nWidth += nWidth;

}

// Tell the status window to create the window parts.

SendMessage(hwndStatus, SB_SETPARTS, (WPARAM) nParts,

(LPARAM) lpParts);

// Free the array, and return.

LocalUnlock(hloc);

LocalFree(hloc);

return hwndStatus;

}

+$#A Status Window Reference

The following functions and messages are associated with status windows. These elements can be grouped as follows.

Creation

CreateStatusWindow

Size and Height

SB_GETRECT
SB_SETMINHEIGHT
SB_GETBORDERS

Text Operations

DrawStatusText
MenuHelp
SB_GETTEXT
SB_GETTEXTLENGTH
SB_SETTEXT

Multiple Part Status Windows

SB_GETPARTS
SB_SETPARTS

Simple Mode Status Windows

SB_SIMPLE

+$#A Status Window Functions

The following functions are used with status windows.

CreateStatusWindow
DrawStatusText
MenuHelp

+$#A Status Window Messages

The following messages are used with status windows.

SB_GETBORDERS
SB_GETPARTS
SB_GETRECT
SB_GETTEXT
SB_GETTEXTLENGTH
SB_SETMINHEIGHT
SB_SETPARTS
SB_SETTEXT
SB_SIMPLE

Créé avec HelpNDoc Personal Edition: Générateur complet de livres électroniques ePub


String Manipulation.rtf

+$#AK String Manipulation

The overview describes the string manipulation functions and explains how to use them in your Win32-based applications.

+$#A About String Manipulation

The string manipulation functions give applications developed for the Win32 application programming interface (API) the means to copy, compare, sort, format, and convert character strings as well as the means to determine the character type of each character in a string. All the string manipulation functions support the single-byte, double-byte, and Unicode character sets if these character sets are supported by the operating system on which the application is run.

+$#A String Manipulation in Windows

Many Windows functions duplicate or enhance familiar string-manipulation functions from the standard C library. Many of the enhancements enable Windows functions to work with Unicode or extended character sets. For example, the Windows functions in the following table are identical to standard C functions except that the Windows functions are enhanced for use with Unicode.

Windows function | Standard C function
lstrcat | strcat
lstrcmp | strcmp
lstrcmpi | strcmpi
lstrcpy | strcpy
lstrlen | strlen

The strlen standard C function, for example, always returns the number of bytes in a string, but the lstrlen function returns the number of characters, regardless of whether the characters are 1 or 2 bytes wide.

The following Windows functions differ from standard C functions such as tolower and toupper in that they operate on any character in a character set. By using the CharLower function, for example, a Windows-based application can convert an uppercase U with an umlaut (Ü) to lowercase (ü). For more information about character sets, see Single-byte Character Sets.

Function | Description
CharLower | Converts a character or string to lowercase.
CharLowerBuff | Converts a character string to lowercase.
CharNext | Moves to the next character in a string.
CharPrev | Moves to the preceding character in a string.
CharUpper | Converts a character or string to uppercase.
CharUpperBuff | Converts a string to uppercase.

The following Windows functions make determinations about a character based on the semantics of the language selected by the user. These functions are Unicode enabled.

Function | Description
IsCharAlpha | Determines whether a character is alphabetic.
IsCharAlphaNumeric | Determines whether a character is alphanumeric.
IsCharLower | Determines whether a character is lowercase.
IsCharUpper | Determines whether a character is uppercase.

The wsprintf and wvsprintf functions are extensions to the standard C functions sprintf and vsprintf. The Windows versions support format specifications unique to Unicode.

+$#A String Resources

An application that maintains character strings in resources can be translated into new languages with minimum effort. Instead of searching for strings in the source modules, a developer can simply translate the strings in the resource file and relink the application. In addition, using string resources simplifies creation of Unicode and non-Unicode versions of the application from the same source files.

The LoadString function loads a string resource from an application's executable file. The FormatMessage function loads a string resource and interprets formatting options that may be embedded in the string.

Windows 32-bit resources in binary form are stored in Unicode format. When loading resources, applications can use the Unicode version of the resource functions (LoadStringW, for example) to obtain resources as Unicode data.

+$#A Obsolete Functions

The AnsiLower function is obsolete. Win32-based applications should use the CharLower function.

The AnsiLowerBuff function is obsolete. Win32-based applications should use the CharLowerBuff function.

The AnsiNext function is obsolete. Win32-based applications should use the CharNext function.

The AnsiPrev function is obsolete. Win32-based applications should use the CharPrev function.

The AnsiUpper function is obsolete. Win32-based applications should use the CharUpper function.

The AnsiUpperBuff function is obsolete. Win32-based applications should use the CharUpperBuff function.

OemToAnsi
OemToAnsiBuff

+$#A String Manipulation Reference

This section provides detailed reference information for the string manipulation functions, structures, and messages.

+$#A String Manipulation Functions

Following are the functions used with string manipulation.

CharLower
CharLowerBuff
CharNext
CharPrev
CharToOem
CharToOemBuff
CharUpper
CharUpperBuff
CompareString
ConvertDefaultLocale
EnumCalendarInfo
EnumCalendarInfoProc
EnumCodePagesProc
EnumDateFormats
EnumDateFormatsProc
EnumLocalesProc
EnumSystemCodePages
EnumSystemLocales
EnumTimeFormats
EnumTimeFormatsProc
FoldString
FormatMessage
GetACP
GetCPInfo
GetCurrencyFormat
GetDateFormat
GetNumberFormat
GetLocaleInfo
GetOEMCP
GetStringTypeA
GetStringTypeEx
GetStringTypeW
GetSystemDefaultLangID
GetSystemDefaultLCID
GetThreadLocale
GetTimeFormat
GetUserDefaultLangID
GetUserDefaultLCID
IsCharAlpha
IsCharAlphaNumeric
IsCharLower
IsCharUpper
IsDBCSLeadByte
IsTextUnicode
IsValidCodePage
IsValidLocale
LCMapString
LoadString
lstrcat
lstrcmp
lstrcmpi
lstrcpy
lstrcpyn
lstrlen
MultiByteToWideChar
OemToChar
OemToCharBuff
SetLocaleInfo
SetThreadLocale
WideCharToMultiByte
wsprintf
wvsprintf

+$#A String Manipulation Constants

This section describes the constants used with the string manipulation functions.

+$#A Character Type Information Values

The character-type bits are divided into several levels. The information for one level can be retrieved by a single call to this function. Each level is limited to 16 bits of information so that the other mapping routines limited to 16 bits of representation per character can return character-type information.

The character types supported by this function include the following.

Ctype 1: These types support ANSI C and POSIX (LC_CTYPE) character-typing functions. A combination of these values is returned in the array pointed to by the lpCharType parameter when the fdwInfoType parameter is set to CT_CTYPE1.

Name | Value | Meaning
C1_UPPER | 0x0001 | Uppercase
C1_LOWER | 0x0002 | Lowercase
C1_DIGIT | 0x0004 | Decimal digits
C1_SPACE | 0x0008 | Space characters
C1_PUNCT | 0x0010 | Punctuation
C1_CNTRL | 0x0020 | Control characters
C1_BLANK | 0x0040 | Blank characters
C1_XDIGIT | 0x0080 | Hexadecimal digits
C1_ALPHA | 0x0100 | Any letter

The following character types are either constant or computable from basic types and do not need to be supported by this function.

Type | Description
Alphanumeric | Alphabetic characters and digits
Printable | Graphic characters and blank

Ctype 2: These types support proper layout of Unicode™ text. The direction attributes are assigned so that the bidirectional layout algorithm standardized by Unicode produces accurate results. These types are mutually exclusive. For more information about the use of these attributes, see The Unicode Standard: Worldwide Character Encoding, Volumes 1 and 2, Addison Wesley Publishing Company: 1991, 1992, ISBN 0201567881.

Name | Value | Meaning
Strong: |  |
C2_LEFTTORIGHT | 0x1 | Left to right
C2_RIGHTTOLEFT | 0x2 | Right to left
Weak: |  |
C2_EUROPENUMBER | 0x3 | European number, European digit
C2_EUROPESEPARATOR | 0x4 | European numeric separator
C2_EUROPETERMINATOR | 0x5 | European numeric terminator
C2_ARABICNUMBER | 0x6 | Arabic number
C2_COMMONSEPARATOR | 0x7 | Common numeric separator
Neutral: |  |
C2_BLOCKSEPARATOR | 0x8 | Block separator
C2_SEGMENTSEPARATOR | 0x9 | Segment separator
C2_WHITESPACE | 0xA | White space
C2_OTHERNEUTRAL | 0xB | Other neutrals
Not applicable: |  |
C2_NOTAPPLICABLE | 0x0 | No implicit directionality (for example, control codes)

Ctype 3: These types are intended to be placeholders for extensions to the POSIX types required for general text processing or for the standard C library functions. These types are supported in the current version of Microsoft® Windows NT™. A combination of these values is returned when fdwInfoType is set to CT_CTYPE3.

Name | Value | Meaning
C3_NONSPACING | 0x1 | Nonspacing mark
C3_DIACRITIC | 0x2 | Diacritic nonspacing mark
C3_VOWELMARK | 0x4 | Vowel nonspacing mark
C3_SYMBOL | 0x8 | Symbol
C3_KATAKANA | 0x10 |  | Katakana character
C3_HIRAGANA | 0x20 |  | Hiragana character
C3_HALFWIDTH | 0x40 |  | Half Width character
C3_FULLWIDTH | 0x80 |  | Full Width character
C3_IDEOGRAPH | 0x100 |  | Ideographic character
C3_KASHIDA | 0x200 |  | Arabic Kashida character
C3_ALPHA | 0x8000 |  | All linguistic characters (alphabetic, syllabary and ideographic)
Not applicable: |  |
C3_NOTAPPLICABLE | 0x0 | Not applicable

+$#A Format Specifications

%[-][#][0][width][.precision]type

Format specifications always begin with a percent sign (%). Each field is a single character or a number signifying a particular format option. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting.

%

Required. Marks the beginning of the format specification. If the percent sign is followed by a character that has no meaning as a format field, the character is not formatted (for example, %% produces a single percent-sign character).

−

Optional. Pads the output with blanks or zeros to the right to fill the field width, justifying output to the left. If this field is omitted, the output is padded to the left, justifying it to the right.

#

Optional. Prefix hexadecimal values with 0x (lowercase) or 0X (uppercase).

0

Optional. Pad the output value with zeros to fill the field width. If this field is omitted, the output value is padded with blank spaces.

width

Optional. Copy the specified minimum number of characters to the output buffer. The width field is a nonnegative integer. The width specification never causes a value to be truncated; if the number of characters in the output value is greater than the specified width, or if the width field is not present, all characters of the value are printed, subject to the precision specification.

.precision

Optional. For numbers, copy the specified minimum number of digits to the output buffer. If the number of digits in the argument is less than the specified precision, the output value is padded on the left with zeros. The value is not truncated when the number of digits exceeds the specified precision. If the specified precision is 0 or omitted entirely, or if the period (.) appears without a number following it, the precision is set to 1.

For strings, copy the specified maximum number of characters to the output buffer.

type

Required. Determines whether the associated argument is interpreted as a character, a string, or a number. Can be one of these character sequences:

c | A single character. The functions ignore character arguments with a numeric value of zero. This sequence is interpreted as type WCHAR when the calling application uses the #define UNICODE compile flag and as type CHAR otherwise.
C | A single character. This sequence is interpreted as type CHAR when the calling application uses the #define UNICODE compile flag and as type WCHAR otherwise.
d | A signed decimal integer argument. This sequence is equivalent to the i sequence.
hc, hC | A single character. The functions ignore character arguments with a numeric value of zero. This sequence is always interpreted as type CHAR, even when the calling application uses the #define UNICODE compile flag.
hs, hS | A string. This sequence is always interpreted as type LPSTR, even when the calling application uses the #define UNICODE compile flag.
i | A signed decimal integer. This sequence is equivalent to the d sequence.
lc, lC | A single character. The functions ignore character arguments with a numeric value of zero. This sequence is always interpreted as type WCHAR, even when the calling application does not use the #define UNICODE compile flag.
ld | A long signed decimal integer. This sequence is equivalent to the li sequence.
li | A long signed decimal integer. This sequence is equivalent to the ld sequence.
ls, lS | A string. This sequence is always interpreted as type LPWSTR, even when the calling application does not use the #define UNICODE compile flag. This sequence is equivalent to the ws sequence.
lu | A long unsigned integer.
lx, lX | A long unsigned hexadecimal integer in lowercase or uppercase.
s | A string. This sequence is interpreted as type LPWSTR when the calling application uses the #define UNICODE compile flag and as type LPSTR otherwise.
S | A string. This sequence is interpreted as type LPSTR when the calling application uses the #define UNICODE compile flag and as type LPWSTR otherwise.
u | An unsigned integer argument.
x, X | An unsigned hexadecimal integer in lowercase or uppercase.

Créé avec HelpNDoc Personal Edition: Générer des livres électroniques EPub facilement


Structured Exception Handling.rtf

+$#AK Structured Exception Handling

An exception is an event that occurs during the execution of a program, and that requires the execution of software outside the normal flow of control. Hardware exceptions can result from the execution of certain instruction sequences, such as division by zero or an attempt to access an invalid memory address. A software routine can also initiate an exception explicitly.

The Microsoft® Win32® application programming interface (API) supports structured exception handling, a mechanism for handling hardware- and software-generated exceptions.

+$#A About Structured Exception Handling

Structured exception handling gives developers complete control over the handling of exceptions, provides support for debuggers, and is usable across all programming languages and machines.

The Win32 API also supports termination handling, which enables developers to ensure that whenever a guarded body of code is executed, a specified block of termination code is also executed. The termination code is executed regardless of how the flow of control leaves the guarded body. For example, a termination handler can guarantee that clean-up tasks are performed even if an exception or some other error occurs while the guarded body of code is being executed.

Structured exception and termination handling is an integral part of the Win32 API; it enables a robust implementation of the system software. Developers can use these mechanisms to create consistently robust and reliable applications.

Structured exception handling is made available to developers primarily through compiler support. For example, the Microsoft compilers provided with the Win32 software development kit support the try keyword that identifies a guarded body of code, and the except and finally keywords that identify an exception handler and a termination handler, respectively. Although this topic uses examples from the support available in Microsoft compilers, other compiler vendors can provide this support as well.

+$#A Exception Handling

Exceptions can be initiated by hardware or software, and can occur in kernel-mode as well as user-mode code. Win32-based structured exception handling provides a single mechanism for the handling of kernel-mode and user-mode exceptions, both hardware- and software-generated.

The execution of certain instruction sequences can result in exceptions that are initiated by hardware. For example, an access violation is generated by the hardware when a process attempts to read from or write to a virtual address to which it does not have the appropriate access.

Events that require exception handling may also occur during execution of a software routine (for example, when an invalid parameter value is specified). When this happens, a thread can initiate an exception explicitly by calling the RaiseException function. This function enables the calling thread to specify information that describes the exception.

+$#A Exception Dispatching

When a hardware or software exception occurs, the processor stops execution at the point at which the exception occurred and transfers control to the system. First, the system saves both the machine state of the current thread and information that describes the exception. The system then attempts to find an exception handler to handle the exception.

The machine state of the thread in which the exception occurred is saved in a CONTEXT structure. This information (called the context record) enables the system to continue execution at the point of the exception if the exception is successfully handled. The description of the exception (called the exception record) is saved in an EXCEPTION_RECORD structure. Because it stores the machine-dependent information of the context record separately from the machine-independent information of the exception record, the exception-handling mechanism is portable to different platforms. The information in both the context and exception records is available by means of the GetExceptionInformation function, and can be made available to any exception handlers that are executed as a result of the exception. The exception record includes the following information.

• An exception code that identifies the type of exception.

• Flags indicating whether the exception is continuable. Any attempt to continue execution after a noncontinuable exception generates another exception.

• A pointer to another exception record. This facilitates creation of a linked list of exceptions if nested exceptions occur.

• The address at which the exception occurred.

• An array of 32-bit arguments that provide additional information about the exception.

When an exception occurs in user-mode code, the system goes through the following search for an exception handler:

1. The system first attempts to notify the process's debugger, if any.

2. If the process is not being debugged, or if the associated debugger does not handle the exception, the system attempts to locate a frame-based exception handler by searching the stack frames of the thread in which the exception occurred. The system searches the current stack frame first, then proceeds backward through preceding stack frames.

3. If no frame-based handler can be found, or no frame-based handler handles the exception, the system makes a second attempt to notify the process's debugger.

4. If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. For most exceptions, the default action is to call the ExitProcess function.

When an exception occurs in kernel-mode code, the system searches the stack frames of the kernel stack in an attempt to locate an exception handler. If a handler cannot be located or no handler handles the exception, the system is shut down as if the ExitWindows function had been called.

+$#A Debugger Support

The system's handling of user-mode exceptions provides support for sophisticated debuggers. If the process in which an exception occurs is being debugged, the system generates a debug event. If the debugger is using the WaitForDebugEvent function, the debug event causes that function to return with a pointer to a DEBUG_EVENT structure. This structure contains the process and thread identifiers that the debugger can use to access the thread's context record. The structure also contains an EXCEPTION_DEBUG_INFO structure that includes a copy of the exception record.

The system's search for an exception handler includes two attempts to notify a process's debugger. The first notification attempt provides the debugger with an opportunity to handle breakpoint or single-step exceptions. The user can then issue debugger commands to manipulate the process's environment before any exception handlers are executed. The second attempt to notify the debugger occurs only if the system is unable to find a frame-based exception handler that handles the exception.

At each notification attempt, the debugger uses the ContinueDebugEvent function to return control to the system. Before returning control, the debugger can handle the exception and modify the thread state as appropriate, or it can choose not to handle the exception. Using ContinueDebugEvent, the debugger can indicate that it has handled the exception, in which case the machine state is restored and thread execution is continued at the point at which the exception occurred. The debugger can also indicate that it did not handle the exception, which causes the system to continue its search for an exception handler.

+$#A Frame-based Exception Handling

A frame-based exception handler is a mechanism by which a developer deals with the possibility that an exception may occur in a certain sequence of code. A frame-based exception handler consists of the following elements.

• A guarded body of code

• A filter expression

• An exception-handler block

Frame-based exception handlers are declared in language-specific syntax. For example, in the Microsoft® C Optimizing Compiler, they are implemented by the try-except statement. For more information about the Microsoft C exception-handling syntax, see Syntax.

The guarded body of code is a set of one or more statements for which the filter expression and the exception-handler block provide exception-handling protection. The guarded body can be a block of code, a set of nested blocks, or an entire procedure or function. In Microsoft C, a guarded body is enclosed by braces ({}) following the try keyword.

The filter expression of a frame-based exception handler is an expression that is evaluated by the system when an exception occurs within the guarded body. This evaluation results in one of the following actions by the system.

• The system stops its search for an exception handler, restores the machine state, and continues thread execution at the point at which the exception occurred.

• The system continues its search for an exception handler.

• The system transfers control to the exception handler, and thread execution continues sequentially in the stack frame in which the exception handler is found. If the handler is not in the stack frame in which the exception occurred, the system unwinds the stack, leaving the current stack frame and any other stack frames until it is back to the exception handler's stack frame. Before an exception handler is executed, termination handlers are executed for any guarded bodies of code that terminated as a result of the transfer of control to the exception handler. For more information about termination handlers, refer to Termination Handling.

The filter expression can be a simple expression, or it can invoke a filter function that attempts to handle the exception. You can call the GetExceptionCode and GetExceptionInformation functions from within a filter expression to get information about the exception being filtered. GetExceptionCode returns a code that identifies the type of exception, and GetExceptionInformation returns a pointer to an EXCEPTION_POINTERS structure that contains pointers to CONTEXT and EXCEPTION_RECORD structures.

These functions cannot be called from within a filter function, but their return values can be passed as parameters to a filter function. GetExceptionCode can be used within the exception-handler block, but GetExceptionInformation cannot because the information it points to is typically on the stack and is destroyed when control is transferred to an exception handler. However, an application can copy the information to safe storage to make it available to the exception handler.

The advantage of a filter function is that it can handle an exception and return a value that causes the system to continue execution from the point at which the exception occurred. With an exception-handler block, in contrast, execution continues sequentially from the exception handler rather than from the point of the exception.

Handling an exception may be as simple as noting an error and setting a flag that will be examined later, printing a warning or error message, or taking some other limited action. If execution can be continued, it may also be necessary to change the machine state by modifying the context record. For an example of a filter function that handles a page fault exception, see Memory Management.

The UnhandledExceptionFilter function can be used as a filter function in a filter expression. It returns EXCEPTION_EXECUTE_HANDLER unless the process is being debugged, in which case it returns EXCEPTION_CONTINUE_SEARCH.

+$#A Termination Handling

A termination handler is a mechanism by which a developer ensures that a specific block of code is executed whenever flow of control leaves a particular guarded body of code. A termination handler consists of the following elements.

• A guarded body of code

• A block of termination code to be executed when the flow of control leaves the guarded body

Termination handlers are declared in language-specific syntax. In Microsoft C, they are implemented by the try-finally statement. For more information about the Microsoft C exception-handling syntax, see Syntax.

The guarded body of code can be a block of code, a set of nested blocks, or an entire procedure or function. Whenever the guarded body is executed, the block of termination code will be executed. The only exception to this is when the thread terminates during execution of the guarded body (for example, if the ExitThread or ExitProcess function is called from within the guarded body).

The termination block is executed when the flow of control leaves the guarded body, regardless of whether the guarded body terminated normally or abnormally. The guarded body is considered to have terminated normally when the last statement in the block is executed and control proceeds sequentially into the termination block. Abnormal termination occurs when the flow of control leaves the guarded body due to an exception, or due to a control statement such as return, goto, break, or continue. The AbnormalTermination function can be called from within the termination block to determine whether the guarded body terminated normally.

+$#A Syntax

This section describes the syntax and usage of structured exception handling as implemented in Microsoft C. The following keywords are interpreted by the Microsoft C compiler as part of the structured exception-handling mechanism.

Keyword | Description
try | Begins a guarded body of code. Used with the except keyword to construct an exception handler, or with the finally keyword to construct a termination handler.
except | Begins a block of code that is executed only when an exception occurs within its associated try block.
finally | Begins a block of code that is executed whenever the flow of control leaves its associated try block.

The compiler also interprets the GetExceptionCode, GetExceptionInformation, and AbnormalTermination functions as keywords, and their use outside the appropriate exception-handling syntax generates a compiler error. Following are brief descriptions of these functions.

Function | Description
GetExceptionCode | Returns a code that identifies the type of exception. This function can be called only from within the filter expression or exception-handler block of a try-except exception handler.
GetExceptionInformation | Returns a pointer to an EXCEPTION_POINTERS structure containing pointers to the context record and the exception record. This function can be called only from within the filter expression of a try-except exception handler.
AbnormalTermination | Indicates whether the flow of control left the associated try block sequentially after executing the last statement in the block. This function can be called only from within the finally block of a try-finally termination handler.

+$#A Exception-Handler Syntax

The try and except keywords are used to construct a frame-based exception handler. The following example shows the structure of a try-except exception handler.

try {

// guarded body of code

}

except (filter-expression) {

// exception-handler block

}

Note that the try block and the exception-handler block require braces ({}). Using a goto statement to jump into the body of a try block or into an exception-handler block is not permitted. This rule applies to both try-except and try-finally statements.

The try block contains the guarded body of code that the exception handler protects. A function can have any number of try-except statements, and these exception-handling statements can be nested within the same function or in different functions. If an exception occurs within the try block, the system takes control and begins the search for an exception handler. For a detailed description of this search, see Exception Handling.

The exception handler receives only exceptions that occur within a single thread. This means that if a try block contains a call to the CreateProcess or CreateThread function, exceptions that occur within the new process or thread are not dispatched to this handler.

The system evaluates the filter expression of each exception handler guarding the code in which the exception occurred until either the exception is handled or there are no more handlers. A filter expression must be evaluated as one of the three following values.

Value | Meaning
EXCEPTION_EXECUTE_HANDLER |
 | The system transfers control to the exception handler, and execution continues in the stack frame in which the handler is found.
EXCEPTION_CONTINUE_SEARCH |
 | The system continues to search for a handler.
EXCEPTION_CONTINUE_EXECUTION |
 | The system stops its search for a handler and returns control to the point at which the exception occurred. If the exception is noncontinuable, this results in a EXCEPTION_NONCONTINUABLE_EXCEPTION exception.

The filter expression is evaluated in the context of the function in which the try-except statement is located, even though the exception may have occurred in a different function. This means that the filter expression can access the function's local variables. Similarly, the exception-handler block can access the local variables of the function in which it is located.

For more information about filter expressions and filter functions, see Frame-based Exception Handling.

+$#A Termination-Handler Syntax

The try and finally keywords are used to construct a termination handler. The following example shows the structure of a try-finally termination handler.

try {

// guarded body of code

}

finally {

// finally block

}

As with the try-except statement, both the try block and the finally block require braces ({}), and using a goto statement to jump into either block is not permitted.

The try block contains the guarded body of code that is protected by the termination handler. A function can have any number of try-finally statements, and these termination handling structures can be nested within the same function or in different functions.

The finally block is executed whenever the flow of control leaves the try block. However, the finally block is not executed if either the ExitProcess or ExitThread function is called from within the try block.

If execution of the try block terminates because of an exception that invokes the exception-handling block of a frame-based exception handler, the finally block is executed before the exception-handling block is executed. Similarly, a call to the standard C longjmp function from the try block causes execution of the finally block before execution resumes at the target of the longjmp operation. If try block execution terminates due to a normal control statement (return, break, continue, or goto), the finally block is executed.

The AbnormalTermination function can be used within the finally block to determine whether the try block terminated sequentially ⎯ that is, whether it reached the closing brace (}). Leaving the try block because of a call to longjmp, a jump to an exception handler, or a return, break, continue, or goto statement, is considered an abnormal termination. Note that failure to terminate sequentially causes the system to search backward through all stack frames to determine whether any termination handlers must be called. This can result in performance degradation due to the execution of hundreds of instructions.

Execution of the finally block can terminate by any of the following means.

• Execution of the last statement in the block and continuation to the next instruction

• Use of a normal control statement (return, break, continue, or goto)

• Use of longjmp or a jump to an exception handler

The finally block is executed in the context of the function in which the try-finally statement is located. This means that the finally block can access that function's local variables.

+$#A Using Structured Exception Handling

• Using an Exception Handler

• Using a Termination Handler

+$#A Using an Exception Handler

The following example shows a version of the standard C strcpy function that can handle an invalid pointer and return NULL if one is encountered.

Note that this exception handler is intended to handle an access-violation exception, but is inappropriate if some other type of exception occurs. Therefore, the filter expression in the example uses the GetExceptionCode function to check the exception type before executing the handler. This enables the system to continue its search for an appropriate handler if some other type of exception occurs.

Note also that there is no problem with using a return statement in the try block of a try-except statement. This differs from the use of return in the try block of a try-finally statement, which causes an abnormal termination of the try block.

LPTSTR SafeStrcpy(LPTSTR lpszString1, LPTSTR lpszString2) {

try {

return strcpy(string1, string2);

}

except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?

EXCEPTION_EXECUTE_HANDLER :

EXCEPTION_CONTINUE_SEARCH ) {

return NULL;

}

}

The following example shows the interaction of nested try-finally and try-except statements. The RaiseException function causes an exception in the guarded body of a try-finally termination handler that is inside the guarded body of a try-except exception handler. The exception causes the system to evaluate the FilterFunction function, whose return value in turn causes the exception handler to be invoked. However, before the exception-handler block is executed, the finally block of the termination handler is executed because the flow of control has left the try block of the termination handler.

DWORD FilterFunction() {

printf("1 "); // printed first

return EXCEPTION_EXECUTE_HANDLER;

}

VOID main(VOID) {

try {

try {

RaiseException(1, // exception code

0, // continuable exception

0, NULL); // no arguments

}

finally {

printf("2 ") // this is printed second

}

}

except ( FilterFunction() ) {

printf("3\n"); // this is printed last

}

}

+$#A Using a Termination Handler

The following example shows how a try-finally statement is used to ensure that resources are released when execution of a guarded body of code terminates. In this case, a thread uses the EnterCriticalSection function to wait for ownership of a critical section object. When the thread is finished executing the code that is protected by the critical section, it must call the LeaveCriticalSection function to make the critical section object available to other threads. Using a try-finally statement guarantees that this will happen.

For more information about critical section objects, see Synchronization.

LPTSTR lpBuffer = NULL;

CRITICAL_SECTION csCriticalSection;

try {

// EnterCriticalSection synchronizes code

// with other threads.

EnterCriticalSection(&CriticalSection);

// Perform a task that may cause an exception.

lpBuffer = (LPTSTR) LocalAlloc(LPTR, 10);

strcpy(lpBuffer,"Hello"); // possible access violation

printf("%s\n",lpBuffer);

LocalFree(lpBuffer);

}

// LeaveCriticalSection is called even if

// an exception occurred.

finally {

LeaveCriticalSection(&CriticalSection);

}

+$#A Structured Exception Handling Functions

Following are the functions used in structured exception handling.

AbnormalTermination
GetExceptionCode
GetExceptionInformation
RaiseException
SetUnhandledExceptionFilter
UnhandledExceptionFilter

Obsolete or Deleted Functions

Catch
Throw

Créé avec HelpNDoc Personal Edition: Produire des livres Kindle gratuitement


Synchronization.rtf

+$#AK Synchronization

The Microsoft® Win32® application programming interface (API) provides a variety of ways to coordinate multiple threads of execution.

+$#A About Synchronization

The functions described in this overview provide synchronization mechanisms, such as wait functions and synchronization objects, and overlapped input and output (I/O) operations. The synchronization mechanisms also include critical section objects and interlocked variable access

+$#A Wait Functions

The Win32 API provides a set of wait functions that enable a thread to block its own execution. These functions do not return until a set of conditions specified by the wait function's parameters have been satisfied. These parameters include a time-out interval and handles of one or more synchronization objects. For more information about synchronization objects, see Synchronization Objects Overview.

When a wait function is called, it checks the state of the specified synchronization object (or objects) and any other conditions that could cause it to return. If this initial state does not satisfy the specified conditions and the time-out interval has not elapsed, the calling thread enters an efficient wait state, consuming very little processor time while waiting for the conditions to be satisfied. The time-out interval can be set to INFINITE to cause an indefinite wait.

The WaitForSingleObject and WaitForSingleObjectEx functions require a handle of one synchronization object. These functions return when the state of the object is signaled, or when the time-out interval elapses. (The state of a synchronization object is either signaled or nonsignaled.)

The WaitForMultipleObjects, WaitForMultipleObjectsEx, and MsgWaitForMultipleObjects functions enable the calling thread to specify an array containing one or more synchronization object handles. These functions can be set to return when the state of any one of the specified objects is signaled, in which case the return value indicates the array index of the object that caused the function to return. Alternatively, the functions can be set to return only when the state of all objects is signaled at the same time, in which case the return value simply indicates that the specified conditions were satisfied.

When waiting for all objects to be signaled, these multiple-object functions do not modify the state of the specified objects until all objects are signaled simultaneously. For example, a mutex can be signaled, but the calling thread does not get ownership of it until the other objects are also signaled. In the meantime, some other thread may get ownership of the mutex, thereby delaying satisfaction of all specified conditions.

The WaitForSingleObjectEx and WaitForMultipleObjectsEx functions differ from the other wait functions in that they can optionally perform an alertable wait. In an alertable wait, the function can return when the specified conditions are satisfied, but it can also return if the system queues a completion routine for execution by the waiting thread. For more information about alertable waits and I/O completion routines, see Synchronization and Overlapped Input and Output.

The MsgWaitForMultipleObjects function is identical to WaitForMultipleObjects, except that it can also return when a specified type of input is available in the calling thread's input queue. For example, a thread could use MsgWaitForMultipleObjects to block its execution until either the state of a specified object is signaled, or there is mouse input available in the thread's input queue. When input is available, the thread can use the GetMessage or PeekMessage function to retrieve it.

Before returning, a wait function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. A wait function can modify the state of synchronization objects as follows:

• The count of a semaphore object decreases by one, with the semaphore's state changing to nonsignaled if its count is zero.

• The state of mutex, auto-reset event, and change notification objects changes to nonsignaled.

• The state of manual-reset event, process, thread, and console input objects is not affected by a wait function.

+$#A Synchronization Objects Overview

A synchronization object is an object whose handle can be specified in one of the wait functions to coordinate the execution of multiple threads. The state of a synchronization object is either signaled, which can allow the wait function to return, or nonsignaled, which can prevent the function from returning. More than one process can have a handle of the same synchronization object, making interprocess synchronization possible.

The Win32 API provides the following three object types intended exclusively for synchronization.

Type | Description
Event | Notifies one or more waiting threads that an event has occurred.
Mutex | Can be owned by only one thread at a time, enabling threads to coordinate mutually exclusive access to a shared resource.
Semaphore | Maintains a count between zero and some maximum value, limiting the number of threads that are simultaneously accessing a shared resource.

Though available for other uses, the following objects can also be used for synchronization.

Object | Description
Change notification | Created by the FindFirstChangeNotification function, its state is signaled when a specified type of change occurs within a specified directory or directory tree. For more information, see Files.
Console input | Created when a console is created, when a handle to console input is returned by the CreateFile function when CONIN$ is specified, or by the GetStdHandle function. Its state is signaled when there is unread input in the console's input buffer, and nonsignaled when the input buffer is empty. For more information about consoles, see Consoles and Character-Mode Support.
Process | Created when a new process is created by calling the CreateProcess function, its state is nonsignaled while the process is running, and signaled when the process terminates. For more information about processes, see Processes and Threads.
Thread | Created when a new thread is created by calling the CreateProcess, CreateThread, or CreateRemoteThread function, its state is nonsignaled while the thread is running, and signaled when the thread terminates. For more information about threads, see Processes and Threads.

In some circumstances, you can also use a file, named pipe, or communications device as a synchronization object; however, their use for this purpose is discouraged. For additional information about I/O operations on files, named pipes, or communications, see Synchronization and Overlapped Input and Output.

+$#A Mutex Objects Overview

A mutex object is a synchronization object whose state is signaled when it is not owned by any thread, and nonsignaled when it is owned. Only one thread at a time can own a mutex, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex.

A thread uses the CreateMutex function to create a mutex object. The creating thread can request immediate ownership of the mutex and can also specify a name for the mutex object. Threads in other processes can open a handle of an existing mutex object by specifying its name in a call to the OpenMutex function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization.

Any thread with a handle of a mutex object can use one of the wait functions to request ownership of the mutex. If the mutex is owned by another thread, the wait function blocks the execution of the requesting thread until the owning thread releases the mutex using the ReleaseMutex function. The return value of the wait function indicates whether the function returned for some reason other than the state of the mutex being signaled.

Once a thread owns a mutex, it can specify the same mutex in repeated wait-function calls without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.

If a thread terminates without releasing its ownership of a mutex object, the mutex is considered to be abandoned. A waiting thread can acquire ownership of an abandoned mutex, but the wait function's return value indicates that the mutex is abandoned. It is best to assume that an abandoned mutex indicates that an error has occurred and that any shared resource being protected by the mutex is in an undefined state. If the thread proceeds normally, as though the mutex had not been abandoned, the mutex's abandoned flag is cleared when the thread releases its ownership. This restores normal mutex behavior if the mutex is subsequently specified in a wait function.

+$#A Semaphore Objects Overview

A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. A semaphore's state is signaled when its count is greater than zero, and nonsignaled when its count is zero.

The semaphore object is useful in controlling a shared resource that can support a limited number of users. It acts like a gate that counts the threads as they enter and exit a controlled area and limits the number of threads sharing the resource to a specified maximum number. For example, an application might want to place a limit on the number of windows that it creates. It uses a semaphore with a maximum count equal to the window limit, decrementing the count whenever a window is created and incrementing it whenever a window is closed. The application specifies the semaphore object in a wait function before each window is created. When the count is zero ⎯ indicating that the window limit has been reached ⎯ the wait function blocks execution of the window-creation code.

A thread uses the CreateSemaphore function to create a semaphore object. The creating thread specifies the initial count and the maximum count of the object. The initial count must be neither less than zero nor greater than the maximum count. The creating thread can also specify a name for the semaphore object. Threads in other processes can open a handle of an existing semaphore object by specifying its name in a call to the OpenSemaphore function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization.

Each time that a wait function returns because of a semaphore's signaled state, the count of the semaphore is decreased by one. The ReleaseSemaphore function increases a semaphore's count by a specified amount. The count can never be less than zero or greater than the maximum count.

The initial count of a semaphore is typically set to the maximum value. The count is then decremented from that level as the protected resource is consumed. Alternatively, a semaphore can be created with an initial count of zero to block access to the protected resource while the application is being initialized. After initialization, you would use ReleaseSemaphore to increment the count to the maximum.

Threads do not acquire ownership of semaphore objects as they do with mutex objects. A thread that owns a mutex can wait repeatedly for the same mutex object without its execution becoming blocked. A thread that waits repeatedly for the same semaphore object, however, decrements the semaphore's count each time a wait is satisfied; its execution is blocked when the count gets to zero. Similarly, only the thread that owns a mutex can successfully call the ReleaseMutex function, though any thread can use ReleaseSemaphore to increase the count of a semaphore object, even if the thread never used a wait function to decrement the count.

A thread can decrement a semaphore's count more than once by repeatedly specifying the same semaphore object in calls to any of the wait functions. However, calling the WaitForMultipleObjects function with an array that contains multiple handles of the same semaphore does not perform multiple decrements.

+$#A Event Objects Overview

An event object is a synchronization object whose state can be explicitly set to signaled by use of the SetEvent or PulseEvent function. Following are the two types of event object.

Object | Description
Manual-reset event | An event object whose state remains signaled until it is explicitly reset to nonsignaled by the ResetEvent function. While it is signaled, any number of waiting threads, or threads that subsequently specify the same event object in one of the wait functions, can be released.
Auto-reset event | An event object whose state remains signaled until a single waiting thread is released, at which time the system automatically sets the state to nonsignaled. If no threads are waiting, the event object's state remains signaled.

The event object is useful in signaling a thread that a particular event has occurred. For example, in overlapped input and output, the system sets a specified event object to the signaled state when the overlapped operation has been completed. A single thread can specify different event objects in several simultaneous overlapped operations, then use the WaitForMultipleObjects function to wait for the state of any one of the event objects to be signaled.

A thread uses the CreateEvent function to create an event object. The creating thread specifies the initial state of the object and whether it is a manual-reset or auto-reset event object. The creating thread can also specify a name for the event object. Threads in other processes can open a handle of an existing event object by specifying its name in a call to the OpenEvent function. For additional information about names for mutex, event, and semaphore objects, see Interprocess Synchronization.

A thread can use the PulseEvent function to set an event object's state to signaled and then reset it to nonsignaled after releasing the appropriate number of waiting threads. For a manual-reset event object, all waiting threads that can be released immediately are released. For an auto-reset event object, the function releases only a single waiting thread, even if multiple threads are waiting. If no threads are waiting, or if no thread can be released immediately, PulseEvent simply sets the event object's state to nonsignaled and returns.

+$#A Interprocess Synchronization

Multiple processes can have handles of the same mutex, semaphore, or event object, so these objects can be used to accomplish interprocess synchronization. The process that creates an object can use the handle returned by the creation function (CreateEvent, CreateMutex, or CreateSemaphore). Other processes can open a handle of the object by using its name, or through inheritance or duplication.

+$#A Object Names

Named objects provide an easy way for processes to share object handles. The name specified by the creating process is limited to MAX_PATH characters, and can include any character except the backslash path-separator character (\). Once a process has created a named event, mutex, or semaphore object, other processes can use the name to call the appropriate function (OpenEvent, OpenMutex, or OpenSemaphore) to open a handle of the object. Name comparison is case sensitive. The names of event, semaphore, mutex, and file-mapping objects share the same name space, so an error occurs if a specified name matches the name of an existing object of a different type. When creating named objects, therefore, use unique names and be sure to check function return values for duplicate-name errors.

If the name specified in a call to the CreateMutex function matches the name of an existing mutex object, the function returns a handle of the existing object. In this case, the call to CreateMutex is equivalent to a call to the OpenMutex function. (The CreateEvent and CreateSemaphore functions work the same way with existing named event and semaphore objects, respectively.) Having multiple processes use CreateMutex for the same mutex is therefore equivalent to having one process that calls CreateMutex while the other processes call OpenMutex, except that it eliminates the need to ensure that the creating process is started first. When using this technique for mutex objects, however, none of the calling processes should request immediate ownership of the mutex. If multiple processes do request immediate ownership, it can be difficult to predict which process actually gets the initial ownership.

+$#A Object Inheritance

A child process created by the CreateProcess function can inherit a handle of a mutex, event, or semaphore if the SECURITY_ATTRIBUTES structure ⎯ specified when the object was created ⎯ enabled inheritance. The handle inherited by the child process has the same access to the object as the parent's handle. The inherited handle appears in the handle table of the child process, but for the child process to be able to use the handle, the parent process must communicate the handle's value to the child process. The parent can do this by specifying the value as a command-line argument when it calls the CreateProcess function. The child process can then use the GetCommandLine function to retrieve the command-line string and convert the handle argument into a usable handle. For more information about object inheritance, see Processes and Threads.

+$#A Object Duplication

The DuplicateHandle function creates a duplicate handle that can be used by another specified process. This method of sharing object handles is more complex than using named objects or inheritance. It requires communication between the creating process and the process into which the handle is duplicated. The necessary information (the handle value and process identifier) can be communicated by any of the interprocess communication methods (for example, named pipe, shared file, shared memory). For more information about object duplication, see Processes and Threads.

+$#A Synchronization and Overlapped Input and Output

The Win32 API supports both synchronous and asynchronous (or overlapped) I/O operations on files, named pipes, and serial communications devices. The WriteFile, ReadFile, DeviceIoControl, WaitCommEvent, ConnectNamedPipe, and TransactNamedPipe functions can be performed either synchronously or asynchronously. The ReadFileEx and WriteFileEx functions can be performed asynchronously only.

When a function is executed synchronously, it does not return until the operation has been completed. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. Functions called for overlapped operation can return immediately, even though the operation has not been completed. This enables a time-consuming I/O operation to be executed in the background while the calling thread is free to perform other tasks. For example, a single thread can perform simultaneous I/O operations on different handles, or even simultaneous read and write operations on the same handle. To synchronize its execution with the completion of the overlapped operation, the calling thread uses the GetOverlappedResult function or one of the wait functions to determine when the operation has been completed.

Overlapped operations require a file, named pipe, or communications device that was created with the FILE_FLAG_OVERLAPPED flag. To call a function as an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED structure. If this pointer is NULL, the operation is performed synchronously, even though the handle was created with FILE_FLAG_OVERLAPPED enabled. The OVERLAPPED structure must contain a handle to a manual-reset ⎯ not an auto-reset ⎯ event object. The system sets the state of the event object to nonsignaled when a call to the I/O function returns before the operation has been completed. The system sets the state of the event object to signaled when the operation has been completed.

When a function is called as an overlapped operation, the operation may have been completed before the function returns. When this happens, the results are handled as if the operation had been performed synchronously. If the operation has not yet been completed, however, the function's return value is FALSE, and the GetLastError function returns ERROR_IO_PENDING.

A thread can manage overlapped operations by either of two methods. It can use the GetOverlappedResult function to wait for the overlapped operation to be completed. Alternatively, it can specify a handle of the OVERLAPPED structure's manual-reset event object in one of the wait functions and then call GetOverlappedResult after the wait function returns. GetOverlappedResult returns the results of the completed overlapped operation, and for functions in which such information is appropriate, it reports the actual number of bytes that were transferred.

When performing multiple simultaneous overlapped operations, the calling thread must specify a separate OVERLAPPED structure with its own manual-reset event object for each operation. The thread can then specify handles of the different event objects in the handle-array parameter of the WaitForMultipleObjects function (or one of the other multiple-object wait functions) to wait for any one of the overlapped operations to be completed. The return value of the multiple-object wait function indicates which event object was signaled, so the thread can determine which operation was completed.

If no event object is specified in the OVERLAPPED structure, the system signals the state of the file, named pipe, or communications device when the overlapped operation has been completed. Thus, you can specify these handles as synchronization objects in a wait function, though their use for this purpose can be difficult to manage. When performing simultaneous overlapped operations on the same file, named pipe, or communications device, there is no way to know which operation caused the object's state to be signaled. It is safer to use a separate event object for each overlapped operation.

For examples that illustrate the use of overlapped operations, completion routines, and the GetOverlappedResult function, see Pipes.

+$#A Critical Section Objects

Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization. Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access. For example, a process could use a critical section object to prevent more than one thread at a time from modifying a global data structure.

The process is responsible for allocating the memory used by a critical section object. Typically, this is done by simply declaring a variable of type CRITICAL_SECTION. Before the threads of the process can use it, the critical section object must be initialized by using the InitializeCriticalSection function.

A thread uses the EnterCriticalSection function to request ownership and the LeaveCriticalSection function to release its ownership. If the critical section object is currently owned by another thread, EnterCriticalSection waits indefinitely for ownership. This differs from the use of a mutex object for mutual exclusion, where the wait functions accept a specified time-out interval.

Once a thread owns a critical section object, it can make additional calls to EnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section object that it already owns. To release its ownership, the thread must call LeaveCriticalSection once for each time that it entered the critical section.

Any thread of the process can use the DeleteCriticalSection function to release the system resources that were allocated when the critical section object was initialized. After this function has been called, the critical section object can no longer be used for synchronization.

When a critical section object is owned, the only other threads affected are those that are waiting for ownership in a call to EnterCriticalSection. Threads that are not waiting are free to continue their normal execution.

+$#A Interlocked Variable Access

The InterlockedIncrement and InterlockedDecrement functions provide a simple mechanism for synchronizing access to a specified 32-bit variable that is shared by multiple threads in the same or in different processes. These functions combine the operations of incrementing or decrementing the variable and checking the resulting value. This is useful in a multitasking operating system, in which the system can interrupt one thread's execution to grant a slice of processor time to another thread. Without such synchronization, one thread could increment a variable but be interrupted by the system before it can check the resulting value of the variable. A second thread could then increment the same variable. When the first thread receives its next timeslice, it will check the value of the variable, which has now been incremented not once but twice. The interlocked variable-access functions protect against this kind of error.

The threads of different processes can use this mechanism if the variable is in shared memory.

+$#A Using Synchronization

• Using named objects

• Waiting for multiple objects

• Using mutex objects

• Using semaphore objects

• Using event objects

• Using critical section objects

+$#A Using Named Objects

This example illustrates the use of an object name to let two processes open handles of the same mutex object.

The first process uses the CreateMutex function to create the mutex object. Note that the function succeeds even if there is an existing object with the same name.

/* One process creates the mutex object. */

HANDLE hMutex;

DWORD dwErr;

hMutex = CreateMutex(

NULL, /* no security descriptor */

FALSE, /* mutex not owned */

"NameOfMutexObject"); /* object name */

if (hMutex == NULL)

printf("CreateMutex error: %d\n", GetLastError() );

else

if ( GetLastError() == ERROR_ALREADY_EXISTS )

printf("CreateMutex opened existing mutex\n");

else

printf("CreateMutex created new mutex\n");

The second process uses the OpenMutex function to open a handle of the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.

/* Another process opens a handle of the existing mutex. */

HANDLE hMutex;

hMutex = OpenMutex(

MUTEX_ALL_ACCESS, /* request full access */

FALSE, /* handle not inheritable */

"NameOfMutexObject"); /* object name */

if (hMutex == NULL)

printf("OpenMutex error: %d\n", GetLastError() );

The following example uses the CreateSemaphore function to illustrate a named-object creation operation that fails if the object already exists.

HANDLE CreateNewSemaphore(LPSECURITY_ATTRIBUTES lpsa,

LONG cInitial, LONG cMax, LPTSTR lpszName) {

HANDLE hSem;

/* Create or open a named semaphore. */

hSem = CreateSemaphore(

lpsa, /* security attributes */

cInitial, /* initial count */

cMax, /* maximum count */

lpszName); /* semaphore name */

/* Close handle, and return NULL if existing semaphore opened. */

if (hSem != NULL &&

GetLastError() == ERROR_ALREADY_EXISTS) {

CloseHandle(hSem);

return NULL;

}

/* If new semaphore was created, return the handle. */

return hSem;

}

+$#A Waiting for Multiple Objects

This example uses the CreateEvent function to create two event objects. It then uses the WaitForMultipleObjects function to wait for one of them to be signaled.

HANDLE hEvents[2];

DWORD i, dwEvent;

/* Create two event objects. */

for (i = 0; i < 2; i++) {

hEvents[i] = CreateEvent(

NULL, /* no security attributes */

FALSE, /* auto-reset event object */

FALSE, /* initial state is nonsignaled */

NULL); /* unnamed object */

if (hEvents[i] == NULL) {

printf("CreateEvent error: %d\n", GetLastError() );

ExitProcess(0);

}

}

/*

* The creating thread waits for other threads or processes

* to signal the event objects.

*/

dwEvent = WaitForMultipleObjects(

2, /* number of objects in array */

hEvents, /* array of objects */

FALSE, /* wait for any */

INFINITE); /* indefinite wait */

/* Return value indicates which event is signaled. */

switch (dwEvent) {

/* hEvent[0] was signaled. */

case WAIT_OBJECT_0 + 0:

/* Perform tasks required by this event. */

break;

/* hEvent[1] was signaled. */

case WAIT_OBJECT_0 + 1:

/* Perform tasks required by this event. */

break;

/* Return value is invalid. */

default:

printf("Wait error: %d\n", GetLastError());

ExitProcess(0);

}

+$#A Using Mutex Objects

You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource. For example, if several threads share access to a database, the threads can use a mutex object to permit only one thread at a time to write to the database.

In the following example, a process uses the CreateMutex function to create a named mutex object or open a handle of an existing mutex object.

HANDLE hMutex;

/* Create a mutex with no initial owner. */

hMutex = CreateMutex(

NULL, /* no security attributes */

FALSE, /* initially not owned */

"MutexToProtectDatabase"); /* name of mutex */

if (hMutex == NULL) {

/* check for error */

}

When a thread of this process writes to the database, as in the next example, it first requests ownership of the mutex. If it gets ownership, the thread writes to the database and then releases its ownership.

The example uses the try-finally structured exception-handling syntax to ensure that the thread properly releases the mutex. The finally block of code is executed no matter how the try block terminates (unless the try block includes a call to the TerminateThread function). This prevents the mutex from being abandoned inadvertently.

BOOL FunctionToWriteToDatabase(HANDLE hMutex) {

DWORD dwWaitResult;

/* Request ownership of mutex. */

dwWaitResult = WaitForSingleObject(

hMutex, /* handle of mutex */

5000L); /* five-second time-out interval */

switch (dwWaitResult) {

/* The thread got mutex ownership. */

case WAIT_OBJECT_0:

try {

.

. /* Write to the database. */

.

}

finally {

/* Release ownership of the mutex object. */

if (! ReleaseMutex(hMutex)) {

/* Deal with error. */

}

break;

}

/* Cannot get mutex ownership due to time-out. */

case WAIT_TIMEOUT:

return FALSE;

/* Got ownership of the abandoned mutex object. */

case WAIT_ABANDONED:

return FALSE;

}

return TRUE;

}

+$#A Using Semaphore Objects

In the following example, a process uses a semaphore object to limit the number of windows it creates. First, it uses the CreateSemaphore function to create the semaphore and to specify initial and maximum counts.

HANDLE hSemaphore;

LONG cMax = 10;

LONG cPreviousCount;

/* Create a semaphore with initial and max. counts of 10. */

hSemaphore = CreateSemaphore(

NULL, /* no security attributes */

cMax, /* initial count */

cMax, /* maximum count */

NULL); /* unnamed semaphore */

if (hSemaphore == NULL) {

/* Check for error. */

}

Before any thread of the process creates a new window, it uses the WaitForSingleObject function to determine whether the semaphore's current count permits the creation of additional windows. The wait function's time-out parameter is set to zero, so the function returns immediately if the semaphore is nonsignaled.

DWORD dwWaitResult;

/* Try to enter the semaphore gate. */

dwWaitResult = WaitForSingleObject(

hSemaphore, /* handle of semaphore */

0L); /* zero-second time-out interval */

switch (dwWaitResult) {

/* The semaphore object was signaled. */

case WAIT_OBJECT_0:

.

. /* OK to open another window. */

.

break;

/* Semaphore was nonsignaled, so a time-out occurred. */

case WAIT_TIMEOUT:

.

. /* Cannot open another window. */

.

break;

}

When a thread closes a window, it uses the ReleaseSemaphore function to increment the semaphore's count.

/* Increment the count of the semaphore. */

if (! ReleaseSemaphore(

hSemaphore, /* handle of semaphore */

1, /* increase count by one */

NULL) ) { /* not interested in previous count */

/* Deal with the error. */

}

+$#A Using Event Objects

The Win32 API uses event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion. For more information about the use of event objects in overlapped I/O operations, see Synchronization and Overlapped Input and Output.

In the following example, an application uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent function to create a manual-reset event object. The master thread sets the event object to nonsignaled when it is writing to the buffer and then resets the object to signaled when it has finished writing. Then it creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.

#define NUMTHREADS 4

HANDLE hGlobalWriteEvent;

void CreateEventsAndThreads(void) {

HANDLE hReadEvents[NUMTHREADS], hThread;

DWORD i, IDThread;

/*

* Create a manual-reset event object. The master thread sets

* this to nonsignaled when it writes to the shared buffer.

*/

hGlobalWriteEvent = CreateEvent(

NULL, /* no security attributes */

TRUE, /* manual-reset event */

TRUE, /* initial state is signaled */

"WriteEvent" /* object name */

);

if (hGlobalWriteEvent == NULL) {

/* error exit */

}

/*

* Create multiple threads and an auto-reset event object

* for each thread. Each thread sets its event object to

* signaled when it is not reading from the shared buffer.

*/

for(i = 1; i <= NUMTHREADS; i++) {

/* Create the auto-reset event. */

hReadEvents[i] = CreateEvent(

NULL, /* no security attributes */

FALSE, /* auto-reset event */

TRUE, /* initial state is signaled */

NULL); /* object not named */

if (hReadEvents[i] == NULL) {

/* error exit */

}

hThread = CreateThread(NULL, 0,

(LPTHREAD_START_ROUTINE) ThreadFunction,

&hReadEvents[i], /* pass event handle */

0, &IDThread);

if (hThread == NULL) {

/* error exit */

}

}

}

Before the master thread writes to the shared buffer, it uses the ResetEvent function to set the state of hGlobalWriteEvent (an application-defined global variable) to nonsignaled. This blocks the reader threads from starting a read operation. The master then uses the WaitForMultipleObjects function to wait for all reader threads to finish any current read operations. When WaitForMultipleObjects returns, the master thread can safely write to the buffer. After it has finished, it sets hGlobalWriteEvent and all the reader-thread events to signaled, enabling the reader threads to resume their read operations.

VOID WriteToBuffer(VOID) {

DWORD dwWaitResult, i;

/* Reset hGlobalWriteEvent to nonsignaled, to block readers. */

if (! ResetEvent(hGlobalWriteEvent) ) {

/* error exit */

}

/* Wait for all reading threads to finish reading. */

dwWaitResult = WaitForMultipleObjects(

NUMTHREADS, /* number of handles in array */

hReadEvents, /* array of read-event handles */

TRUE, /* wait until all are signaled */

INFINITE); /* indefinite wait */

switch (dwWaitResult) {

/* All read-event objects were signaled. */

case WAIT_OBJECT_0:

.

. /* Write to the shared buffer. */

.

break;

/* An error occurred. */

default:

printf("Wait error: %d\n", GetLastError());

ExitProcess(0);

}

/* Set hGlobalWriteEvent to signaled. */

if (! SetEvent(hGlobalWriteEvent) ) {

/* error exit */

}

/* Set all read events to signaled. */

for(i = 1; i <= NUMTHREADS; i++)

if (! SetEvent(hReadEvents[i]) ) {

/* error exit */

}

}

Before starting a read operation, each reader thread uses WaitForMultipleObjects to wait for the application-defined global variable hGlobalWriteEvent and its own read event to be signaled. When WaitForMultipleObjects returns, the reader thread's auto-reset event has been reset to nonsignaled. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent function to set the event's state back to signaled.

VOID ThreadFunction(LPVOID lpParam) {

DWORD dwWaitResult, i;

HANDLE hEvents[2];

hEvents[0] = (HANDLE) *lpParam; /* thread's read event */

hEvents[1] = hGlobalWriteEvent;

dwWaitResult = WaitForMultipleObjects(

2, /* number of handles in array */

hEvents, /* array of event handles */

TRUE, /* wait till all are signaled */

INFINITE); /* indefinite wait */

switch (dwWaitResult) {

/* Both event objects were signaled. */

case WAIT_OBJECT_0:

.

. /* Read from the shared buffer. */

.

break;

/* An error occurred. */

default:

printf("Wait error: %d\n", GetLastError());

ExitThread(0);

}

/* Set the read event to signaled. */

if (! SetEvent(hEvents[0]) ) {

/* error exit */

}

}

+$#A Using Critical Section Objects

The following example shows how a thread initializes, enters, and leaves a critical section. As with the mutex example (see Using Mutex Objects), this example uses the try-finally structured exception-handling syntax to ensure that the thread calls the LeaveCriticalSection function to release its ownership of the critical section object.

CRITICAL_SECTION GlobalCriticalSection;

/* Initialize the critical section. */

InitializeCriticalSection(&GlobalCriticalSection);

/* Request ownership of the critical section. */

try {

EnterCriticalSection(&GlobalCriticalSection);

.

. /* Access the shared resource. */

.

}

finally {

/* Release ownership of the critical section. */

LeaveCriticalSection(&GlobalCriticalSection);

}

+$#A Synchronization Functions

Following are the functions used in synchronization.

CreateEvent
CreateMutex
CreateSemaphore
DeleteCriticalSection
EnterCriticalSection
GetOverlappedResult
InitializeCriticalSection
InterlockedDecrement
InterlockedExchange
InterlockedIncrement
LeaveCriticalSection
MsgWaitForMultipleObjects
OpenEvent
OpenMutex
OpenSemaphore
PulseEvent
ReleaseMutex
ReleaseSemaphore
ResetEvent
SetEvent
WaitForMultipleObjects
WaitForMultipleObjectsEx
WaitForSingleObject
WaitForSingleObjectEx

Créé avec HelpNDoc Personal Edition: Écrire des livres électroniques ePub pour l'iPad


System Information.rtf

+$#AK System Information

The Microsoft® Win32® application programming interface (API) includes functions that describe the current system configuration.

+$#A About System Information

The functions described in this overview retrieve a variety of data, such as the computer name, user name, settings of environment variables, processor type, system colors, and so on. The Win32 API also includes functions that can change the system configuration, shut down the system, and start the Microsoft® Windows™ Help application.

+$#A System Configuration

Several of the functions in the Win32 API are specifically intended to describe or change the system configuration. At startup, for example, a computer is assigned the name listed in the registry. An application can retrieve this name by using the GetComputerName function and can change the name by using the SetComputerName function. SetComputerName changes the registry, not the current computer name. As a result, the new name is not assigned until the computer is restarted. For more information about the registry, see Registry.

The GetUserName function retrieves the name of the user currently logged onto the system. The user name is either the logon name or the user's full name, if the latter is included in the registry.

The GetSystemInfo function retrieves processor and memory information, such as the page size, original equipment manufacturer (OEM) identifier, number and type of processors, application address range, and so on. The GetVersion function retrieves the major and minor revision numbers of the installed version of Microsoft Windows. The GetKeyboardType function retrieves such information as the type of keyboard and the number of function keys on the current keyboard.

The SystemParametersInfo function retrieves or sets various system attributes, such as double-click time, screen saver time-out, window border width, and desktop pattern. When an application uses SystemParametersInfo to set a parameter, the change takes place immediately. This function also enables applications to update the user profile, so changes to the system will be preserved when the system is restarted.

The GetThreadDesktop function retrieves a handle of the desktop for a given thread. This handle can be used in calls to functions that retrieve and set security attributes.

The Windows directory is the directory that contains Windows-based applications, initialization files, and Help files. The GetWindowsDirectory function retrieves the path to this directory. Applications that create files should do so in the Windows directory. This is the only directory guaranteed to be private to a user running a shared version of Windows.

The system directory is the directory that contains Windows libraries, drivers, and font files. The GetSystemDirectory function retrieves the path to this directory.

An environment variable is a symbolic variable that represents some element of the system, such as a path, a filename, or other literal data. For example, the environment variable PATH represents the directories in which to search for executable files. When a user logs on, the system initializes environment variables based on the environment section of the registry. The ExpandEnvironmentStrings function retrieves the values of specified environment variables.

System metrics are the dimensions of various Windows display elements. (Display elements are the parts of a window and the Windows display that appear on the system display screen.) Typical system metrics include the window border width, icon height, and so on. System metrics also describe other aspects of the system, such as whether a mouse is installed, double-byte characters are supported, or a debugging version of Windows is installed. The GetSystemMetrics function retrieves a specified system metric.

Applications can also retrieve and set the color of window elements such as menus, scroll bars, and buttons by using the GetSysColor and SetSysColors functions, respectively.

+$#A System Shutdown

The Win32 API also provides a way for an application to shut down a local or remote computer. This is particularly useful for system administrators who update system software on remote computers. The InitiateSystemShutdown function starts a timer and displays a dialog box that prompts the user to log off. While the dialog box is displayed, the AbortSystemShutdown function can stop the timer and prevent the computer from shutting down. However, if the timer expires, the computer is shut down. InitiateSystemShutdown can also restart the computer following shutdown.

+$#A Using System Information

• Getting system configuration information

• Getting hardware information

• Changing the colors of window elements

• Shutting down the system

+$#A Getting System Configuration Information

The following example uses the GetComputerName, GetUserName, GetSystemDirectory, GetWindowsDirectory, and ExpandEnvironmentStrings functions to get information that describes the system configuration. This example displays the information in a window's client area.

LPTSTR lpszSystemInfo; /* ptr. to system info. string */

DWORD cchBuff = 256; /* size of comp. or user name */

TCHAR tchBuffer2[BUFFER]; /* buffer for concat'd. str. */

DWORD dwResult; /* function return value */

SYSTEM_INFO siSysInfo; /* struct. for hardware info. */

int aTabs[1] = {260}; /* tab stop for TabbedTextOut */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

lpszSystemInfo = tchBuffer2;

/* Get and display the name of the computer. */

GetComputerName(lpszSystemInfo, &cchBuff);

nSize = sprintf(tchBuffer, "Computer name: %s",

lpszSystemInfo);

TextOut(hdc, 15, 20, tchBuffer, nSize);

/* Get and display the user name. */

GetUserName(lpszSystemInfo, &cchBuff);

nSize = sprintf(tchBuffer, "User name: %s",

lpszSystemInfo);

TextOut(hdc, 15, 40, tchBuffer, nSize);

/* Get and display the system directory. */

nSize = GetSystemDirectory(lpszSystemInfo, MAX_PATH);

nSize = sprintf(tchBuffer, "System directory: %s",

lpszSystemInfo);

TextOut(hdc, 15, 60, tchBuffer, nSize);

/* Get and display the Windows directory. */

nSize = GetWindowsDirectory(lpszSystemInfo, MAX_PATH);

nSize = sprintf(tchBuffer, "Windows directory: %s",

lpszSystemInfo);

TextOut(hdc, 15, 80, tchBuffer, nSize);

/* Display the "environment variables" header. */

nSize = sprintf(tchBuffer,

"Environment variables (partial list):");

TextOut(hdc, 15, 105, tchBuffer, nSize);

/* Expand the OS and NTVERSION environment variables. */

dwResult = ExpandEnvironmentStrings(

"OS=%OS% NTVERSION=%NTVERSION%",

lpszSystemInfo,

BUFFER);

TextOut(hdc, 25, 120, (LPCTSTR) lpszSystemInfo,

dwResult - 1);

/* Expand the PATH environment variable. */

dwResult = ExpandEnvironmentStrings(

"PATH=%PATH%",

lpszSystemInfo,

BUFFER);

TextOut(hdc, 25, 135, (LPCTSTR) lpszSystemInfo,

dwResult - 1);

/* Expand the LIBPATH environment variable. */

dwResult = ExpandEnvironmentStrings(

"LIBPATH=%LIBPATH%",

lpszSystemInfo,

BUFFER);

TextOut(hdc, 25, 150, (LPCTSTR) lpszSystemInfo,

dwResult - 1);

/* Expand the TMP environment variable. */

dwResult = ExpandEnvironmentStrings(

"TMP=%TMP%",

lpszSystemInfo,

BUFFER);

TextOut(hdc, 25, 165, (LPCTSTR) lpszSystemInfo,

dwResult - 1);

The next example uses the GetVersion function to obtain the current version of Windows and then displays the version number in a message box.

DWORD dwResult; /* function return value */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

dwResult = GetVersion();

nSize = sprintf(tchBuffer, "Windows version %d.%d\n",

LOBYTE(LOWORD(dwResult)),

HIBYTE(LOWORD(dwResult)));

MessageBox(NULL, tchBuffer, "GetVersion", MB_ICONINFORMATION);

+$#A Getting Hardware Information

The following example uses the GetSystemInfo function to obtain hardware information such as the OEM identifier, processor type, page size, and so on. The example displays the information in a window's client area.

SYSTEM_INFO siSysInfo; /* struct. for hardware info. */

int aTabs[1] = {260}; /* tab stop for TabbedTextOut */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

/* Display the "hardware information" header. */

nSize = sprintf(tchBuffer,

"Hardware information:");

TextOut(hdc, 15, 20, tchBuffer, nSize);

/* Copy the hardware information to the SYSTEM_INFO structure. */

GetSystemInfo(&siSysInfo);

/* Display the contents of the SYSTEM_INFO structure. */

nSize = sprintf(tchBuffer,

"OEM ID: %u\tNumber of Processors: %u",

siSysInfo.dwOemId,

siSysInfo.dwNumberOfProcessors);

TabbedTextOut(hdc, 25, 40, tchBuffer,

nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,

"Page size: %u\tProcessor Type: %u",

siSysInfo.dwPageSize,

siSysInfo.dwProcessorType);

TabbedTextOut(hdc, 25, 60, tchBuffer,

nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,

"Minimum app address: %lx\tMaximum app address: %lx",

siSysInfo.lpMinimumApplicationAddress,

siSysInfo.lpMaximumApplicationAddress);

TabbedTextOut(hdc, 25, 80, tchBuffer,

nSize, 1, aTabs, 25);

nSize = sprintf(tchBuffer,

"Active processor mask: %u",

siSysInfo.dwActiveProcessorMask);

TextOut(hdc, 25, 100, tchBuffer, nSize);

The following example uses the GetSystemMetrics function to determine whether a mouse is installed and whether the mouse buttons are swapped. The example also uses the SystemParametersInfo function to retrieve the mouse threshold and speed. It displays the information in a message box.

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

BOOL fResult; /* system shutdown flag */

int aMouseInfo[3]; /* array for mouse info. */

/* Is there a mouse? */

fResult = GetSystemMetrics(SM_MOUSEPRESENT);

if (fResult == 0) {

/* Indicate if there is no mouse. */

nSize = sprintf(tchBuffer, "No mouse installed.");

} else {

/*

* If there is a mouse, determine whether

* its buttons are swapped.

*/

fResult = GetSystemMetrics(SM_SWAPBUTTON);

if (fResult == 0) {

nSize = sprintf(tchBuffer, "Buttons not swapped.\r");

} else {

nSize = sprintf(tchBuffer, "Buttons swapped.\r");

}

/* Get the mouse speed and the threshold values. */

SystemParametersInfo(SPI_GETMOUSE, /* get mouse info. */

NULL, /* not used */

&aMouseInfo, /* holds mouse info. */

NULL); /* not used */

nSize += sprintf(tchBuffer + nSize,

"Speed: %d\r", aMouseInfo[2]);

sprintf(tchBuffer + nSize,

"Threshold (x,y): %d,%d",

aMouseInfo[0], aMouseInfo[1]);

}

/* Display the mouse information. */

MessageBox(NULL, tchBuffer, "Mouse information",

MB_ICONINFORMATION);

This next example uses SystemParametersInfo to double the mouse speed and update the MouseSpeed value in the WIN.INI file.

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

int aMouseInfo[3]; /* array for mouse info. */

/* Get the current mouse speed. */

SystemParametersInfo(SPI_GETMOUSE, /* get mouse info. */

NULL, /* not used */

&aMouseInfo, /* holds mouse info. */

NULL); /* not used */

/* Double it. */

aMouseInfo[2] = 2 * aMouseInfo[2];

/* Change the mouse speed to the new value and update WIN.INI. */

SystemParametersInfo(SPI_SETMOUSE, /* set mouse info. */

NULL, /* not used */

aMouseInfo, /* mouse info. to set */

SPIF_UPDATEINIFILE); /* update win.ini */

+$#A Changing the Colors of Window Elements

The following example uses the SetSysColors function to change the color of active window borders to dark purple, of active title bars to dark cyan, and of the window background to light gray.

/* window elements to change */

int aiElements[3] = {COLOR_ACTIVEBORDER,

COLOR_ACTIVECAPTION,

COLOR_WINDOW};

DWORD aColors[3]; /* array of RGB values */

/* Define the new colors. */

aColors[0] = RGB(0x80, 0x00, 0x80); /* dark purple */

aColors[1] = RGB(0x00, 0x80, 0x80); /* dark cyan */

aColors[2] = RGB(0xC0, 0xC0, 0xC0); /* light gray */

/*

* Set the window elements in aiElements to the colors

* specified in aColors.

*/

SetSysColors(3, aiElements, aColors);

The next example uses the GetSysColor function to retrieve the color of the window background and displays the red, green, blue (RGB) value, in hexadecimal notation, in a message box.

DWORD dwResult; /* function return value */

TCHAR tchBuffer[BUFFER]; /* buffer for expanded string */

int nSize; /* size of string */

/* Get the color of the window background. */

dwResult = GetSysColor(COLOR_WINDOW);

nSize = sprintf(tchBuffer,

"Window color: {%x, %x, %x}",

GetRValue(dwResult),

GetGValue(dwResult),

GetBValue(dwResult));

MessageBox(NULL, tchBuffer, "GetSysColor", MB_ICONINFORMATION);

+$#A Shutting Down the System

The following example uses the InitiateSystemShutdown function to begin the system shutdown process on the local computer. Note that the application must first enable the SE_SHUTDOWN_NAME privilege to shut down the computer the user is logged onto.

HANDLE hToken; /* handle to process token */

TOKEN_PRIVILEGES tkp; /* ptr. to token structure */

BOOL fResult; /* system shutdown flag */

/*

* Get the current process token handle

* so we can get shutdown privilege.

*/

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

ErrorHandler("OpenProcessToken failed.");

/* Get the LUID for shutdown privilege. */

LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,

&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,

(PTOKEN_PRIVILEGES) NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)

ErrorHandler("AdjustTokenPrivileges enable failed.");

/* Display the shutdown dialog box and start the time-out countdown. */

fResult = InitiateSystemShutdown(

NULL, /* shut down local computer */

"Click on the main window and press \

the Escape key to cancel shutdown.",

/* msg. to user */

20, /* time-out period */

FALSE, /* prompt user to close apps. */

TRUE); /* reboot after shutdown */

if (!fResult) {

ErrorHandler("InitiateSystemShutdown failed.");

}

/* Disable shutdown privilege. */

tkp.Privileges[0].Attributes = 0;

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,

(PTOKEN_PRIVILEGES) NULL, 0);

if (GetLastError() != ERROR_SUCCESS)

ErrorHandler("AdjustTokenPrivileges disable failed.");

If the AbortSystemShutdown function is executed in the time-out period specified by InitiateSystemShutdown, the system does not shut down. The following example enables the user to prevent the system from shutting down by clicking on the application's main window and pressing the ESC key. As noted earlier, your application must enable the SE_SHUTDOWN_NAME privilege to stop the system shutdown.

HANDLE hToken; /* handle to process token */

TOKEN_PRIVILEGES tkp; /* ptr. to token structure */

BOOL fResult; /* system shutdown flag */

case WM_KEYDOWN:

/* Process only the Escape key. */

if (wParam != VK_ESCAPE) {

break;

}

/*

* Get the current process token handle

* so we can get shutdown privilege.

*/

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

ErrorHandler("OpenProcessToken failed.");

/* Get the LUID for shutdown privilege. */

LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,

&tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1; /* one privilege to set */

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Get shutdown privilege for this process. */

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,

(PTOKEN_PRIVILEGES)NULL, 0);

/* Cannot test the return value of AdjustTokenPrivileges. */

if (GetLastError() != ERROR_SUCCESS)

ErrorHandler("AdjustTokenPrivileges enable failed.");

/* Prevent the system from shutting down. */

fResult = AbortSystemShutdown(NULL);

if (!fResult) {

ErrorHandler("AbortSystemShutdown failed.");

}

/* Disable shutdown privilege. */

tkp.Privileges[0].Attributes = 0;

AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,

(PTOKEN_PRIVILEGES) NULL, 0);

if (GetLastError() != ERROR_SUCCESS)

ErrorHandler("AdjustTokenPrivileges disable failed.");

break;

+$#A System Information Functions

Following are the functions used to retrieve or set system information, shut down the system, or start Windows Help.

AbortSystemShutdown
ExpandEnvironmentStrings
GetComputerName
GetKeyboardType
GetSysColor
GetSystemDirectory
GetSystemInfo
GetSystemMetrics
GetThreadDesktop
GetUserName
GetVersion
GetVersionEx
GetWindowsDirectory
InitiateSystemShutdown
SetComputerName
SetSysColors
SystemParametersInfo
WinHelp

Obsolete or Deleted Functions

DOS3Call
GetCurrentPDB
GetCurrentTask
GetDOSEnvironment
GetWinFlags
IsTask

Créé avec HelpNDoc Personal Edition: Éditeur complet de livres électroniques ePub


System Messages.rtf

+$#AK System Messages

System messages provide the way to uniformly notify all applications and other components in the system of changes that may affect their operation and access to resources. Applications and other components use and process system messages to take advantage of new resources when they become available and to prevent loss of data when existing resources become unavailable.

+$#A About System Messages

The Windows operating system provides two predefined classes of system messages: device messages and power messages. Device messages notify applications and installable drivers of device change events; power messages notify these of power management events. Applications and other components can also define and use their own system messages to provide for notification of other types of events.

This overview describes system messages, system-message broadcasts, and the specific class of system messages called device messages. For more information about power messages, see Power Management.

+$#A System Messages and System-Message Broadcasts

Each system message consists of a message identifier and two 32-bit parameters, wParam and lParam. The message identifier is a unique value that specifies the message purpose. The parameters provide additional information that is message-specific, but the wParam parameter is often a notification value that further specifies the message purpose.

A system-message broadcast is simply the sending of a system message to components in the system. You broadcast a system message by using the BroadcastSystemMessage function and specifying the recipients of the message. Rather than specify individual recipients, you must specify one or more types of recipients. These types are applications, installable drivers, Windows-based network drivers, and system-level device drivers. BroadcastSystemMessage sends messages to all members of each type you specify.

Most applications do not broadcast system messages. Instead, they process system messages sent by other components. The operating system typically broadcasts system messages in response to changes that usually take place within system-level device drivers. The device driver or related component generates the system message and broadcasts it to applications and other components to notify them of the change. For example, the subsystem responsible for disk drives generates and broadcasts a system message whenever the device driver for the floppy disk drive detects a change of media such as when the user inserts a disk in the drive.

Applications receive system messages through the window procedure of their top-level windows. System messages are not sent to child windows. The action an application takes in response to a system message depends on the message. Some system messages, called query messages, require the application to respond by returning either TRUE or FALSE to indicate whether the system should continue to broadcast the message to other recipients.

+$#A Custom System Messages

You can create your own system messages and use them to coordinate activities between applications and other components in the system. This is especially useful if you have created your own installable drivers or system-level device drivers. Your custom system messages can carry information to and from your driver and the applications that use the driver.

You broadcast custom system messages using the BroadcastSystemMessage function.(System-level device drivers use a related, system-level function.) The function sends the messages to these recipients in this order: system-level device drivers, Windows-based network drivers, installable drivers, and applications. This means that system-level device drivers, if chosen as recipients, always get the first opportunity to respond to a system message. Within a given recipient type, no driver is guaranteed to receive a given message before any other driver. This means a system message intended for a specific driver must have an globally-unique message identifier so that no other driver unintentionally processes it.

Query messages are a useful way to poll recipients for permission to carry out a given action. You can generate your own a query messages by setting the BSF_QUERY value in the dwFlags parameter when calling BroadcastSystemMessage. Each recipient of the query message must return TRUE for the function to send the message to the next recipient. If any recipient returns FALSE, the broadcast ends immediately and the function returns 0.

You can create installable drivers that broadcast and process system messages. An installable driver is a dynamic-link library (DLL) that exports a DriverProc function. The driver receives system messages through its DriverProc function and can broadcast messages using BroadcastSystemMessage. Installable drivers are typically used to support multimedia devices, such as sound boards, but can be used for other devices and purposes too.

Windows-based network drivers are dynamic-link libraries that provide the underlying support for applications that use the Windows network functions to connect to and browse network resources. System-level device drivers are operating-system-specific executable components that provide direct access to and management of the hardware devices of the computer. The details regarding how these components process system messages is beyond the scope of this book.

+$#A Device Messages and Device Change Events

Device messages are system messages that notify applications and other components of device change events. These events occur whenever the system detects a change to the system hardware such as when the user docks or undocks a laptop computer, or inserts or removes a device such as a PCMCIA card. Change events can occur while the system is running or when the system resumes operation after temporarily being suspended.

To help ensure that applications and installable drivers do not lose data when devices become unavailable, the operating system monitors the hardware configuration and sends device message to the applications and installable drivers to notify them of the changes and to give them the opportunity to prepare for the changes before they occur.

For each event, the system broadcasts a WM_DEVICECHANGE message to all applications and installable drivers. In this message, the wParam parameter identifies the event type and the lParam parameter is usually the address of event-specific data.

The event-specific data identifies the device and provides additional detail about the event. The format of this data depends on the device type, but the first few bytes always has the same format as the DEV_BROADCAST_HDR structure. This means you can always check the dbch_devicetype member in the data to determine the device type.

The system sends a DBT_DEVICEARRIVAL message (that is, a WM_DEVICECHANGE message with wParam set to DBT_DEVICEARRIVAL) whenever a device has been inserted and is available for use. Applications typically check the device type and begin using the device immediately if appropriate.

The system sends a DBT_DEVICEQUERYREMOVE to request permission to remove a device. If an application determines that it needs the device, it can deny this request and cancel the removal by returning FALSE. To determine whether it needs the device, an application can display a dialog box to prompt the user for instructions. If the application does not need the device, it must return TRUE. The system immediately sends a DBT_DEVICEQUERYREMOVEFAILED message if any application or driver canceled a previous request to remove a device.

The system sends a DBT_DEVICEREMOVEPENDING message as a last warning before a device is removed. At this point, the application cannot cancel the removal, so if it is using the device it must prepare for its removal to prevent loss of data. This is especially important when a network connection is being removed. The application must determine whether any of its open files or pipes are on that connection. It can do this by comparing the network resource identifier in the event-specific data of the message with the resource identifiers previously obtained for the files and pipes. The system sends a DBT_DEVICEREMOVECOMPLETE when a device has been removed and is no longer available.

The system sends DBT_DEVICETYPESPECIFIC message whenever a device-specific event occurs. It sends a DBT_CONFIGCHANGED message if current configuration has changed, such as when the operational parameters of a device have been changed by the user.

+$#A Using System Messages

• Processing a Request to Remove a Device

+$#A Processing a Request to Remove a Device

An application receives a DBT_DEVICEQUERYREMOVE message when a component in the system has decided to remove a given device. When the application receives this message, it should determine whether it is using the given device and either cancel or prepare for the removal. In the following example, the application processes the message by checking the type of device that is to be removed.

PDEV_BROADCAST_HDR pdbch;
PDEV_BROADCAST_VOLUME pdbcv;

case WM_DEVICECHANGE:
pdbch = (PDEV_BROADCAST_HDR) lParam;
switch (pdbch->dbch_devicetype) {
case DBT_DEVTYP_VOLUME:
pdbcv = (PDEV_BROADCAST_VOLUME) pdbch;
if (pdbcv->dbcv_flags == DBTF_MEDIA)
// pdbcv->dbcv_unitmask identifies which logical drive
}
return TRUE;
default:
return TRUE;
}

+$#A System Message Reference

This section describes the functions, messages, and structures that support system messages.

+$#A System Message Functions

This section describes the functions that support system messages.

BroadcastSystemMessage

+$#A System Messages Reference

This section describes the device messages.

DBT_CONFIGCHANGED
DBT_DEVICEARRIVAL
DBT_DEVICEQUERYREMOVE
DBT_DEVICEQUERYREMOVEFAILED
DBT_DEVICEREMOVECOMPLETE
DBT_DEVICEREMOVEPENDING
DBT_DEVICETYPESPECIFIC
WM_DEVICECHANGE

+$#A System Message Structures

This section describes the structures associated with device messages.

_DEV_BROADCAST_USERDEFINED
DEV_BROADCAST_HDR
DEV_BROADCAST_OEM
DEV_BROADCAST_PORT
DEV_BROADCAST_VOLUME

Créé avec HelpNDoc Personal Edition: Créer de la documentation iPhone facilement


Tab Controls.rtf

+$#AK Tab Controls

A tab control is analogous to the dividers in a notebook or the labels in a file cabinet. By using a tab control, an application can define multiple pages for the same area of a window or dialog box. Each page consists of a set of information or a group of controls that the application displays when the user selects the corresponding tab. A special type of tab control displays tabs that look like buttons. Clicking a button should immediately perform a command instead of displaying a page.

+$#A About Tab Controls

You can create a tab control by calling the CreateWindowEx function, specifying the WC_TABCONTROL window class. This window class is registered when the common controls dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls function.

You send messages to a tab control to add tabs and otherwise affect the control's appearance and behavior. Each message has a corresponding macro, which you can use instead of sending the message explicitly. You cannot, however, disable an individual tab in a tab control. However, you can disable a tab control in a property sheet by disabling the corresponding page.

+$#A About Tab Control Styles

You can apply specific characteristics to tab controls by specifying tab control styles (window styles specific to tab controls). For example, you can specify the alignment and general appearance of the tabs in a tab control.

You can cause the tabs to look like buttons by specifying the TCS_BUTTONS style. Tabs in this type of tab control should serve the same function as button controls; that is, clicking a tab should carry out a command instead of displaying a page. Because the display area in a button tab control is typically not used, no border is drawn around it.

You can cause a tab to receive the input focus when clicked by specifying the TCS_FOCUSONBUTTONDOWN style. This style is typically used only with the TCS_BUTTONS style. You can specify that a tab never receives the input focus by using the TCS_FOCUSNEVER style.

By default, a tab control displays only one row of tabs. If not all tabs can be shown at once, the tab control displays an up-down control so that the user can scroll additional tabs into view. For more information about up-down controls, see Up-Down Controls. You can cause a tab control to display multiple rows of tabs, if necessary, by specifying the TCS_MULTILINE style. In this way, all tabs can be displayed at once. The tabs are left-aligned within each row unless you specify the TCS_RIGHTJUSTIFY style. In this case, the width of each tab is increased so that each row of tabs fills the entire width of the tab control.

A tab control automatically sizes each tab to fit its icon, if any, and its label. To give all tabs the same width, you can specify the TCS_FIXEDWIDTH style. The control sizes all the tabs to fit the widest label, or you can assign a specific width and height by using the TCM_SETITEMSIZE message. Within each tab, the control centers the icon and label with the icon to the left of the label. You can force the icon to the left, leaving the label centered, by specifying the TCS_FORCEICONLEFT style. You can left-align both the icon and label by using the TCS_FORCELABELLEFT style. You cannot use the TCS_FIXEDWIDTH style with the TCS_RIGHTJUSTIFY style.

You can specify that the parent window draws the tabs in the control by using the TCS_OWNERDRAWFIXED style. For more information about owner-drawn tabs, see Owner-Drawn Tabs.

You can specify that a tab control create a tool tip control by using the TCS_TOOLTIPS style. For more information about using tool tip controls with a tab control, see Tooltip Controls.

+$#A Tabs and Tab Attributes

Each tab in a tab control consists of an icon, a label, and application-defined data. This information is specified by a TC_ITEM structure. You can add tabs to a tab control, get the number of tabs, retrieve and set the contents of a tab, and delete tabs. Tabs are identified by their zero-based index.

To add tabs to a tab control, use the TCM_INSERTITEM message, specifying the position of the item and the address of a TC_ITEM structure. You can retrieve and set the contents of an existing tab by using the TCM_GETITEM and TCM_SETITEM messages. For each tab, you can specify an icon, a label, or both. You can also specify application-defined data to associate with the tab.

You can retrieve the current number of tabs by using the TCM_GETITEMCOUNT message, delete a tab by using the TCM_DELETEITEM message, and delete all tabs in a tab control by using the TCM_DELETEALLITEMS message.

You can associate application-defined data with each tab. For example, you might save information about each page with its corresponding tab. By default, a tab control allocates four extra bytes per tab for application-defined data. You can change the number of extra bytes per tab by using the TCM_SETITEMEXTRA message. You can only use this message when the tab control is empty.

The application-defined data is specified by the lParam member of the TC_ITEM structure. If you use more than four bytes of application-defined data, you need to define your own structure and use it instead of TC_ITEM. You can retrieve and set application-defined data the same way you retrieve and set other information about a tab ⎯ by using the TCM_GETITEM and TCM_SETITEM messages.

The first member of your structure must be a TC_ITEMHEADER structure, and the remaining members must specify application-defined data. TC_ITEMHEADER is identical to TC_ITEM, except it does not have the lParam member. The difference between the size of your structure and the size of TC_ITEMHEADER should equal the number of extra bytes per tab.

+$#A Display Area

The display area of a tab control is the area in which an application displays the current page. Typically, an application creates a child window or dialog box, setting the window size and position to fit the display area. Given the window rectangle for a tab control, you can calculate the bounding rectangle of the display area by using the TCM_ADJUSTRECT message.

Sometimes the display area must be a particular size ⎯ for example, the size of a modeless child dialog box. Given the bounding rectangle for the display area, you can use TCM_ADJUSTRECT to calculate the corresponding window rectangle for the tab control.

+$#A Tab Selection

When the user selects a tab, a tab control sends its parent window notification messages in the form of WM_NOTIFY messages. The TCN_SELCHANGING notification message is sent before the selection changes, and the TCN_SELCHANGE notification message is sent after the selection changes.

You can process TCN_SELCHANGING to save the state of the outgoing page. You can return TRUE to prevent the selection from changing. For example, you might not want to switch away from a child dialog box in which a control has an invalid setting.

You must process TCN_SELCHANGE to display the incoming page in the display area. This might simply entail changing the information displayed in a child window. More often, each page consists of a child window or dialog box. In this case, an application might process this notification by destroying or hiding the outgoing child window or dialog box and by creating or showing the incoming child window or dialog box.

You can retrieve and set the current selection by using the TCM_GETCURSEL and TCM_SETCURSEL messages.

+$#A Tab Control Image Lists

Each tab can have an icon associated with it, which is specified by an index into the image list for the tab control. When a tab control is created, it has no image list associated with it. An application can create an image list by using the ImageList_Create function and then assign it to a tab control by using the TCM_SETIMAGELIST message.

You can add images to a tab control's image list just as you would to any other image list. However, an application should remove images by using the TCM_REMOVEIMAGE message instead of the ImageList_Remove function. This message ensures that each tab remains associated with the same image it had been.

Destroying a tab control does not destroy an image list that is associated with it. You must destroy the image list separately. This is useful if you want to assign the same image list to multiple tab controls.

To retrieve the handle of the image list currently associated with a tab control, you can use the TCM_GETIMAGELIST message.

+$#A Tab Size and Position

Each tab in a tab control has a size and position. You can set the size of tabs, retrieve the bounding rectangle of a tab, or determine which tab is at a specified position.

For fixed-width and owner-drawn tab controls, you can set the exact width and height of tabs by using the TCM_SETITEMSIZE message. In other tab controls, each tab's size is calculated based on the icon and label for the tab. The tab control includes space for a border and an additional margin. You can set the thickness of the margin by using the TCM_SETPADDING message.

You can determine the current bounding rectangle for a tab by using the TCM_GETITEMRECT message. You can determine which tab, if any, is at a specified location by using the TCM_HITTEST message.

In a tab control with the TCS_MULTILINE style, you can determine the current number of rows of tabs by using the TCM_GETROWCOUNT message.

+$#A Owner-Drawn Tabs

If a tab control has the TCS_OWNERDRAWFIXED style, the parent window must paint tabs by processing the WM_DRAWITEM message. The tab control sends this message whenever a tab needs to be painted. The lParam parameter specifies the address of a DRAWITEMSTRUCT structure, which contains the index of the tab, its bounding rectangle, and the device context (DC) in which to draw.

By default, the itemData member of DRAWITEMSTRUCT contains the value of the lParam member of the TC_ITEM structure. However, if you change the amount of application-defined data per tab, itemData contains a pointer to the data instead. You can change the amount of application-defined data per tab by using the TCM_SETITEMEXTRA message.

To specify the size of items in a tab control, the parent window must process the WM_MEASUREITEM message. Because all tabs in an owner-drawn tab control are the same size, this message is sent only once. There is no tab control style for owner-drawn tabs of varying size. You can also set the width and height of tabs by using the TCM_SETITEMSIZE message.

+$#A Tooltip Controls

You can use a tooltip control to provide a brief description of each tab in a tab control. A tab control that has the TCS_TOOLTIPS style creates a tooltip control when it is created, and when the tab control is destroyed, it destroys the tooltip control. You can also create a tooltip control and assign it to a tab control.

If you use a tooltip control with a tab control, the parent window must process the TTN_NEEDTEXT notification message to provide a description of each tab on request.

To use the same tooltip control with more than one tab control, create the tooltip control yourself and assign it to the tab control by using the TCM_SETTOOLTIPS message. You can get the handle of a tab control's current tooltip control by using the TCM_GETTOOLTIPS message. If you use this method, you should not use the TCS_TOOLTIPS style. For more information about tooltip controls, see Tooltip Controls.

+$#A Default Tab Control Message Processing

This section describes the message processing performed by a tab control. Messages specific to tab controls are discussed elsewhere and are, therefore, not included here.

Message

Processing performed

WM_CAPTURECHANGED

Does nothing if the tab control released the mouse capture itself. If another window captured the mouse and a button is held down, the command releases the button.

WM_CREATE

Allocates and initializes an internal data structure. The control creates a tool tip control if the TCS_TOOLTIPS style is specified.

WM_DESTROY

Frees resources allocated during WM_CREATE processing.

WM_GETDLGCODE

Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values.

WM_GETFONT

Returns the handle of the font used for labels.

WM_KEYDOWN

Processes DIRECTION keys and changes the selection, if appropriate.

WM_KILLFOCUS

Invalidates the tab that has the focus, so it will be repainted to reflect an unfocused state.

WM_LBUTTONDOWN

Forwards the message to the tool tip control, if any, and changes the selection if the user is clicking a tab. If the user is clicking a button, the control redraws the button to give a sunken appearance and captures the mouse.

If the user is clicking either a tab or button and the TCS_FOCUSONBUTTONDOWN style is specified, the control sets the focus to itself.

WM_LBUTTONUP

Releases the mouse if a button was pressed. If the cursor is over the button and is being held down, the control changes the selection accordingly and redraws the button.

WM_MOUSEMOVE

Forwards the message to the tool tip control, if any. If the TCS_BUTTONS style is specified and the mouse button is being held down after clicking, the control may also redraw the affected button to give it a raised or sunken appearance.

WM_NOTIFY

Forwards notification messages sent by the tool tip control.

WM_PAINT

Draws a border around the display area (unless the TCS_BUTTONS style is specified) and paints any tabs that intersect the invalid rectangle.

For each tab, it draws the body of each tab (or sends a WM_DRAWITEM message to the parent window) and then draws a border around the tab.

WM_RBUTTONDOWN

Sends an NM_RCLICK notification message to the parent window.

WM_SETFOCUS

Invalidates the tab that has the focus so that it will be repainted to reflect a focused state.

WM_SETFONT

Sets the font used for labels.

WM_SETREDRAW

Sets the state of an internal flag that determines whether the control is repainted when items are inserted and deleted, when the font is changed, and so on.

WM_SIZE

Recalculates the positions of tabs and may invalidate part of the tab control to force repainting of some or all tabs.

+$#A Using Tab Controls

This section provides two examples that use tab controls. The first example uses a tab control to switch between multiple pages of text in an application's main window. The second example uses a tab control to switch between multiple pages of controls in a dialog box.

• Creating a tab control

• Creating a tabbed dialog box

+$#A Creating a Tab Control

The example in this section creates a tab control and displays it in the client area of the application's main window. The application displays a third window (a static control) in the display area of the tab control. The parent window positions and sizes the tab control and static control when it processes the WM_SIZE message.

There are seven tabs, one for each day of the week. When the user selects a tab, the application displays the name of the corresponding day in the static control. The following global variables are used in this example.

// Global variables

HINSTANCE g_hinst; // handle of application instance

char g_achTemp[256]; // temporary buffer for strings

HWND g_hwndMain; // main application window

HWND g_hwndTab; // tab control

HWND g_hwndDisplay; // handle of static control in

// tab control's display area

The following function creates the tab control and adds a tab for each day of the week. The names of the days are defined as string resources, consecutively numbered starting with IDS_FIRSTDAY (defined in the application's header file). Both the parent window and the tab control must have the WS_CLIPSIBLINGS window style. The application's initialization function calls this function after creating the main window.

// DoCreateTabControl - creates a tab control, sized to fit the

// specified parent window's client area, and adds some tabs.

// Returns the handle of the tab control.

// hwndParent - parent window (the application's main window)

HWND WINAPI DoCreateTabControl(HWND hwndParent)

{

RECT rcClient;

HWND hwndTab;

TC_ITEM tie;

int i;

// Get the dimensions of the parent window's client area, and

// create a tab control child window of that size.

GetClientRect(hwndParent, &rcClient);

InitCommonControls();

hwndTab = CreateWindow(

WC_TABCONTROL, "",

WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,

0, 0, rcClient.right, rcClient.bottom,

hwndParent, NULL, g_hinst, NULL

);

if (hwndTab == NULL)

return NULL;

// Add tabs for each day of the week.

tie.mask = TCIF_TEXT | TCIF_IMAGE;

tie.iImage = -1;

tie.pszText = g_achTemp;

for (i = 0; i < 7; i++) {

LoadString(g_hinst, IDS_FIRSTDAY + i,

g_achTemp, sizeof(g_achTemp));

if (TabCtrl_InsertItem(hwndTab, i, &tie) == -1) {

DestroyWindow(hwndTab);

return NULL;

}

}

return hwndTab;

}

The following function creates the static control that occupies the tab control's display area. The application's initialization function calls this function after creating the main window and the tab control.

// DoCreateDisplayWindow - creates a child window (a static

// control) to occupy the tab control's display area.

// Returns the handle of the static control.

// hwndParent - parent window (the application's main window)

HWND WINAPI DoCreateDisplayWindow(HWND hwndParent)

{

HWND hwndStatic = CreateWindow("STATIC", "",

WS_CHILD | WS_VISIBLE | WS_BORDER,

0, 0, CW_USEDEFAULT, CW_USEDEFAULT,

hwndParent, NULL, g_hinst, NULL);

return hwndStatic;

}

Following are the relevant portions of the application's window procedure. The application processes the WM_SIZE message to position and size the tab control and the static control. To determine the appropriate position and size for the static control, this example sends the tab control a TCM_ADJUSTRECT message (by using the TabCtrl_AdjustRect macro).

When a tab is selected, the tab control sends a WM_NOTIFY message, specifying the TCN_SELCHANGE notification message. The application processes this notification message by setting the text of the static control.

// MainWindowProc - processes the message for the main window class.

// The return value depends on the message.

// hwnd - handle of the window

// uMsg - identifier for the message

// wParam - message-specific parameter

// lParam - message-specific parameter

LRESULT CALLBACK MainWindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

{

switch (uMsg) {

case WM_SIZE: {

HDWP hdwp;

RECT rc;

// Calculate the display rectangle, assuming the

// tab control is the size of the client area.

SetRect(&rc, 0, 0,

LOWORD(lParam), HIWORD(lParam));

TabCtrl_AdjustRect(g_hwndTab, FALSE, &rc);

// Size the tab control to fit the client area.

hdwp = BeginDeferWindowPos(2);

DeferWindowPos(hdwp, g_hwndTab, NULL, 0, 0,

LOWORD(lParam), HIWORD(lParam),

SWP_NOMOVE | SWP_NOZORDER

);

// Position and size the static control to fit the

// tab control's display area, and make sure the

// static control is in front of the tab control.

DeferWindowPos(hdwp,

g_hwndDisplay, HWND_TOP, rc.left, rc.top,

rc.right - rc.left, rc.bottom - rc.top, 0

);

EndDeferWindowPos(hdwp);

}

break;

case WM_NOTIFY:

switch (HIWORD(wParam)) {

case 0:

.

. // menu command processing

.

case TCN_SELCHANGE: {

int iPage = TabCtrl_GetCurSel(g_hwndTab);

LoadString(g_hinst, IDS_FIRSTDAY + iPage,

g_achTemp, sizeof(g_achTemp));

SendMessage(g_hwndDisplay, WM_SETTEXT, 0

(LPARAM) g_achTemp);

}

break;

}

break;

.

. // additional message processing

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

+$#A Creating a Tabbed Dialog Box

The example in this section creates a dialog box that uses tabs to provide multiple pages of controls. The main dialog box is a modal dialog box. Each page of controls is defined by a dialog box template that specifies the WS_CHILD style. When a tab is selected, the example creates a modeless dialog box for the incoming page and destroys the dialog box for the outgoing page.

Note In many cases, you can implement multiple-page dialog boxes more easily by using property sheets. For more information about property sheets, see Property Sheets.

The template for the main dialog box simply defines two button controls. When processing the WM_INITDIALOG message, the dialog box procedure creates a tab control and loads the dialog template resources for each of the child dialog boxes.

The information is saved in an application-defined structure called DLGHDR. A pointer to this structure is associated with the dialog box window by using the SetWindowLong function. The structure is defined in the application's header file, as follows.

#define C_PAGES 3

typedef struct tag_dlghdr {

HWND hwndTab; // tab control

HWND hwndDisplay; // current child dialog box

RECT rcDisplay; // display rectangle for the tab control

DLGTEMPLATE *apRes[C_PAGES];

} DLGHDR;

The following function processes the WM_INITDIALOG message for the main dialog box. The function allocates the DLGHDR structure, loads the dialog template resources for the child dialog boxes, and creates the tab control.

The size of each child dialog box is specified by the DLGTEMPLATE structure. The function examines the size of each dialog box and uses the macro for the TCM_ADJUSTRECT message to calculate an appropriate size for the tab control. Then it sizes the dialog box and positions the two buttons accordingly. This example sends TCM_ADJUSTRECT by using the TabCtrl_AdjustRect macro.

VOID WINAPI OnTabbedDialogInit(HWND hwndDlg)

{

DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));

DWORD dwDlgBase = GetDialogBaseUnits();

int cxMargin = LOWORD(dwDlgBase) / 4;

int cyMargin = HIWORD(dwDlgBase) / 8;

TC_ITEM tie;

RECT rcTab;

HWND hwndButton;

RECT rcButton;

int i;

// Save a pointer to the DLGHDR structure.

SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr);

// Create the tab control.

pHdr->hwndTab = CreateWindow(

WC_TABCONTROL, "",

WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,

0, 0, 100, 100,

hwndDlg, NULL, g_hinst, NULL

);

// Add a tab for each of the three child dialog boxes.

tie.mask = TCIF_TEXT | TCIF_IMAGE;

tie.iImage = -1;

tie.pszText = "First";

TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);

tie.pszText = "Second";

TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);

tie.pszText = "Third";

TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);

// Lock the resources for the three child dialog boxes.

pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(DLG_FIRST));

pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(DLG_SECOND));

pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(DLG_THIRD));

// Determine the bounding rectangle for all child dialog boxes.

SetRectEmpty(&rcTab);

for (i = 0; i < C_PAGES; i++) {

if (pHdr->apRes[i]->cx > rcTab.right)

rcTab.right = pHdr->apRes[i]->cx;

if (pHdr->apRes[i]->cy > rcTab.bottom)

rcTab.bottom = pHdr->apRes[i]->cy;

}

rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;

rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;

// Calculate how large to make the tab control, so

// the display area can accomodate all the child dialog boxes.

TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);

OffsetRect(&rcTab, cxMargin - rcTab.left,

cyMargin - rcTab.top);

// Calculate the display rectangle.

CopyRect(&pHdr->rcDisplay, &rcTab);

TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);

// Set the size and position of the tab control, buttons,

// and dialog box.

SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top,

rcTab.right - rcTab.left, rcTab.bottom - rcTab.top,

SWP_NOZORDER);

// Move the first button below the tab control.

hwndButton = GetDlgItem(hwndDlg, BTN_CLOSE);

SetWindowPos(hwndButton, NULL,

rcTab.left, rcTab.bottom + cyMargin, 0, 0,

SWP_NOSIZE | SWP_NOZORDER);

// Determine the size of the button.

GetWindowRect(hwndButton, &rcButton);

rcButton.right -= rcButton.left;

rcButton.bottom -= rcButton.top;

// Move the second button to the right of the first.

hwndButton = GetDlgItem(hwndDlg, BTN_TEST);

SetWindowPos(hwndButton, NULL,

rcTab.left + rcButton.right + cxMargin,

rcTab.bottom + cyMargin, 0, 0,

SWP_NOSIZE | SWP_NOZORDER);

// Size the dialog box.

SetWindowPos(hwndDlg, NULL, 0, 0,

rcTab.right + cyMargin +

2 * GetSystemMetrics(SM_CXDLGFRAME),

rcTab.bottom + rcButton.bottom + 2 * cyMargin +

2 * GetSystemMetrics(SM_CYDLGFRAME) +

GetSystemMetrics(SM_CYCAPTION),

SWP_NOMOVE | SWP_NOZORDER);

// Simulate selection of the first item.

OnSelChanged(hwndDlg);

}

// DoLockDlgRes - loads and locks a dialog template resource.

// Returns a pointer to the locked resource.

// lpszResName - name of the resource

DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName)

{

HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);

HGLOBAL hglb = LoadResource(g_hinst, hrsrc);

return (DLGTEMPLATE *) LockResource(hglb);

}

The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page.

// OnSelChanged - processes the TCN_SELCHANGE notification.

// hwndDlg - handle of the parent dialog box

VOID WINAPI OnSelChanged(HWND hwndDlg)

{

DLGHDR *pHdr = (DLGHDR *) GetWindowLong(

hwndDlg, GWL_USERDATA);

int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);

// Destroy the current child dialog box, if any.

if (pHdr->hwndDisplay != NULL)

DestroyWindow(pHdr->hwndDisplay);

// Create the new child dialog box.

pHdr->hwndDisplay = CreateDialogIndirect(g_hinst,

pHdr->apRes[iSel], hwndDlg, ChildDialogProc);

}

The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area.

// OnChildDialogInit - Positions the child dialog box to fall

// within the display area of the tab control.

VOID WINAPI OnChildDialogInit(HWND hwndDlg)

{

HWND hwndParent = GetParent(hwndDlg);

DLGHDR *pHdr = (DLGHDR *) GetWindowLong(

hwndParent, GWL_USERDATA);

SetWindowPos(hwndDlg, HWND_TOP,

pHdr->rcDisplay.left, pHdr->rcDisplay.top,

0, 0, SWP_NOSIZE);

}

+$#A Tab Control Reference

The messages, notification messages, and structures are associated with tab controls. These elements can be grouped as follows.

Tabs and Tab Attributes

TC_ITEM
TC_ITEMHEADER
TCM_DELETEALLITEMS
TCM_DELETEITEM
TCM_GETITEM
TCM_INSERTITEM
TCM_SETITEM
TCM_SETITEMEXTRA

Tab Selection

TCM_GETCURFOCUS
TCM_GETCURSEL
TCM_SETCURSEL
TCN_SELCHANGE
TCN_SELCHANGING

Tab Size and Position

TC_HITTESTINFO
TCM_ADJUSTRECT
TCM_GETITEMRECT
TCM_GETROWCOUNT
TCM_HITTEST
TCM_SETITEMSIZE
TCM_SETPADDING

Miscellaneous

TC_KEYDOWN
TCM_GETIMAGELIST
TCM_GETITEMCOUNT
TCM_GETTOOLTIPS
TCM_REMOVEIMAGE
TCM_SETIMAGELIST
TCM_SETTOOLTIPS
TCN_KEYDOWN

+$#A Tab Control Messages

An application sends messages to add tabs and otherwise control the appearance and behavior of a tab control. Each message has a corresponding macro, which you can use instead of sending the message explicitly.

TCM_ADJUSTRECT
TCM_DELETEALLITEMS
TCM_DELETEITEM
TCM_GETCURFOCUS
TCM_GETCURSEL
TCM_GETIMAGELIST
TCM_GETITEM
TCM_GETITEMCOUNT
TCM_GETITEMRECT
TCM_GETROWCOUNT
TCM_GETTOOLTIPS
TCM_HITTEST
TCM_INSERTITEM
TCM_REMOVEIMAGE
TCM_SETCURSEL
TCM_SETIMAGELIST
TCM_SETITEM
TCM_SETITEMEXTRA
TCM_SETITEMSIZE
TCM_SETPADDING
TCM_SETTOOLTIPS

+$#A Tab Control Notification Messages

The following notification messages are used with tab controls.

TCN_KEYDOWN
TCN_SELCHANGE
TCN_SELCHANGING

+$#A Tab Control Structures

The following structures are used with tab controls.

TC_HITTESTINFO
TC_ITEM
TC_ITEMHEADER
TC_KEYDOWN

+$#A Tab Control Styles

The following window styles are specific to tab controls.

TCS_BUTTONS

Specifies that tabs appear as buttons and no border is drawn around the display area.

TCS_FIXEDWIDTH

Specifies that all tabs are the same width. This style cannot be combined with the TCS_RIGHTJUSTIFY style.

TCS_FOCUSNEVER

Specifies that the tab control never receives the input focus.

TCS_FOCUSONBUTTONDOWN

Specifies that tabs receive the input focus when clicked.

TCS_FORCEICONLEFT

Aligns icons with the left edge of each fixed-width tab. This style can only be used with the TCS_FIXEDWIDTH style.

TCS_FORCELABELLEFT

Aligns labels with the left edge of each fixed-width tab; that is, it displays the label immediately to the right of the icon instead of centering it.

This style can only be used with the TCS_FIXEDWIDTH style, and it implies the TCS_FORCEICONLEFT style.

TCS_MULTILINE

Displays multiple rows of tabs, if necessary, so all tabs are visible at once.

TCS_OWNERDRAWFIXED

Specifies that the parent window is responsible for drawing tabs.

TCS_RAGGEDRIGHT

Does not stretch each row of tabs to fill the entire width of the control. This style is the default.

TCS_RIGHTJUSTIFY

Increases the width of each tab, if necessary, so that each row of tabs fills the entire width of the tab control.

This window style is ignored unless the TCS_MULTILINE style is also specified.

TCS_SINGLELINE

Displays only one row of tabs. The user can scroll to see more tabs, if necessary. This style is the default.

TCS_TABS

Specifies that tabs appear as tabs and that a border is drawn around the display area. This style is the default.

TCS_TOOLTIPS

Specifies that the tab control has a tooltip control associated with it. For more information about tooltip controls, see Tooltip Controls.

Créé avec HelpNDoc Personal Edition: Éditeur de documentation CHM facile


Tape Backup.rtf

+$#AK Tape Backup

The tape functions provided in the Microsoft® Win32® application programming interface (API) enable backup applications to read from and write to a tape, initialize a tape, and retrieve tape or tape drive information.

+$#A About Tape Backup

In Microsoft® Windows™, a tape volume consists of a recording medium and its physical carrier. The entire length of tape in a volume is not available for recording data. Short sections at the beginning and the end of the tape are reserved for attaching the tape to the hubs in the carrier. The first position on the tape where data can be recorded is the called the beginning-of-medium marker, and the last position is called the end-of-medium marker.

Every tape volume has one or more partitions. A partition is a portion of the volume, containing its own beginning and ending points, that does not overlap with any other portion of the volume. Each partition has three predefined positions. The first position in a partition where you can record data is called the beginning-of-partition marker, and the last is called end-of-partition marker. The early-warning position is located immediately before the end-of-partition marker. The early-warning position notifies tape applications to transfer buffered data to the tape before reaching the end-of-partition marker.

The area between a partition's beginning and ending points is typically divided into sections by filemarks or setmarks. Filemarks and setmarks are special recorded elements that do not contain user data; they simply divide the partition into smaller areas to provide an address scheme. Filemarks and setmarks serve similar purposes, but setmarks provide faster positioning on high-capacity tape drives.

Typically, tape devices support filemarks and setmarks. Support of both enables tape data to be formatted such that setmarks separate data from different disk volumes and filemarks separate data from individual files on a disk volume.

Another recorded element that denotes locations on the tape is an erase gap, an area of erased tape or a pattern that the device does not recognize as a mark or as user data.

There are three types of filemarks. A short filemark contains a short erase gap that cannot be overwritten unless the write operation is performed from the beginning of the partition or from an earlier long filemark. A long filemark contains a long erase gap that enables an application to position the tape at the beginning of the filemark and to overwrite the filemark and the erase gap. A normal filemark does not contain an erase gap. Tape devices that use filemarks support either short and long filemarks or normal filemarks, but not all three.

The area on a partition between setmarks or filemarks is available for recording data. A unit of data written to or read from a tape is referred to as a block.

+$#A Tape Initialization

An application must use the CreateFile function to create a handle of a tape device. This handle is used in subsequent operations on the tape in the device.

Before an application writes to a tape, the tape must be formatted according to the needs of the application and the capabilities of the tape drive being used. The CreateTapePartition function reformats a tape, creating on it a given number of partitions of a specified size.

The PrepareTape function prepares a tape to be accessed or removed. This function can load, unload, lock, or unlock a tape. This function can also tension the tape by moving the tape to the end of the tape and back to the beginning.

To retrieve and set information about a tape and tape drive, an application uses the GetTapeParameters, SetTapeParameters, and GetTapeStatus functions.

GetTapeParameters retrieves information that describes a tape or a tape drive. The tape information includes the tape's type, density, and block size; the number of partitions on the tape; the amount of tape remaining; and so on. The tape drive information includes the drive's default block size, the maximum partition count, and the features that are supported.

SetTapeParameters either sets the tape block size or sets the tape drive flags that indicate whether the drive supports hardware error correction, data compression, data padding, or any combination of the three.

GetTapeStatus indicates whether the tape drive is ready to process tape commands.

+$#A Tape Input and Output

The Win32 API provides several functions that applications can use to perform input and output (I/O) on a tape drive. Tape I/O is similar to I/O performed on a communications device.

When performing tape I/O, some tape drives store tape firmware information in the first few blocks on a tape, typically using some portion of the first 100 blocks. Applications should not use those blocks. More specific information on this subject is available from individual tape system manufacturers. In general, an application that skips the first 100 blocks on a tape will avoid tape drive idiosyncracies.

The GetTapePosition and SetTapePosition functions retrieve and move the current tape position. The WriteTapemark function writes a specified number of setmarks, filemarks, short filemarks, and long filemarks. The EraseTape function erases all or part of a tape.

The ReadFile and WriteFile functions read and write file data from and to the tape. The data is read and written in complete blocks. If the tape's block size is 512 bytes, all read and write operations must use buffers that are simple integer multiples of that block size: 512, 1024, 1536, 2048, and so on. Most, if not all, drives only allow a write operation after the tape is rewound or after a read operation produces an end-of-data error message.

If ReadFile encounters a filemark, the data up to the filemark is read and the function fails. (The GetLastError function returns an error code indicating the type of filemark that was encountered.) To continue reading beyond the filemark, an application must move the tape past the filemark and call ReadFile again.

ReadFile and WriteFile read and write only the data stream. The BackupRead and BackupWrite functions read and write all the streams associated with a file. These include data, extended attributes, security, and alternative data streams. The security and alternate data streams are relevant only on the new technology file system (NTFS) partition.

The BackupSeek function seeks forward in a file initially accessed by BackupRead or BackupWrite. This function enables an application to skip information that causes access errors.

If an application needs to access only the file data, it should use ReadFile and WriteFile. These functions can also read alternative data streams if the streams were created by using the CreateFile function.

A tape-backup application must use BackupRead and BackupWrite to copy all information pertaining to a file. However, these functions do not read or write file characteristics such as attributes, file creation time, and so on. Applications must use the file input and output functions, such as GetFileAttributes and SetFileAttributes, to retrieve and set those values.

+$#A Using Tape Backup

• Creating a backup application

• Backing up and restoring Posix file links

+$#A Creating a Backup Application

To perform any input or output on a tape, a backup application must first obtain a handle of the tape device. The following code sample shows how to use the CreateFile function to open a tape device.

HANDLE hTape; /* handle to tape device */

hTape = CreateFile(

"\\\\.\\TAPE0", /* name of tape device to open */

GENERIC_READ | GENERIC_WRITE, /* read-write access */

0, /* not used */

0, /* not used */

OPEN_EXISTING, /* required for tape devices */

0, /* not used */

NULL); /* not used */

To back up a directory tree to a tape, an application must use the FindFirstFile and FindNextFile functions to traverse the directory tree. Each time a file is found, the application should get the file's attributes by using the GetFileAttributes function. If there are hard links, the application should determine the number of links and save the file's unique identifier in a table for future comparisons. The first time a file is found, the application should use CreateFile to open the file and the BackupRead function to begin the backup. Then it can use the WriteFile function repeatedly to transfer all the information in the buffer filled by BackupRead to the tape. The second time a file is found (checked against the table of file identifiers when there are hard links), the application can simply write the general file information to the tape, followed by a stream whose identifier is BACKUP_LINK.

When restoring files from tape to disk, an application must use the CreateFile, BackupWrite, and ReadFile functions. For each file on the tape, the application should use CreateFile to create a new file on disk and BackupWrite to begin restoring the file. Then it should use ReadFile repeatedly until all the information for the file has been read from the tape into the buffer filled by BackupWrite. If one of the streams in the BackupWrite buffer has a BACKUP_LINK stream identifier, the application must establish a hard link. If the data needed to establish the link does not exist, BackupWrite fails. The application can use a preexisting catalog to locate and restore the original data, or it can notify the user that the file data to be restored is in a different location.

+$#A Backing Up and Restoring Posix File Links

A backup application can use the BackupWrite function to recreate Posix file links.

The BackupRead function does not return Posix file link data. A backup application must maintain the link information itself. The following pseudocode presents algorithms for backing up and restoring Posix file link data along with other file information.

PseudoCode Algorithm for Backing Up Posix File Links

1. Initialize and empty a list of known links.

2. While there are more files to back up

3. Read the disk and get the next file.

4. Open the file for read.

5. Call GetFileInformationByHandle() to get the

NumberOfLinks and the FileIndex.

6. If the NumberOfLinks is greater than 1

7. Search the list of know links looking for

the same FileIndex.

8. If a match is NOT found

9. add the full path of the file and the

FileIndex to the list.

10. Call BackupRead() to copy all data to

your backup media.

10. Else

11. Mark the data as a LINK on your backup media

11. store the full path from the list

to your backup media.

12. Endif

13. Else

14. Call BackupRead() to copy all data to your

backup media.

15. Endif

16. EndWhile

PseudoCode Algorithm for Restoring Posix File Links

1. While there are more files to restore

2. If the file is a LINK

3. use the full path which was saved as data

to open the file.

4. Initialize a WIN32_STREAM_ID structure with

dwStreamId equal to BACKUP_LINK.

5. Initialize the dwStreamAttributes to 0.

6. Initialize the dwStreamNameSize to 0.

7. Initialize a buffer containing the full path

of the file you are restoring in UNICODE.

8. Initialize the dwStreamSizeHigh to 0.

9. Initialize the dwStreamSizeLow to the size

in bytes of the buffer containing the full path.

10. Call BackupWrite() with the WIN32_STREAM_ID

11. Call BackupWrite() with the buffer containing

the full path.

13. Else

14. Call BackupWrite() with the data stored on

your backup media.

15. Endif

16. EndWhile

+$#A Tape Backup Functions

Following are the functions used for tape backup.

BackupRead
BackupSeek
BackupWrite
CreateTapePartition
EraseTape
GetTapeParameters
GetTapePosition
GetTapeStatus
PrepareTape
SetTapeParameters
SetTapePosition
WriteTapemark

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide CHM facilement


Time.rtf

+$#AK Time

The Microsoft® Win32® application programming interface (API) provides a variety of date and time functions, structures, and messages to retrieve and set the date and time for the system, files, and the local time zone.

+$#A About Time

This overview describes various functions, structures, and messages and explains how to use them to examine and modify dates and times.

For information about related topics, see the following overviews:

• Files

• File Systems

+$#A System Time

System time is the current date and time of day. Windows keeps system time so that your applications have ready access to accurate time. When Windows first starts, it sets the system time to a value based on the real-time clock of the computer and thereafter regularly updates the time.

Windows bases system time on coordinated universal time (UTC). UTC-based time is loosely defined as the current date and time of day in Greenwich, England. You can retrieve the system time by using the GetSystemTime function. GetSystemTime copies the time to a SYSTEMTIME structure that contains individual members for month, day, year, weekday, hour, minute, second, and milliseconds.

Because system time is UTC-based, the time copied to the SYSTEMTIME structure may not be the same as the local time⎯the date and time of day for your time zone. You can retrieve the local time by using the GetLocalTime function. GetLocalTime converts the system time to a local time based on the current time-zone settings and copies the result to a SYSTEMTIME structure.

The current time-zone settings control how Windows converts between UTC and local time. You can retrieve the current time-zone settings by using the GetTimeZoneInformation function. The function copies the setting to a TIME_ZONE_INFORMATION structure and returns a value indicating whether local time is currently in standard or daylight-savings time. You can set the time-zone settings by using the SetTimeZoneInformation function.

You can set the system time by using the SetSystemTime or SetLocalTime function. SetSystemTime assumes you have specified a UTC-based time; SetLocalTime assumes local time and converts to UTC before setting the system time. If you change the system time, you should send the WM_TIMECHANGE message to all top-level windows by using the SendMessage function.

+$#A Windows Time

Windows time is the number of milliseconds elapsed since Windows started running. To ensure that applications designed for earlier versions of Windows continue to run successfully, the GetTickCount and GetCurrentTime functions return the current Windows time. However, if your application requires the current date and time of day, you should use the GetSystemTime or GetLocalTime function whenever possible.

Because GetTickCount and GetCurrentTime are identical in operation, you should use GetTickCount to retrieve Windows time.You typically use GetTickCount to compare the current Windows time with the time returned by the GetMessageTime function. GetMessageTime returns the Windows time when the given message was created.

Windows time is stored as a 32-bit value, which means Windows can record no more than 2^32 millisecond intervals before the 32-bit value overflows to zero. This is approximately 49.7 days. If you use Windows time, check for the overflow condition when comparing times.

+$#A File Times

A file time represents the specific date and time at which a given file was created, last accessed, or last written to. Windows records file times whenever applications create, access, and write to files. Windows records the times using a 64-bit value specifying the number of 100-nanosecond intervals that have elapsed since 12:00 A.M. January 1, 1601 (UTC). Writing to a file changes the last write time; writing to or reading from the file (including running an executable file) changes the last access time.

Note Not all file systems can record creation and last access time. In particular, the file allocation table (FAT) file system records only last-write times.

You can retrieve the file times for a given file by using the GetFileTime function. GetFileTime copies the creation, last access, and last write times to individual FILETIME structures. Each structure consists of two 32-bit values that combine to form the single 64-bit value. You can also retrieve the file times by using the FindFirstFile and FindNextFile functions. These functions copy the times to FILETIME structures in a WIN32_FIND_DATA structure.

You can set the file times for a file by using the SetFileTime function. This function lets you modify creation, last access, and last write times without changing the content of the file. You can compare the times of different files by using the CompareFileTime function. The function compares two file times and returns a value indicating which time is greater or returns zero if the times are equal.

To make a file time more meaningful to a user, you can extract the month, day, year, and time of day from a file time by using the FileTimeToSystemTime function. FileTimeToSystemTime converts the file time and copies the individual values for date and time of day to a SYSTEMTIME structure.

Windows records all file times in UTC-based times, but you can convert a file time to the local time for your time zone by using the FileTimeToLocalFileTime function. Before displaying a file time to a user, applications typically convert the file time to local time, then extract the month, day, year and time of day using FileTimeToSystemTime.

If you plan to modify file times for given files, you can convert a date and time of day to a file time by using the SystemTimeToFileTime function. If the original date and time of day are given in the local time for your time zone, you can convert the resulting file time to UTC by using the LocalFileTimeToFileTime function. Always make sure the file times you set using SetFileTime are valid UTC-based times.

+$#A MS-DOS Date and Time

MS-DOS date and MS-DOS time are packed 16-bit values that specify the month, day, year, and time of day an MS-DOS file was last written to. MS-DOS records the date and time whenever an MS-DOS application creates or writes to a file. MS-DOS applications retrieve this date and time using MS-DOS functions. When you use the GetFileTime function to retrieve the file times for files that were created by MS-DOS, GetFileTime automatically converts MS-DOS dates and times to UTC-based times.

If you encounter an MS-DOS date and time that has not been converted, you can convert it to a UTC-based time by using the DosDateTimeToFileTime function. This function copies the converted date and time to a FILETIME structure. You can convert the value back to an MS-DOS date and time by using the FileTimeToDosDateTime function.

+$#A Using Time

• Retrieving the last-write time

• Changing a file time to the current time

• Setting the system time

+$#A Retrieving the Last-Write Time

The following example retrieves the last-write time for a file, converts it to local time based on the current time-zone settings, and creates a date and time string that can be shown to the user.

// GetLastWriteTime - retrieves the last-write time and converts the
// time to a string
// Return value - TRUE if successful, FALSE otherwise
// hFile - must be a valid file handle
// lpszString - address of buffer to receive string

BOOL GetLastWriteTime(HANDLE hFile, LPSTR lpszString)
{
FILETIME ftCreate, ftAccess, ftWrite, ftLocal;
SYSTEMTIME stCreate;

// Retrieve the file times for the file.
if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
return FALSE;

// Convert the last-write time to local time.
if (!FileTimeToLocalFileTime(&ftWrite, &ftLocal))
return FALSE;

// Convert the local file time from UTC to system time.
FileTimeToSystemTime(&ftLocal, &stCreate);

// Build a string showing the date and time.
wsprintf(lpszString, "%02d/%02d/%d %02d:%02d",
stCreate.wDay, stCreate.wMonth, stCreate.wYear,
stCreate.wHour, stCreate.wMinute);

return TRUE;
}

+$#A Changing a File Time to the Current Time

The following example sets the last-write time for a file to the current system time.

// SetFileToCurrentTime - sets last write time to current system time
// Return value - TRUE if successful, FALSE otherwise
// hFile - must be a valid file handle

BOOL SetFileToCurrentTime(HANDLE hFile)
{
FILETIME ft;
SYSTEMTIME st;
BOOL f;

GetSystemTime(&st); // gets current time
SystemTimeToFileTime(&st, &ft); // converts to file time format
f = SetFileTime(hf, // sets last-write time for file
(LPFILETIME) NULL, (LPFILETIME) NULL, &ft);

return f;
}

+$#A Setting the System Time

The following example sets the system time.

// SetNewTime - sets system time
// Return value - TRUE if successful, FALSE otherwise
// hour - new hour (0-23)
// minutes - new minutes (0-59)

BOOL SetNewTime(WORD hour, WORD minutes)
{
SYSTEMTIME st;
char *pc;

GetSystemTime(&st); // gets current time
st.wHour = hour; // adjusts hours
st.wMinute = minutes; // and minutes
if (!SetSystemTime(&st)) // sets system time
return FALSE;
return TRUE;
}

+$#A Time Functions

Following are the functions used with time formats.

CompareFileTime
DosDateTimeToFileTime
FileTimeToDosDateTime
FileTimeToLocalFileTime
FileTimeToSystemTime
GetCurrentTime
GetFileTime
GetLocalTime
GetSystemTime
GetSystemTimeAdjustment
GetSystemTimeAsFileTime
GetTickCount
GetTimeZoneInformation
LocalFileTimeToFileTime
SetFileTime
SetLocalTime
SetSystemTime
SetSystemTimeAdjustment
SetTimeZoneInformation
SystemTimeToFileTime
SystemTimeToTZSpecificLocalTime


Timers.rtf

+$#AK Timers

A timer is an internal routine that repeatedly measures a specified interval, in milliseconds. Each time the interval (or time-out value) elapses, the system notifies the window associated with the timer. Because the accuracy of a timer depends on the system clock rate and how often the application retrieves messages from the message queue, the time-out value is only approximate.

+$#A About Timers

An application written for Microsoft® Windows® creates a timer by using the SetTimer function. If a window handle is given in this function, the application creates the timer for that window. Then, whenever the time-out value elapses, the system posts a WM_TIMER message to the window. If no window handle is given in SetTimer, the application that created the timer must monitor its message queue for WM_TIMER messages and dispatch them to the appropriate window.

A new timer starts timing as soon as it is created. An application can change a timer's time-out value by using SetTimer and can destroy a timer by using the KillTimer function. To use system resources efficiently, applications should destroy timers that are no longer necessary.

Each timer has a unique identifier. When creating a timer, an application can either specify an identifier or have the system create a unique value. The first parameter of a WM_TIMER message contains the identifier of the timer that posted the message.

+$#A Using Timers

• Creating a timer

• Destroying a timer

• Using timer functions to create a mousetrap

• Process the WM_TIMER message.

• Use a TimerProc callback function.

+$#A Creating a Timer

The following example uses the SetTimer function to create two timers. The first timer is set for every 10 seconds, the second for every 5 minutes.

/* Set two timers. */

SetTimer(hwnd, /* handle of main window */

IDT_TIMER1, /* timer identifier */

10000, /* 10-second interval */

(TIMERPROC) NULL); /* no timer callback */

SetTimer(hwnd, /* handle of main window */

IDT_TIMER2, /* timer identifier */

300000, /* 5-minute interval */

(TIMERPROC) NULL); /* no timer callback */

To process the WM_TIMER messages generated by these timers, add a WM_TIMER case statement to the window procedure for the hwnd parameter.

case WM_TIMER:

switch (wParam) {

case IDT_TIMER1:

.

. /* Process the 10-second timer. */

.

return 0;

case IDT_TIMER2:

.

. /* Process the 5-minute timer. */

.

return 0;

}

An application can also create a timer whose WM_TIMER messages are processed not by the main window procedure but by an application-defined callback function, as in the following code sample, which creates a timer and uses the callback function MyTimerProc to process the timer's WM_TIMER messages.

/* Set the timer. */

SetTimer(hwnd, /* handle of main window */

IDT_TIMER3, /* timer identifier */

5000, /* 5-second interval */

(TIMERPROC) MyTimerProc); /* timer callback */

The calling convention for MyTimerProc must be based on the Microsoft® Win32® callback function, TimerProc.

If your application creates a timer without specifying a window handle, your application must monitor the message queue for WM_TIMER messages and dispatch them to the appropriate window.

HWND hwndTimer; /* handle of window for timer messages */

MSG msg; /* message structure */

while (GetMessage(&msg, /* message structure */

NULL, /* handle of window receiving msg. */

NULL, /* lowest message to examine */

NULL)) /* highest message to examine */

{

/* Post WM_TIMER messages to the hwndTimer procedure. */

if (msg.message == WM_TIMER) {

msg.hwnd = hwndTimer;

}

TranslateMessage(&msg); /* translates virtual-key codes */

DispatchMessage(&msg); /* dispatches message to window */

}

+$#A Destroying a Timer

Applications should use the KillTimer function to destroy timers that are no longer necessary. The following example destroys the timers identified by the constants IDT_TIMER1, IDT_TIMER2, and IDT_TIMER3.

/* Destroy the timers. */

KillTimer(hwnd, IDT_TIMER1);

KillTimer(hwnd, IDT_TIMER2);

KillTimer(hwnd, IDT_TIMER3);

+$#A Using Timer Functions to Create a Mousetrap

Sometimes it is necessary to prevent more input while you have a cursor on the screen. One way to accomplish this is to create a special routine that traps mouse input until a specific event occurs. Many developers refer to this routine as "building a mousetrap."

The following example uses the SetTimer and KillTimer functions to create a simple mousetrap. SetTimer creates a timer that sends a WM_TIMER message every 10 seconds. Each time the application receives a WM_TIMER message, it records the cursor location. If the current location is the same as the previous location and the application's main window is minimized, the application moves the cursor to the icon. When the application closes, KillTimer stops the timer.

HICON hIcon1; /* icon handle */

POINT ptOld; /* previous cursor location */

UINT uResult; /* SetTimer's return value */

HINSTANCE hinstance; /* handle of current instance */

.

. /* Perform application initialization here. */

.

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400));

wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200));

/* Record the initial cursor position. */

GetCursorPos(&ptOld);

/* Set the timer for the mousetrap. */

uResult = SetTimer(hwnd, /* handle of main window */

IDT_MOUSETRAP, /* timer identifier */

10000, /* 10-second interval */

(TIMERPROC) NULL); /* no timer callback */

if (uResult == 0) {

ErrorHandler("No timer is available.");

}

LONG APIENTRY MainWndProc(

HWND hwnd, /* handle of main window */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

HDC hdc; /* handle of device context */

POINT pt; /* current cursor location */

RECT rc; /* location of minimized window */

switch (message) {

.

. /* Process other messages. */

.

case WM_TIMER:

/*

* If the window is minimized, compare the current

* cursor position with the one from 10 seconds

* earlier. If the cursor position has not changed,

* move the cursor to the icon.

*/

if (IsIconic(hwnd)) {

GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {

GetWindowRect(hwnd, &rc);

SetCursorPos(rc.left, rc.top);

}

else {

ptOld.x = pt.x;

ptOld.y = pt.y;

}

}

return 0;

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_MOUSETRAP);

PostQuitMessage(0);

break;

.

. /* Process other messages. */

.

}

Although the following example also creates a mousetrap, it processes the WM_TIMER message through the application-defined callback function MyTimerProc, rather than through the application's message queue.

UINT uResult; /* SetTimer's return value */

HICON hIcon1; /* icon handle */

POINT ptOld; /* previous cursor location */

HINSTANCE hinstance; /* handle of current instance */

.

. /* Perform application initialization here. */

.

wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400));

wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200));

/* Record the current cursor position. */

GetCursorPos(&ptOld);

/* Set the timer for the mousetrap. */

uResult = SetTimer(hwnd, /* handle of main window */

IDT_MOUSETRAP, /* timer identifier */

10000, /* 10-second interval */

(TIMERPROC) MyTimerProc); /* timer callback */

if (uResult == 0) {

ErrorHandler("No timer is available.");

}

LONG APIENTRY MainWndProc(

HWND hwnd, /* handle of main window */

UINT message, /* type of message */

UINT wParam, /* additional information */

LONG lParam) /* additional information */

{

HDC hdc; /* handle of device context */

switch (message) {

.

. /* Process other messages. */

.

case WM_DESTROY:

/* Destroy the timer. */

KillTimer(hwnd, IDT_MOUSETRAP);

PostQuitMessage(0);

break;

.

. /* Process other messages. */

.

}

/*

* MyTimerProc is an application-defined callback function that

* processes WM_TIMER messages.

*/

VOID CALLBACK MyTimerProc(

HWND hwnd, /* handle of window for timer messages */

UINT message, /* WM_TIMER message */

UINT idTimer, /* timer identifier */

DWORD dwTime) /* current system time */

{

RECT rc;

POINT pt;

/*

* If the window is minimized, compare the current

* cursor position with the one from 10 seconds earlier.

* If the cursor position has not changed, move the

* cursor to the icon.

*/

if (IsIconic(hwnd)) {

GetCursorPos(&pt);

if ((pt.x == ptOld.x) && (pt.y == ptOld.y)) {

GetWindowRect(hwnd, &rc);

SetCursorPos(rc.left, rc.top);

}

else {

ptOld.x = pt.x;

ptOld.y = pt.y;

}

}

}

+$#A Timer Functions

The following functions are used with timers.

KillTimer
SetTimer
TimerProc

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide HTML facilement


Toolbars.rtf

+$#AK Toolbars

A toolbar is a control window that contains one or more buttons. Each button sends a command message to the parent window when the user chooses it.

+$#A About Toolbars

Typically, the buttons in a toolbar correspond to items in the application's menu, providing an additional and more direct way for the user to access an application's commands. The following illustration shows a window that has a toolbar positioned below the menu bar.

{ewc msdncd, EWGraphic, group10606 0 /a "SDK_01.bmp"}

A toolbar has built-in customization features, including a system-defined customization dialog box, that allow the user to insert, delete, or rearrange toolbar buttons. An application determines whether the customization features are available to the user and controls the extent to which the user may customize the toolbar.

+$#A Toolbar Creation

You can use the CreateToolbarEx function to create a toolbar and add an initial set of buttons to it. You can also use the CreateWindowEx function, specifying the TOOLBARCLASSNAME window class, but this second method creates a toolbar that initially contains no buttons. You add buttons to the toolbar by using the TB_ADDBUTTONS or TB_INSERTBUTTON message.

The TOOLBARCLASSNAME window class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, use the InitCommonControls function first.

A toolbar must be created as a child window with the WS_CHILD style. If you use CreateWindowEx to create a toolbar, you must specify the WS_CHILD window style. CreateToolbarEx includes the WS_CHILD style by default. You must specify the initial parent window when creating the toolbar, but you can change the parent window after creation by using the TB_SETPARENT message.

+$#A Toolbar Size and Position

The window procedure for a toolbar automatically sets the size and position of the toolbar window. The height is based on the height of the buttons in the toolbar. The width is the same as the width of the parent window's client area. The CCS_TOP and CCS_BOTTOM common control styles determine whether the toolbar is positioned along the top or bottom of the client area. By default, a toolbar has the CCS_TOP style.

The toolbar window procedure automatically adjusts the size of the toolbar whenever it receives a WM_SIZE or TB_AUTOSIZE message. An application should send either of these messages whenever the size of the parent window changes or after sending a message that requires the size of the toolbar to be adjusted ⎯ for example, the TB_SETBUTTONSIZE message.

+$#A Tooltip Style

There is only one class-specific style associated with toolbars: TBSTYLE_TOOLTIPS. When you specify this style, the toolbar creates and manages a tooltip control. A tooltip control is a small pop-up window that contains a line of text describing a toolbar button. The tooltip control is hidden and appears only when the user puts the cursor on a toolbar button, leaving it there for approximately one second. The tooltip control is displayed near the cursor.

When the toolbar receives a WM_MOUSEMOVE message, it sends a notification message to the tooltip control. The tooltip control sets a timer, and after approximately one second, checks if the cursor is still at the same location and is on a toolbar button. If it is, the tooltip control sends the TTN_NEEDTEXT notification message to the parent window to retrieve the descriptive text for the button. Then the tooltip control creates a pop-up window and displays the text in the window. The tooltip control destroys the pop-up window when the user clicks a mouse button or moves the cursor out of the toolbar.

An application that needs to send messages directly to the tooltip control can retrieve the handle of the tooltip by using the TB_GETTOOLTIPS message. An application can replace the tooltip control of a toolbar by using the TB_SETTOOLTIPS message. For more information about tooltip controls, see Tooltip Controls.

+$#A Toolbar Bitmaps

Each button in a toolbar can include a bitmapped image. A toolbar stores the information that it needs to draw the images in an internal list. When you call the CreateToolbarEx function, you specify a monochrome or color bitmap that contains the initial images, and the toolbar adds the information to the internal list of images. You can add additional images later by using the TB_ADDBITMAP message.

Each image has a zero-based index. The first image added to the internal list has an index of zero, the second image has an index of one, and so on. TB_ADDBITMAP adds images to the end of the list and returns the index of the first new image that it added. You use an image's index to associate the image with a button.

Windows assumes that all of a toolbar's bitmapped images are the same size. You specify the size when you create the toolbar by using CreateToolbarEx. If you use the CreateWindowEx function to create a toolbar, the size of the images is set to the default dimensions of 16 by 15 pixels. You can use the TB_SETBITMAPSIZE message to change the dimensions of the bitmapped images, but you must do so before adding any images to the internal list of images.

+$#A Toolbar Strings

Each button can display a string in addition to, or instead of, an image. A toolbar maintains an internal list that contains all of the strings available to toolbar buttons. You add strings to the internal list by using the TB_ADDSTRING message, specifying the address of the buffer containing the strings to add. Each string must be null-terminated, and the last string must be terminated with two null characters.

Each string has a zero-based index. The first string added to the internal list of strings has an index of zero, the second string has an index of one, and so on. TB_ADDSTRING adds strings to the end of the list and returns the index of the first new string. You use a string's index to associate the string with a button.

+$#A Toolbar Buttons

You add buttons to a toolbar by filling an array of TBBUTTON structures and specifying the address of the array either in the TB_ADDBUTTONS message or in a call to the CreateToolbarEx function. You can also use the TB_INSERTBUTTON message, which passes the address of a TBBUTTON structure to the toolbar. Each TBBUTTON structure defines the attributes of a button, including the indexes of its string and bitmap as well as its style, state, command identifier, and application-defined 32-bit value.

If you use the CreateWindowEx function to create a toolbar, you must send the TB_BUTTONSTRUCTSIZE message before adding any buttons. The message passes the size of the TBBUTTON structure to the toolbar.

+$#A Toolbar Button Styles

A button's style determines how the button appears and how it responds to user input. The TBSTYLE_BUTTON style creates a toolbar button that behaves like a standard push button. A button that has the TBSTYLE_CHECK style is similar to a standard push button, except it toggles between the pressed and nonpressed states each time the user clicks it.

You can create groups of toolbar buttons by using the TBSTYLE_GROUP or TBSTYLE_CHECKGROUP style, causing a button to stay pressed until the user chooses another button in the group. The TBSTYLE_SEP style creates a small gap between buttons. A button with this style does not receive user input.

+$#A Toolbar Button States

Each button in a toolbar has a current state. The toolbar updates a button's state to reflect user actions, such as clicking the button. The state indicates whether the button is currently pressed or not pressed, enabled or disabled, hidden or visible, and so on. Although an application sets a button's initial state when adding the button to the toolbar, it can change and retrieve the state by sending messages to the toolbar. For a list of toolbar button states, see Toolbar States.

An application can use the TB_GETSTATE and TB_SETSTATE messages to retrieve and set the state of a button. In addition, a toolbar includes the following messages that retrieve or set a particular state.

TB_CHECKBUTTON | TB_ISBUTTONCHECKED
TB_ENABLEBUTTON | TB_ISBUTTONENABLED
TB_HIDEBUTTON | TB_ISBUTTONHIDDEN
TB_INDETERMINATE | TB_ISBUTTONINDETERMINATE
TB_PRESSBUTTON | TB_ISBUTTONPRESSED

+$#A Command Identifier

Each button has a command identifier associated with it. When the user selects a button, the toolbar sends the parent window a WM_COMMAND message that includes the command identifier of the button. The parent window examines the command identifier and carries out the command associated with the button.

+$#A Button Size and Position

A toolbar keeps track of its buttons by assigning each button a position index. The index is zero based; that is, the leftmost button has an index of zero, the next button to the right has an index of one, and so on. An application must specify the index of a button when sending messages to retrieve information about the button or set the button's attributes.

A toolbar updates the position indexes as buttons are inserted and removed. An application can retrieve the current position index of a button by using the TB_COMMANDTOINDEX message. The message specifies the command identifier of a button, and the toolbar window uses the identifier to locate the button and return its position index.

All buttons in a toolbar are the same size. The CreateToolbarEx function requires you to set the initial size of the buttons when you create the toolbar. When you use the CreateWindowEx function to create a toolbar, the initial size is set to the default dimensions of 24 by 22 pixels. You can use the TB_SETBUTTONSIZE message to change the button size, but you must do so before adding any buttons to the toolbar. The TB_GETITEMRECT message retrieves the current dimensions of the buttons.

A toolbar automatically sets the width of its buttons when you add a string to the toolbar that is longer than any current toolbar string. The width is set to accommodate the longest string in the toolbar.

+$#A Customization

A toolbar has built in customization features that you can make available to the user by giving the toolbar the CCS_ADJUSTABLE common control style. The customization features allow the user to drag a button to a new position or to remove a button by dragging it off the toolbar. In addition, the user can double-click the toolbar to display the Customize Toolbar dialog box, which allows the user to add, delete, and rearrange toolbar buttons. An application can display the dialog box by using the TB_CUSTOMIZE message.

The toolbar sends notification messages (in the form of WM_NOTIFY messages) to the parent window at each step in the customization process. If the user holds the SHIFT key down and begins dragging a button, the toolbar automatically handles the drag operation. The toolbar sends the TBN_QUERYDELETE notification message to the parent window to determine whether the button may be deleted. The drag operation ends if the parent window returns FALSE. Otherwise, the toolbar captures mouse input and waits for the user to release the mouse button.

When the user releases the mouse button, the toolbar determines the location of the mouse cursor. If the cursor is outside the toolbar, the button is deleted. If the cursor is on another toolbar button, the toolbar sends the TBN_QUERYINSERT notification message to the parent window to determine if a button may be inserted to the left of the given button. The button is inserted if the parent window returns TRUE; otherwise, it is not. The toolbar sends the TBN_TOOLBARCHANGE notification message to signal the end of the drag operation.

If the user begins a drag operation without holding down the SHIFT key, the toolbar sends the TBN_BEGINDRAG notification message to the parent window. An application that implements its own button-dragging code can use this message as a signal to begin a drag operation. The toolbar sends the TBN_ENDDRAG notification message to signal the end of the drag operation.

A toolbar sends notification messages when the user customizes a toolbar by using the Customize Toolbar dialog box. The toolbar sends the TBN_BEGINADJUST notification message after the user double-clicks the toolbar, but before the dialog box is created. Next, the toolbar begins sending a series of TBN_QUERYINSERT notification messages to determine whether the toolbar allows buttons to be inserted. When the parent window returns TRUE, the toolbar stops sending TBN_QUERYINSERT notifications. If the parent window does not return TRUE for any button, the toolbar destroys the dialog box.

Next, the toolbar determines if any buttons may be deleted by sending one TBN_QUERYDELETE notification message for each button in the toolbar. The parent window returns TRUE to indicate that a button may be deleted; otherwise, it returns FALSE. The toolbar adds all toolbar buttons to the dialog box, but it grays those that may not be deleted.

Whenever the toolbar needs information about a button in the Customize Toolbar dialog box, it sends the TBN_GETBUTTONINFO notification message, specifying the index of the button for which it needs information and the address of a TBNOTIFY structure. The parent window must fill the structure with the relevant information.

The Customize Toolbar dialog box includes a Help button and a Reset button. When the user chooses the Help button, the toolbar sends the TBN_CUSTHELP notification message. The parent window should respond by displaying help information. The dialog box sends the TBN_RESET notification message when the user selects the Reset button. This message signals that the toolbar is about to reinitialize the dialog box.

An application can use the TB_SAVERESTORE message to save the current state of a toolbar in an initialization file or to restore the state based on information previously stored in an initialization file. The message saves the state information in the specified initialization file or in the WIN.INI file if no initialization file is specified. An application typically stores the state before the user begins customizing the toolbar in case the user later wants to restore the toolbar to its original state.

+$#A Default Message Handling

This section describes the window message processing performed by a toolbar. Because messages specific to toolbars are discussed elsewhere, they are not included here.

Message | Default processing
WM_CHARTOITEM | Forwards this message to the parent window.
WM_COMMAND | Forwards this message to the parent window.
WM_CREATE | Allocates internal memory and graphics objects. It also initializes internal variables and data structures.
WM_DESTROY | Frees all memory and graphics objects previously allocated for the toolbar.
WM_DRAWITEM | Forwards this message to the parent window.
WM_LBUTTONDBLCLK | Forwards this message to the DefWindowProc function and returns the result.
WM_LBUTTONDOWN | Forwards this message to the tooltip control. If a button was clicked, it sets the state of the button and redraws it. It begins a drag operation if the toolbar has the CCS_ADJUSTABLE style and the SHIFT key is down (or the ALT key, if the toolbar has the TBSTYLE_ALTDRAG style).
WM_LBUTTONUP | Forwards this message to the tooltip control. If a button was clicked, it sets the state of the button and redraws it. It also ends the drag operation, if one is underway, and redraws the toolbar.
WM_MEASUREITEM | Forwards this message to the parent window.
WM_MOUSEMOVE | Forwards this message to the tooltip control. If a button has the mouse capture and the cursor moves out of the button rectangle, it redraws the button. It checks whether the toolbar has lost the mouse capture and, if it has, ends the drag operation.
WM_NCACTIVATE, WM_NCPAINT | Redraws the borders of the toolbar if the toolbar does not have the CCS_NODIVIDER style. Otherwise, it forwards the message to the DefWindowProc function.
WM_NOTIFY | Forwards the TTN_NEEDTEXT notification message from the tooltip control to the parent of the toolbar.
WM_PAINT | Draws the toolbar.
WM_SIZE | Recalculates the size and position of the toolbar and redraws it.
WM_SYSCOLORCHANGE | Updates the colors of the buttons and toolbar to march the system colors.
WM_VKEYTOITEM | Forwards this message to the parent window.
WM_WININICHANGE | Reinitializes internal variables that depend on system metric values.

+$#A Using Toolbars

• Creating a toolbar

• Processing tooltip notification messages

+$#A Creating a Toolbar

The following example uses the CreateWindowEx function to create a toolbar that the user can customize and that has a tooltip control associated with it. The example uses the TB_ADDBITMAP and TB_ADDSTRING messages to add button images and buttons strings to the toolbar. The example also adds three buttons by using the TB_ADDBUTTONS message.

// CreateAToolBar - creates a toolbar and adds the initial set of

// buttons to it.

// Returns the handle of the toolbar if successful or NULL otherwise.

// hwndParent - handle of the parent window

HWND CreateAToolBar(HWND hwndParent)

{

HWND hwndTB;

TBADDBITMAP tbab;

TBBUTTON tbb[3];

char szBuf[16];

int iCut, iCopy, iPaste;

// Ensure that the common control DLL is loaded.

InitCommonControls();

// Create a toolbar that the user can customize and that has a

// tooltip associated with it.

hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,

WS_CHILD | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE,

0, 0, 0, 0, hwndParent, (HMENU) ID_TOOLBAR, g_hinst, NULL);

// Send the TB_BUTTONSTRUCTSIZE message, which is required for

// backward compatibility.

SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE,

(WPARAM) sizeof(TBBUTTON), 0);

// Add the bitmap containing button images to the toolbar.

tbab.hInst = g_hinst;

tbab.nID = IDB_BUTTONS;

SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) NUM_BUTTON_BITMAPS,

(WPARAM) &tbab);

// Add the button strings to the toolbar.

LoadString(g_hinst, IDS_CUT, (LPSTR) &szBuf, MAX_LEN);

iCut = SendMessage(hwndTB, TB_ADDSTRING, 0, (LPARAM) (LPSTR) szBuf);

LoadString(g_hinst, IDS_COPY, (LPSTR) &szBuf, MAX_LEN);

iCopy = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,

(LPARAM) (LPSTR) szBuf);

LoadString(g_hinst, IDS_PASTE, (LPSTR) &szBuf, MAX_LEN);

iPaste = SendMessage(hwndTB, TB_ADDSTRING, (WPARAM) 0,

(LPARAM) (LPSTR) szBuf);

// Fill the TBBUTTON array with button information, and add the

// buttons to the toolbar.

tbb[0].iBitmap = BMP_CUT;

tbb[0].idCommand = IDM_CUT;

tbb[0].fsState = TBSTATE_ENABLED;

tbb[0].fsStyle = TBSTYLE_BUTTON;

tbb[0].dwData = 0;

tbb[0].iString = iCut;

tbb[1].iBitmap = BMP_COPY;

tbb[1].idCommand = IDM_COPY;

tbb[1].fsState = TBSTATE_ENABLED;

tbb[1].fsStyle = TBSTYLE_BUTTON;

tbb[1].dwData = 0;

tbb[1].iString = iCopy;

tbb[2].iBitmap = BMP_PASTE;

tbb[2].idCommand = IDM_PASTE;

tbb[2].fsState = TBSTATE_ENABLED;

tbb[2].fsStyle = TBSTYLE_BUTTON;

tbb[2].dwData = 0;

tbb[2].iString = iPaste;

SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM) NUM_BUTTONS,

(LPARAM) (LPTBBUTTON) &tbb);

ShowWindow(hwndTB, SW_SHOW);

return hwndTB;

}

+$#A Processing Tooltip Notification Messages

A toolbar that has the TBSTYLE_TOOLTIPS style creates a tooltip control, which an application can use to display help text for toolbar buttons. The parent window receives the TTN_NEEDTEXT notification message when the toolbar needs the help text for a button. The tooltip sends the notification in the form of a WM_NOTIFY message. The lParam parameter includes the address of a TOOLTIPTEXT structure that specifies the command identifier of the button for which help text is needed. An application can copy the help text to the structure, specify the address of a string containing the help text, or specify the instance handle and resource identifier of a string resource.

The following example demonstrates how to process the TTN_NEEDTEXT notification.

case WM_NOTIFY:

switch (((LPNMHDR) lParam)->code) {

case TTN_NEEDTEXT:

{

LPTOOLTIPTEXT lpttt;

lpttt = (LPTOOLTIPTEXT) lParam;

lpttt->hinst = g_hinst;

// Specify the resource identifier of the descriptive

// text for the given button.

idButton = lpttt->hdr.idFrom;

switch (idButton) {

case IDM_CUT:

lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_CUT);

break;

case IDM_COPY:

lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_COPY);

break;

case IDM_PASTE:

lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_PASTE);

break;

}

break;

}

.

. // Process other notifications here.

.

default:

break;

}

+$#A Toolbar Reference

The functions, messages, notification messages, and structures are associated with toolbars. These elements can be grouped as follows.

Toolbar Creation

CreateToolbarEx
TB_BUTTONSTRUCTSIZE
TB_SETPARENT

Toolbar Size and Position

TB_AUTOSIZE
TB_SETBUTTONSIZE

Tooltip Style

TB_GETTOOLTIPS
TB_SETTOOLTIPS
TTN_NEEDTEXT

Toolbar Bitmaps

COLORMAP
CreateMappedBitmap
TB_ADDBITMAP
TB_CHANGEBITMAP
TB_GETBITMAP
TB_GETBITMAPFLAGS
TB_SETBITMAPSIZE
TBADDBITMAP

Toolbar Strings

TB_ADDSTRING
TB_GETBUTTONTEXT

Toolbar Buttons

TB_ADDBUTTONS
TB_BUTTONCOUNT
TB_COMMANDTOINDEX
TB_DELETEBUTTON
TB_GETBUTTON
TB_GETITEMRECT
TB_GETROWS
TB_INSERTBUTTON
TB_SETCMDID
TB_SETROWS
TBBUTTON
TBNOTIFY

Toolbar Button States

TB_CHECKBUTTON
TB_ENABLEBUTTON
TB_GETSTATE
TB_HIDEBUTTON
TB_INDETERMINATE
TB_ISBUTTONCHECKED
TB_ISBUTTONENABLED
TB_ISBUTTONHIDDEN
TB_ISBUTTONINDETERMINATE
TB_ISBUTTONPRESSED
TB_PRESSBUTTON
TB_SETSTATE

Toolbar Customization

TB_CUSTOMIZE
TB_SAVERESTORE
TBN_BEGINADJUST
TBN_BEGINDRAG
TBN_CUSTHELP
TBN_ENDADJUST
TBN_ENDDRAG
TBN_GETBUTTONINFO
TBN_QUERYDELETE
TBN_QUERYINSERT
TBN_RESET
TBN_TOOLBARCHANGE
TBSAVEPARAMS

+$#A Toolbar Functions

The following functions are used with toolbars.

CreateMappedBitmap
CreateToolbarEx

+$#A Toolbar Messages

The following messages are used with toolbars.

TB_ADDBITMAP
TB_ADDBUTTONS
TB_ADDSTRING
TB_AUTOSIZE
TB_BUTTONCOUNT
TB_BUTTONSTRUCTSIZE
TB_CHANGEBITMAP
TB_CHECKBUTTON
TB_COMMANDTOINDEX
TB_CUSTOMIZE
TB_DELETEBUTTON
TB_ENABLEBUTTON
TB_GETBITMAP
TB_GETBITMAPFLAGS
TB_GETBUTTON
TB_GETBUTTONTEXT
TB_GETITEMRECT
TB_GETROWS
TB_GETSTATE
TB_GETTOOLTIPS
TB_HIDEBUTTON
TB_INDETERMINATE
TB_INSERTBUTTON
TB_ISBUTTONCHECKED
TB_ISBUTTONENABLED
TB_ISBUTTONHIDDEN
TB_ISBUTTONINDETERMINATE
TB_ISBUTTONPRESSED
TB_PRESSBUTTON
TB_SAVERESTORE
TB_SETBITMAPSIZE
TB_SETBUTTONSIZE
TB_SETCMDID
TB_SETPARENT
TB_SETROWS
TB_SETSTATE
TB_SETTOOLTIPS

+$#A Toolbar Notification Messages

The parent window receives the following notification messages from a toolbar.

TBN_BEGINADJUST
TBN_BEGINDRAG
TBN_CUSTHELP
TBN_ENDADJUST
TBN_ENDDRAG
TBN_GETBUTTONINFO
TBN_QUERYDELETE
TBN_QUERYINSERT
TBN_RESET
TBN_TOOLBARCHANGE

+$#A Toolbar Structures

The following structures are used with toolbars.

COLORMAP
TBADDBITMAP
TBBUTTON
TBNOTIFY
TBSAVEPARAMS

+$#AK Toolbar Constants

The following style flags and state flags are used with toolbars.

+$#AK Toolbar Styles

A toolbar can have a combination of the following styles.

TBSTYLE_ALTDRAG | Allows the user to change the position of a toolbar button by dragging it while holding down the ALT key. If this style is not specified, the user must hold down the SHIFT key while dragging a button. Note that the CCS_ADJUSTABLE style must be specified to enable toolbar buttons to be dragged.
TBSTYLE_TOOLTIPS | Creates a tooltip control that an application can use to display descriptive text for the buttons in the toolbar.
TBSTYLE_WRAPABLE | Creates a toolbar that can have multiple lines of buttons. Toolbar buttons can "wrap" to the next line when the toolbar becomes too narrow to include all buttons on the same line. Wrapping occurs on separation and non-group boundaries.

A button in a toolbar can have a combination of the following styles.

TBSTYLE_BUTTON | Creates a standard push button.
TBSTYLE_CHECK | Creates a button that toggles between the pressed and not pressed states each time the user clicks it. The button has a different background color when it is in the pressed state.
TBSTYLE_CHECKGROUP | Creates a check button that stays pressed until another button in the group is pressed.
TBSTYLE_GROUP | Creates a button that stays pressed until another button in the group is pressed.
TBSTYLE_SEP | Creates a separator, providing a small gap between button groups. A button that has this style does not receive user input.

+$#A Toolbar States

A toolbar button can have a combination of the following states.

TBSTATE_CHECKED | The button has the TBSTYLE_CHECKED style and is being pressed.
TBSTATE_ENABLED | The button accepts user input. A button not having this state does not accept user input and is grayed.
TBSTATE_HIDDEN | The button is not visible and cannot receive user input.
TBSTATE_INDETERMINATE | The button is grayed.
TBSTATE_PRESSED | The button is being pressed.
TBSTATE_WRAP | A line break follows the button. The button must also have the TBSTATE_ENABLED state.

Créé avec HelpNDoc Personal Edition: Créer des documents d'aide CHM facilement


Tooltip Controls.rtf

+$#AK Tooltip Controls

A tooltip control is a small pop-up window that displays a single line of descriptive text giving the purpose of tools in an application. A tool is either a window, such as a child window or control, or an application-defined rectangular area within a window's client area.

+$#A About Tooltip Controls

A tooltip control is hidden most of the time, appearing only when the user puts the cursor on a tool and leaves it there for approximately one-half second. The tooltip control appears near the cursor and disappears when the user clicks a mouse button or moves the cursor off of the tool. A single tooltip control can support any number of tools. The following illustration shows a tooltip control associated with a button in a toolbar control.

{ewc msdncd, EWGraphic, group10607 0 /a "SDK_01.bmp"}

+$#A Tooltip Creation

You create a tooltip control by using the CreateWindowEx function, specifying the TOOLTIPS_CLASS window class. The class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, include the InitCommonControls function in your application.

The window procedure for a tooltip control automatically sets the size, position, and visibility of the tooltip control. The height of the tooltip window is based on the height of the font currently selected into the device context for the tooltip control. The width varies based on the length of the string currently in the tooltip window.

+$#A Style and Activation

A tooltip control has two class-specific styles: TTS_ALWAYSTIP and TTS_NOPREFIX.

A tooltip control with the TTS_ALWAYSTIP style appears when the cursor is on a tool, regardless of whether the tooltip control's owner window is active or inactive. Without this style, the tooltip control appears when the tool's owner window is active, but not when it is inactive.

The TTS_NOPREFIX style prevents the system from stripping the ampersand (&) character from a string. If a tooltip control does not have the TTS_NOPREFIX style, the system automatically strips ampersand characters, allowing an application to use the same string as both a menu item and as text in a tooltip control.

A tooltip control itself can be either active or inactive. When it is active, the tooltip control appears when the cursor is on a tool. When it is inactive, the tooltip control does not appear, even if the cursor is on a tool. The TTM_ACTIVATE message activates and deactivates a tooltip control.

A tooltip control has the WS_POPUP and WS_EX_TOOLWINDOW window styles, regardless of whether you specify them when creating the control.

+$#A Types of Tools

A tooltip control can support any number of tools. To support a particular tool, you must register the tool with the tooltip control by sending the TTM_ADDTOOL message to the tooltip control. The message includes the address of a TOOLINFO structure, which provides information the tooltip control needs to display text for the tool. The cbSize member is required and must specify the size of the structure.

A tooltip control supports tools implemented as windows (such as child windows or control windows) and as rectangular areas within a window's client area. When you add a tool implemented as a rectangular area, the hwnd member of TOOLINFO must specify the handle of the window that contains the area, and the rect member must specify the client coordinates of the area's bounding rectangle. In addition, the uId member must specify the application-defined identifier for the tool.

When you add a tool implemented as a window, the uId member of TOOLINFO must contain the window handle of the tool. Also, the uFlags member must specify the TTF_IDISHWND value, which tells the tooltip control to interpret the uId member as a window handle.

+$#A Tool Text

When you add a tool to a tooltip control, the lpszText member of the TOOLINFO structure must specify the address of the string to display for the tool. You can change the text any time after adding the tool by using the TTM_UPDATETIPTEXT message.

If the high-order word of lpszText is zero, the low-order word must be the identifier of a string resource. When the tooltip control needs the text, the system loads the specified string resource from the application instance identified by the hinst member of TOOLINFO.

If you specify the LPSTR_TEXTCALLBACK value in the lpszText member, the tooltip control notifies the window specified in the hwnd member of TOOLINFO whenever the tooltip control needs to display text for the tool. The tooltip control sends the TTN_NEEDTEXT notification message to the window. The message includes the address of a TOOLTIPTEXT structure, which contains the window handle as well as the application-defined identifier for the tool. The window examines the structure to determine the tool for which text is needed, and it fills the appropriate structure members with information that the tooltip control needs to display the string.

Many applications create toolbars containing tools that correspond to menu commands. For such tools, it is convenient for the tooltip control to display the same text as the corresponding menu item. The system automatically strips the ampersand (&) accelerator characters from all strings passed to a tooltip control, unless the control has the TTS_NOPREFIX style.

To retrieve the text for a tool, use the TTM_GETTEXT message.

+$#A Relaying Mouse Messages to the Tooltip

A tooltip control needs to receive mouse messages to determine when to display the tooltip window. Because Windows sends mouse messages only to the window that contains the cursor, you must use the TTM_RELAYEVENT message to relay mouse messages to the tooltip control.

If a tool is implemented as a rectangular area in an application-defined window, the window procedure receives mouse messages and can relay them to the tooltip control. However, if a tool is implemented as a system-defined window, the mouse messages are sent to the window and are not readily available to the application. You must use a message hook to access and relay the mouse messages, or you must subclass the window.

When a tooltip control receives a relayed WM_MOUSEMOVE message, it determines whether the cursor is in the bounding rectangle of a tool. If the cursor is there, the tooltip control sets a timer. At the end of the time-out duration, the tooltip control checks the position of the cursor to see whether it has moved. If the cursor has not, the tooltip control retrieves the text for the tool, copies the text into the tooltip window, and shows the window. The tooltip control continues to show the window until it receives a relayed button-up or button-down message or until a relayed WM_MOUSEMOVE message indicates that the cursor has moved outside the bounding rectangle of the tool.

A tooltip control actually has three time-out durations associated with it. The initial duration is the length of time that the cursor must remain stationary within the bounding rectangle of a tool before the tooltip window is displayed. The reshow duration is the length of the delay before subsequent tooltip windows are displayed when the cursor moves from one tool to another. The autopopup duration is the length of time that the tooltip window remains displayed before it is hidden. That is, if the cursor remains stationary within the bounding rectangle after the tooltip window is displayed, the tooltip window is automatically hidden at the end of the autopopup duration. You can adjust all of the time-out durations by using the TTM_SETDELAYTIME message.

If an application includes a tool implemented as a rectangular area and the size or position of the control changes, it can use the TTM_NEWTOOLRECT message to report the change to the tooltip control. An application does not need to report size and position changes for a tool implemented as a window. Reporting that is not necessary because the tooltip control uses the window handle of a tool to determine if the cursor is on the tool, not the tool's bounding rectangle.

When it is about to be displayed, a tootip control sends the TTN_SHOW notification to the owner window. A tooltip control sends the TTN_POP notification when it is about to be hidden. Each notification is sent in the context of a WM_NOTIFY message.

+$#A Tooltip Hit Testing

The TTM_HITTEST message allows you to retrieve information that a tooltip control maintains about the tool occupying a particular point. The message includes a TTHITTESTINFO structure that contains a window handle, the coordinates of a point, and the address of a TOOLINFO structure. The tooltip control determines whether a tool occupies the point and, if it does, fills TOOLINFO with information about the tool.

+$#A Miscellaneous Messages

The TTM_GETCURRENTTOOL and TTM_GETTOOLINFO messages fill a TOOLINFO structure with information about a tool that has been registered with a tooltip control. The TTM_SETTOOLINFO message allows you to change the information that a tooltip control maintains for a particular tool. The TTM_DELTOOL message deletes a tool from a tooltip control.

+$#A Default Tooltip Control Message Processing

This section describes the window messages handled by the window procedure for the TOOLTIPS_CLASS window class.

Message | Default processing
WM_CREATE | Ensures that the tooltip control has the WS_EX_TOOLWINDOW and WS_POPUP window styles. It also allocates memory and initializes internal variables.
WM_DESTROY | Frees resources allocated for the tooltip control.
WM_GETFONT | Returns the handle of the font currently selected into the DC for the tooltip control.
WM_MOUSEMOVE | Hides the tooltip window.
WM_PAINT | Draws the tooltip window.
WM_SETFONT | Selects a new font handle into the DC for the tooltip control.
WM_TIMER | Hides the tooltip window if the tool has changed position or if the cursor has moved outside the tool. Otherwise, it shows the tooltip window.
WM_WININICHANGE | Resets internal variables that are based on system metrics.

+$#A Using Tooltip Controls

• Creating a tooltip control

• Using a tooltip control with a dialog box

+$#A Creating a Tooltip Control

The following example creates a tooltip control and adds several tools to it. The example creates a grid of rectangles in the client area of a window and then uses the TTM_ADDTOOL message to add each rectangle to the tooltip control. Note that the window procedure for the owner of the tooltip control must handle mouse messages and pass them on to the tooltip control by using the TTM_RELAYEVENT message.

// DoCreateTooltip - creates a tooltip control and adds some tools to it.

// Returns the handle of the tooltip control if successful or NULL otherwise.

// hwndOwner - handle of the owner window

//

// Global variable

// g_hinst - handle of the application instance

extern HINSTANCE g_hinst;

HWND DoCreateTooltip(HWND hwndOwner)

{

HWND hwndTT; // handle of tooltip

int row, col; // rows and columns

TOOLINFO ti; // tool information

int id = 0; // offset to string identifiers

static char *szTips[NUM_TIPS] = // tip text

{

"Cut", "Copy", "Paste", "Undo", "Open", "Save"

};

// Ensure that the common control DLL is loaded, and create a tooltip control. ( Jmm #define TOOLTIPS_CLASSW L"tooltips_class32" )

InitCommonControls();

hwndTT = CreateWindow(TOOLTIPS_CLASS, (LPSTR) NULL, TTS_ALWAYSTIP,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

NULL, (HMENU) NULL, g_hinst, NULL);

if (hwndTT == (HWND) NULL)

return (HWND) NULL;

// Divide the client area into a grid of rectangles, and add each rectangle to the tooltip.

for (row = 0; row < MAX_ROWS ; row++ )

for (col = 0; col < MAX_COLS; col++) {

ti.cbSize = sizeof(TOOLINFO);

ti.uFlags = 0;

ti.hwnd = hwndOwner;

ti.hinst = g_hinst;

ti.uId = (UINT) id;

ti.lpszText = (LPSTR) szTips[id++];

ti.rect.left = col * CX_COLUMN;

ti.rect.top = row * CY_ROW;

ti.rect.right = ti.rect.left + CX_COLUMN;

ti.rect.bottom = ti.rect.top + CY_ROW;

if (!SendMessage(hwndTT, TTM_ADDTOOL, 0,

(LPARAM) (LPTOOLINFO) &ti))

return NULL;

}

return hwndTT;

}

+$#A Using a Tooltip Control with a Dialog Box

The following example includes a set of application-defined functions that implement a tooltip control for a dialog box. The DoCreateDialogTooltip function creates a tooltip control and uses the EnumChildWindows function to enumerate the controls in the dialog box. The enumeration procedure, EnumChildProc, registers each control with the tooltip control. The procedure specifies the dialog box as the parent window of each tooltip control and includes the LPSTR_TEXTCALLBACK value for each tooltip control. As a result, the dialog box receives a WM_NOTIFY message that contains the TTN_NEEDTEXT notification message whenever the tooltip control needs the text for a control. The dialog box procedure calls the OnWMNotify function to process the TTN_NEEDTEXT notifications. OnWMNotify provides the appropriate string based on the identifier of the tooltip control.

The tooltip control needs to receive mouse messages that the system sends to the control windows. To access the messages, the DoCreateDialogTooltip function installs a hook procedure of the WH_GETMESSAGE type. The hook procedure, GetMsgProc, monitors the message stream for mouse messages intended for one of the control windows and relays the messages to the tooltip control.

// DoCreateDialogTooltip - creates a tooltip control for a dialog box,

// enumerates the child control windows, and installs a hook

// procedure to monitor the message stream for mouse messages posted

// to the control windows.

// Returns TRUE if successful or FALSE otherwise.

//

// Global variables

// g_hinst ⎯ handle of the application instance

// g_hwndTT ⎯ handle of the tooltip control

// g_hwndDlg ⎯ handle of the dialog box

// g_hhk ⎯ handle of the hook procedure

BOOL DoCreateDialogTooltip(void)

{

// Ensure that the common control DLL is loaded, and create

// a tooltip control.

InitCommonControls();

g_hwndTT = CreateWindowEx(0, TOOLTIPS_CLASS, (LPSTR) NULL,

TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, g_hwndDlg, (HMENU) NULL, g_hinst, NULL);

if (g_hwndTT == NULL)

return FALSE;

// Enumerate the child windows to register them with the tooltip
// control.

if (!EnumChildWindows(g_hwndDlg, (WNDENUMPROC) EnumChildProc, 0))

return FALSE;

// Install a hook procedure to monitor the message stream for mouse

// messages intended for the controls in the dialog box.

g_hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc,

(HINSTANCE) NULL, GetCurrentThreadId());

if (g_hhk == (HHOOK) NULL)

return FALSE;

return TRUE;

}

// EmumChildProc - registers control windows with a tooltip control by
// using the TTM_ADDTOOL message to pass the address of a

// TOOLINFO structure.

// Returns TRUE if successful or FALSE otherwise.

// hwndCtrl - handle of a control window

// lParam - application-defined value (not used)

BOOL EnumChildProc(HWND hwndCtrl, LPARAM lParam)

{

TOOLINFO ti;

char szClass[64];

// Skip static controls.

GetClassName(hwndCtrl, szClass, sizeof(szClass));

if (lstrcmp(szClass, "STATIC") {

ti.cbSize = sizeof(TOOLINFO);

ti.uFlags = TTF_IDISHWND;

ti.hwnd = g_hwndDlg;

ti.uId = (UINT) hwndCtrl;

ti.hinst = 0;

ti.lpszText = LPSTR_TEXTCALLBACK;

SendMessage(g_hwndTT, TTM_ADDTOOL, 0,

(LPARAM) (LPTOOLINFO) &ti);

}

return TRUE;

}

// GetMsgProc - monitors the message stream for mouse messages intended

// for a control window in the dialog box.

// Returns a message-dependent value.

// nCode - hook code

// wParam - message flag (not used)

// lParam - address of an MSG structure

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)

{

MSG *lpmsg;

lpmsg = (MSG *) lParam;

if (nCode < 0 || !(IsChild(g_hwndDlg, lpmsg->hwnd)))

return (CallNextHookEx(g_hhk, nCode, wParam, lParam));

switch (lpmsg->message) {

case WM_MOUSEMOVE:

case WM_LBUTTONDOWN:

case WM_LBUTTONUP:

case WM_RBUTTONDOWN:

case WM_RBUTTONUP:

if (g_hwndTT != NULL) {

MSG msg;

msg.lParam = lpmsg->lParam;

msg.wParam = lpmsg->wParam;

msg.message = lpmsg->message;

msg.hwnd = hwnd;

SendMessage(g_hwndTT, TTM_RELAYEVENT, 0,

(LPARAM) (LPMSG) &msg);

}

break;

default:

break;

}

return (CallNextHookEx(g_hhk, nCode, wParam, lParam));

}

// OnWMNotify - provides the tooltip control with the appropriate text

// to display for a control window. This function is called by

// the dialog box procedure in response to a WM_NOTIFY message.

// lParam - second message parameter of the WM_NOTIFY message

VOID OnWMNotify(LPARAM lParam)

{

LPTOOLTIPTEXT lpttt;

int idCtrl;

if ((((LPNMHDR) lParam)->code) == TTN_NEEDTEXT) {

idCtrl = GetDlgCtrlID((HWND) ((LPNMHDR) lParam)->idFrom);

lpttt = (LPTOOLTIPTEXT) lParam;

switch (idCtrl) {

case ID_HORZSCROLL:

lpttt->lpszText = "A horizontal scroll bar.";

return;

case ID_CHECK:

lpttt->lpszText = "A check box.";

return;

case ID_EDIT:

lpttt->lpszText = "An edit control.";

return;

}

}

return;

}

+$#A Tooltip Control Reference

The following messages and structures are associated with tooltip controls. These elements can be grouped as follows.

Tooltip Activation

TTM_ACTIVATE

Adding and Deleting Tools

TOOLINFO
TTM_ADDTOOL
TTM_DELTOOL

Mouse Messages

TTM_NEWTOOLRECT
TTM_RELAYEVENT
TTM_SETDELAYTIME
TTN_POP
TTN_SHOW

Tool Text

TOOLTIPTEXT
TTM_GETTEXT
TTM_UPDATETIPTEXT
TTN_NEEDTEXT

Tooltip Hit Testing

TTHITTESTINFO
TTM_HITTEST
TTM_WINDOWFROMPOINT

Tool Information

TTM_ENUMTOOLS
TTM_GETCURRENTTOOL
TTM_GETTOOLCOUNT
TTM_GETTOOLINFO
TTM_SETTOOLINFO

+$#A Tooltip Control Messages

An application sends messages to activate and deactivate a tooltip control, add and delete tools, and so on.

TTM_ACTIVATE
TTM_ADDTOOL
TTM_DELTOOL
TTM_ENUMTOOLS
TTM_GETCURRENTTOOL
TTM_GETTEXT
TTM_GETTOOLCOUNT
TTM_GETTOOLINFO
TTM_HITTEST
TTM_NEWTOOLRECT
TTM_RELAYEVENT
TTM_SETDELAYTIME
TTM_SETTOOLINFO
TTM_UPDATETIPTEXT
TTM_WINDOWFROMPOINT

+$#A Tooltip Control Notification Messages

The following notification messages are used with tooltip controls.

TTN_NEEDTEXT
TTN_POP
TTN_SHOW

+$#A Tooltip Control Structures

The following structures are used with tooltip control messages.

TOOLINFO
TOOLTIPTEXT
TTHITTESTINFO

Créé avec HelpNDoc Personal Edition: Générateur gratuit de livres électroniques et documentation


Trackbars.rtf

+$#AK Trackbars

A trackbar is a window that contains a slider and optional tick marks. When the user moves the slider, using either the mouse or the direction keys, the trackbar sends notification messages to indicate the change. The following illustration shows a typical trackbar.

{ewc msdncd, EWGraphic, group10608 0 /a "SDK_01.bmp"}

+$#A About Trackbars

Trackbars are useful when you want the user to select a discrete value or a set of consecutive values in a range. For example, you might use a trackbar to allow the user to set the repeat rate of the keyboard by moving the slider to a given tick mark.

The slider in a trackbar moves in increments that you specify when you create it. For example, if you specify that the trackbar should have a range of five, the slider can only occupy six positions: a position at the left side of the trackbar and one position for each increment in the range. Typically, each of these positions is identified by a tick mark.

You create a trackbar by using the CreateWindowEx function, specifying the TRACKBAR_CLASS window class. Once you have created a trackbar, you can use trackbar messages to change many of its properties. Changes that you can make include setting the minimum and maximum positions for the slider, drawing tick marks, setting a selection range, and repositioning the slider.

+$#A Trackbar Styles

Trackbars can have either a vertical or horizontal orientation. They can have tick marks on either side, both sides, or neither. They can also be used to specify a range of consecutive values. These properties are controlled by using trackbar styles, which you specify when you create the trackbar.

The TBS_HORZ and TBS_VERT styles determine the orientation of the trackbar. If you do not specify an orientation, the trackbar is oriented horizontally.

The TBS_AUTOTICKS style creates a trackbar that has a tick mark for each increment in its range of values. These tick marks are added automatically when an application sends the TBM_SETRANGE message. If you do not specify TBS_AUTOTICKS, you can use messages, such as TBM_SETTIC and TBM_SETTICFREQ, to specify the positions of the tick marks. To create a trackbar that does not display tick marks, you can use the TSM_NOTICKS style.

You can display tick marks on either or both sides of the trackbar. For horizontal trackbars, you can specify the TBS_BOTTOM or TBS_TOP style. For vertical trackbars, you can specify the TBS_RIGHT or TBS_LEFT style. (TBS_BOTTOM and TBS_RIGHT are the default settings.) For tick marks on both sides of the trackbar in any orientation, specify the TBS_BOTH style.

A trackbar can display a selection range only if you specify the TBS_ENABLESELRANGE style when you create it. When a trackbar has this style, the tick marks at the starting and ending positions of a selection range are displayed as triangles (instead of vertical dashes) and the selection range is highlighted. For example, selection ranges might be useful in a simple scheduling application. The user could select a range of tick marks corresponding to hours in a day to identify a scheduled meeting time.

By default, the length of a trackbar's slider varies as the selection range changes. If the trackbar has the TBS_FIXEDLENGTH style, the length of the slider remains the same even if the selection range changes. A trackbar that has the TBS_NOTHUMB style does not include a slider.

+$#A Messages to Trackbars

An application can send messages to the trackbar to retrieve information about the window and to change its characteristics.

To retrieve the position of the slider (that is, the value the user has chosen), use the TBM_GETPOS message. To set the position of the slider, use the TBM_SETPOS message.

The range of a trackbar is the set of contiguous values that the trackbar can represent. Most applications use the TBM_SETRANGE message to set the range of a trackbar when it is first created. Applications can dynamically alter the range after the trackbar has been created by using the TBM_SETRANGEMAX and TBM_SETRANGEMIN messages. An application that allows the range to be changed dynamically typically retrieves the final range settings when the user has finished working with the trackbar. To retrieve these settings, use the TBM_GETRANGEMAX and TBM_GETRANGEMIN messages.

An application can use the TBS_AUTOTICKS style to have a trackbar's tick marks displayed automatically. If an application needs to control the position or frequency of the tick marks, however, a number of messages can be used. To set the position of a tick mark, an application can use the TBM_SETTIC message. The TBM_SETTICFREQ message allows an application to set tick marks that appear at regular intervals in the trackbar's range. For example, the application can use this message to display only ten tick marks in a range of 1 through 100. To retrieve the index in the range corresponding to a tick mark, use the TBM_GETTIC message. The TBM_GETPTICS message retrieves an array of these indices. To retrieve the position of a tick mark, in client coordinates, use the TBM_GETTICPOS message. An application can retrieve the number of tick marks by using the TBM_GETNUMTICS message. The TBM_CLEARTICS message removes all of a trackbar's tick marks.

A trackbar's line size determines how far the slider moves when an application receives a TB_LINEDOWN or TB_LINEUP notification message. Similarly, the page size determines the response to the TB_PAGEDOWN and TB_PAGEUP notification messages. Applications can retrieve and set the line and page size values by using the TBM_GETLINESIZE, TBM_SETLINESIZE, TBM_GETPAGESIZE, and TBM_SETPAGESIZE messages.

An application can use messages to retrieve the dimensions of a trackbar. The TBM_GETTHUMBRECT message retrieves the bounding rectangle for the slider. The TBM_GETCHANNELRECT message retrieves the bounding rectangle for the trackbar's channel. (The channel is the area over which the slider moves and which contains the highlight when a range is selected.)

If a trackbar has the TBS_ENABLESELRANGE style, the user can select a range of contiguous values from it. A number of messages allow the selection range to be adjusted dynamically. The TBM_SETSEL message sets the starting and ending positions of a selection. To set just the starting position or just the ending position of a selection, use the TBM_SETSELSTART or TBM_SETSELEND message. When the user has finished setting a selection range, an application can retrieve the settings by using the TBM_GETSELSTART and TBM_GETSELEND messages. To clear a user's selection, use the TBM_CLEARSEL message.

+$#A Trackbar Notification Messages

A trackbar notifies its parent window of user actions by sending the parent WM_HSCROLL messages. The low-order word of the wParam parameter of WM_HSCROLL contains the notification code, and the high-order word specifies the position of the slider. The lParam parameter is the handle of the trackbar.

The system sends the TB_BOTTOM, TB_LINEDOWN, TB_LINEUP, and TB_TOP notification messages only when the user interacts with a trackbar by using the keyboard. The TB_THUMBPOSITION and TB_THUMBTRACK notification messages are only sent when the user is using the mouse. The TB_ENDTRACK, TB_PAGEDOWN, and TB_PAGEUP notification messages are sent in both cases. The following table lists the trackbar notification messages and the events (virtual-key codes or mouse events) that cause the notifications to be sent.

Notification message | Reason sent
TB_BOTTOM | VK_END
TB_ENDTRACK | WM_KEYUP (the user released a key that sent a relevant virtual-key code)
TB_LINEDOWN | VK_RIGHT or VK_DOWN
TB_LINEUP | VK_LEFT or VK_UP
TB_PAGEDOWN | VK_NEXT (the user clicked the channel below or to the right of the slider)
TB_PAGEUP | VK_PRIOR (the user clicked the channel above or to the left of the slider)
TB_THUMBPOSITION | WM_LBUTTONUP following a TB_THUMBTRACK notification message
TB_THUMBTRACK | Slider movement (the user dragged the slider)
TB_TOP | VK_HOME

+$#A Default Trackbar Message Processing

This section describes the window message processing performed by a trackbar.

Message | Processing performed
WM_CAPTURECHANGED | Kills the timer if one was set during WM_LBUTTONDOWN processing and sends the TB_THUMBPOSITION notification message, if necessary. It always sends the TB_ENDTRACK notification message.
WM_CREATE | Performs additional initialization. It sets the initial tick frequency to 1, the line size to 1, and the page size to − 1.
WM_DESTROY | Frees resources.
WM_ENABLE | Repaints the trackbar window.
WM_ERASEBKGND | Erases the window background, using the current background color for the trackbar.
WM_GETDLGCODE | Returns the DLGC_WANTARROWS value.
WM_KEYDOWN | Processes the direction keys and sends the TB_TOP, TB_BOTTOM, TB_PAGEUP, TB_PAGEDOWN, TB_LINEUP, and TB_LINEDOWN notification messages, as appropriate.
WM_KEYUP | Sends the TB_ENDTRACK notification message if the key was one of the direction keys.
WM_KILLFOCUS | Repaints the trackbar window.
WM_LBUTTONDOWN | Sets the focus and the mouse capture to the trackbar. When necessary, it sets a timer that determines how quickly the slider moves toward the mouse cursor when the user holds down the mouse button in the window.
WM_LBUTTONUP | Releases the mouse capture and kills the timer if one was set during WM_LBUTTONDOWN processing. It sends the TB_THUMBPOSITION notification message, if necessary. It always sends the TB_ENDTRACK notification message.
WM_MOUSEMOVE | Moves the slider and sends the TB_THUMBTRACK notification message when tracking the mouse (see WM_TIMER).
WM_PAINT | Paints the trackbar.
WM_SETFOCUS | Repaints the trackbar window.
WM_SIZE | Sets the dimensions of the trackbar, removing the slider if there is not enough room to display it.
WM_TIMER | Retrieves the mouse position and updates the position of the slider. (It is received only when the user is dragging the slider.)
WM_WININICHANGE | Initializes slider dimensions.

+$#A Using Trackbars

• Creating a trackbar

• Processing trackbar notification messages

+$#A Creating a Trackbar

The following example shows how to create a trackbar with the TBS_AUTOTICKS and TBS_ENABLESELRANGE styles. When the trackbar is created, both its range and its selection range are initialized. The page size is also set at this time.

// CreateTrackbar - creates and initializes a trackbar.

//

// Global variable

// g_hinst - instance handle

HWND WINAPI CreateTrackbar(

HWND hwndDlg, // handle of dialog box (parent window)

UINT iMin, // minimum value in trackbar range

UINT iMax, // maximum value in trackbar range

UINT iSelMin, // minimum value in trackbar selection

UINT iSelMax) // maximum value in trackbar selection

{

InitCommonControls(); // loads common control's DLL

hwndTrack = CreateWindowEx(

0, // no extended styles

TRACKBAR_CLASS, // class name

"Trackbar Control", // title (caption)

WS_CHILD | WS_VISIBLE |

TBS_AUTOTICKS | TBS_ENABLESELRANGE, // style

10, 10, // position

200, 30, // size

hwndDlg, // parent window

ID_TRACKBAR, // control identifier

g_hinst, // instance

NULL // no WM_CREATE parameter

);

SendMessage(hwndTrack, TBM_SETRANGE,

(WPARAM) TRUE, // redraw flag

(LPARAM) MAKELONG(iMin, iMax)); // min. & max. positions

SendMessage(hwndTrack, TBM_SETPAGESIZE,

0, (LPARAM) 4); // new page size

SendMessage(hwndTrack, TBM_SETSEL,

(WPARAM) FALSE, // redraw flag

(LPARAM) MAKELONG(iSelMin, iSelMax);

SendMessage(hwndTrack, TBM_SETPOS,

(WPARAM) TRUE, // redraw flag

(LPARAM) iSelMin);

SetFocus(hwndTrack);

return hwndTrack;

}

+$#A Processing Trackbar Notification Messages

The following example is a function that is called whenever a WM_HSCROLL message is received by the dialog box containing the trackbar. The trackbar has the TBS_ENABLESELRANGE style. The position of the slider is compared against the selection range, and the slider is moved to the starting or ending position of the selection range, when necessary.

// TBNotifications - handles trackbar notifications received

// in the wParam parameter of WM_HSCROLL. This function simply

// ensures that the slider remains within the selection range.

VOID WINAPI TBNotifications(

WPARAM wParam, // wParam of WM_HSCROLL message

HWND hwndTrack, // handle of trackbar window

UINT iSelMin, // minimum value of trackbar selection

UINT iSelMax) // maximum value of trackbar selection

{

DWORD dwPos; // current position of slider

switch (LOWORD(wParam)) {

case TB_ENDTRACK:

dwPos = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);

if (dwPos > iSelMax)

SendMessage(hwndTrack, TBM_SETPOS,

(WPARAM) TRUE, // redraw flag

(LPARAM) iSelMax);

else if (dwPos < iSelMin)

SendMessage(hwndTrack, TBM_SETPOS,

(WPARAM) TRUE, // redraw flag

(LPARAM) iSelMin);

break;

default:

break;

}

}

+$#A Trackbar Reference

The following messages are associated with trackbars.

Size and Position

TBM_GETCHANNELRECT
TBM_GETTHUMBLENGTH
TBM_GETTHUMBRECT
TBM_SETTHUMBLENGTH

Slider Position

TBM_GETPOS
TBM_SETPOS

Line and Page Size

TBM_GETLINESIZE
TBM_GETPAGESIZE
TBM_SETLINESIZE
TBM_SETPAGESIZE

Range

TBM_GETRANGEMAX
TBM_GETRANGEMIN
TBM_SETRANGE
TBM_SETRANGEMAX
TBM_SETRANGEMIN

Selection

TBM_CLEARSEL
TBM_GETSELEND
TBM_GETSELSTART
TBM_SETSEL
TBM_SETSELEND
TBM_SETSELSTART

Tick Marks

TBM_CLEARTICS
TBM_GETNUMTICS
TBM_GETPTICS
TBM_GETTIC
TBM_GETTICPOS
TBM_SETTIC
TBM_SETTICFREQ

+$#A Trackbar Messages

An application sends messages to the trackbar to retrieve information about the trackbar and to change its characteristics. The following are the trackbar messages.

TBM_CLEARSEL
TBM_CLEARTICS
TBM_GETCHANNELRECT
TBM_GETLINESIZE
TBM_GETNUMTICS
TBM_GETPAGESIZE
TBM_GETPOS
TBM_GETPTICS
TBM_GETRANGEMAX
TBM_GETRANGEMIN
TBM_GETSELEND
TBM_GETSELSTART
TBM_GETTHUMBLENGTH
TBM_GETTHUMBRECT
TBM_GETTIC
TBM_GETTICPOS
TBM_SETLINESIZE
TBM_SETPAGESIZE
TBM_SETPOS
TBM_SETRANGE
TBM_SETRANGEMAX
TBM_SETRANGEMIN
TBM_SETSEL
TBM_SETSELEND
TBM_SETSELSTART
TBM_SETTHUMBLENGTH
TBM_SETTIC
TBM_SETTICFREQ

Créé avec HelpNDoc Personal Edition: Éditeur complet de livres électroniques ePub


Tree-View Controls.rtf

+$#AK Tree-View Controls

A tree-view control is a window that displays a hierarchical list of items, such as the headings in a document, the entries in an index, or the files and directories on a disk. Each item consists of a label and an optional bitmapped image, and each item can have a list of subitems associated with it. By clicking an item, the user can expand and collapse the associated list of subitems. The following illustration shows a tree-view control that displays a table of contents.

{ewc msdncd, EWGraphic, group10609 0 /a "SDK_01.bmp"}

+$#A About Tree-View Controls

You create a tree-view control by using the CreateWindowEx function, specifying the WC_TREEVIEW window class. The class is registered when the common control dynamic-link library (DLL) is loaded. To ensure that this DLL is loaded, include the InitCommonControls function in your application.

After creating a tree-view control, you add, remove, arrange, or otherwise manipulate items by sending messages to it. Each message has one or more corresponding macros that you can use instead of sending the message explicitly. The macros are documented with the messages in the Reference section of this topic.

+$#A Tree-View Styles

Tree-view styles govern aspects of a tree-view control's appearance. You set the initial styles when you create the tree-view control. You can retrieve and change the styles after creating the tree-view control by using the GetWindowLong and SetWindowLong functions.

The TVS_HASLINES style enhances the graphic representation of a tree-view control's hierarchy by drawing lines that link child items to their corresponding parent item. This style does not link items at the root of the hierarchy. To do so, you need to combine the TVS_HASLINES and TVS_LINESATROOT styles.

The user can expand or collapse a parent item's list of child items by double-clicking the parent item. A tree-view control that has the TVS_HASBUTTONS style adds a button to the left side of each parent item. The user can click the button to expand or collapse the child items as an alternative to double-clicking the parent item. TVS_HASBUTTONS does not add buttons to items at the root of the hierarchy. To do so, you must combine TVS_HASLINES, TVS_LINESATROOT, and TVS_HASBUTTONS.

The TVS_EDITLABELS style makes it possible for the user to edit the labels of tree-view items. For more information about editing labels, see Tree-View Label Editing.

The TVS_SHOWSELALWAYS style causes a selected item to remain selected when the tree-view control loses focus.

+$#A Parent and Child Items

Any item in a tree-view control can have a list of subitems, which are called child items, associated with it. An item that has one or more child items is called a parent item. A child item is displayed below its parent item and is indented to indicate it is subordinate to the parent. An item that has no parent is at the top of the hierarchy and is called a root item.

You add an item to a tree-view control by sending the TVM_INSERTITEM message to the tree-view control. The message returns a handle of the HTREEITEM type, which uniquely identifies the item. When adding an item, you must specify the handle of the new item's parent item. If you specify NULL or the TVI_ROOT value instead of an parent item handle in the TV_INSERTSTRUCT structure, the item is added as a root item.

At any given time, the state of a parent item's list of child items can be either expanded or collapsed. When the state is expanded, the child items are displayed below the parent item. When it is collapsed, the child items are not displayed. The list automatically toggles between the expanded and collapsed states when the user double-clicks the parent item or, if the parent has the TVS_HASBUTTONS style, when the user clicks the button associated with the parent item. An application can expand or collapse the child items by using the TVM_EXPAND message.

A tree-view control sends the parent window a TVN_ITEMEXPANDING notification message when a parent item's list of child items is about to be expanded or collapsed. The notification gives an application the opportunity to prevent the change or to set any attributes of the parent item that depend on the state of the list of child items. After changing the state of the list, the tree-view control sends the parent window a TVN_ITEMEXPANDED notification message.

When a list of child items is expanded, it is indented relative to the parent item. You can set the amount of indentation by using the TVM_SETINDENT message or retrieve the current amount by using the TVM_GETINDENT message.

A tree-view control uses memory allocated from the heap of the process that creates the tree-view control. The maximum number of items in a tree view is based on the amount of memory available in the heap. Each item takes 64 bytes.

+$#A Item Labels

You typically specify the text of an item's label when adding the item to the tree-view control. The TVM_INSERTITEM message includes a TV_ITEM structure that defines the item's properties, including a string containing the text of the label.

A tree-view control allocates memory for storing each item; the text of the item labels takes up a significant portion of this memory. If your application maintains a copy of the strings in the tree-view control, you can decrease the memory requirements of the control by specifying the LPSTR_TEXTCALLBACK value in the pszText member of TV_ITEM instead of passing actual strings to the tree view. Using LPSTR_TEXTCALLBACK causes the tree-view control to retrieve the text of an item's label from the parent window whenever the item needs to be redrawn. To retrieve the text, the tree-view control sends a TVN_GETDISPINFO notification message, which includes the address of a TV_DISPINFO structure. The parent window must fill the appropriate members of the included structure.

+$#A Tree-View Label Editing

The user can directly edit the labels of items in a tree-view control that has the TVS_EDITLABELS style. The user begins editing by clicking the label of the item that has the focus. An application begins editing by using the TVM_EDITLABEL message. The tree-view control notifies the parent window when editing begins and when it is canceled or completed. When editing is completed, the parent window is responsible for updating the item's label, if appropriate.

When label editing begins, a tree-view control sends its parent window a TVN_BEGINLABELEDIT notification message. By processing this notification, an application can allow editing of some labels and prevent editing of others. Returning zero allows editing, and returning nonzero prevents it.

When label editing is canceled or completed, a tree-view control sends its parent window a TVN_ENDLABELEDIT notification message. The lParam parameter is the address of a TV_DISPINFO structure. The item member is a TV_ITEM structure that identifies the item and includes the edited text. The parent window is responsible for updating the item's label, if appropriate, perhaps after validating the edited string. The pszText member of TV_ITEM is zero if editing is canceled.

During label editing, typically in response to the TVN_BEGINLABELEDIT notification message, you can get the handle of the edit control used for label editing by using the TVM_GETEDITCONTROL message. You can send the edit control an EM_SETLIMITTEXT message to limit the amount of text a user can enter or subclass the edit control to intercept and discard invalid characters. Note, however, that the edit control is displayed only after TVN_BEGINLABELEDIT is sent.

+$#A Tree-View Item Position

An item's initial position is set when the item is added to the tree-view control by using the TVM_INSERTITEM message. The message includes a TV_INSERTSTRUCT structure that specifies the handle of the parent item and the handle of the item after which the new item is to be inserted. The second handle must identify either a child item of the given parent or one of these values: TVI_FIRST, TVI_LAST, or TVI_SORT.

When TVI_FIRST or TVI_LAST is specified, the tree-view control places the new item at the beginning or end of the given parent item's list of child items. When TVI_SORT is specified, the tree-view control inserts the new item into the list of child items in alphabetical order based on the text of the item labels.

You can put a parent item's list of child items into alphabetical order by using the TVM_SORTCHILDREN message. The message includes a parameter that specifies whether all levels of child items descending from the given parent item are also sorted in alphabetical order.

The TVM_SORTCHILDRENCB message allows you to sort child items based on criteria that you define. When you use this message, you specify an application-defined callback function that the tree-view control can call whenever the relative order of two child items needs to be decided. The callback function receives two 32-bit application-defined values for the items being compared and a third 32-bit value that you specify when sending TVM_SORTCHILDRENCB.

+$#A Tree-View Item States Overview

Each item in a tree-view control has a current state. For example, an item can be selected, disabled, expanded, and so on. For the most part, the tree-view control automatically sets an item's state to reflect user actions, such as selection of an item. However, you can also set an item's state by using the TVM_SETITEM message and retrieve the current state of an item by using the TVM_GETITEM message. For a complete list of item states, see Tree-View Control Constants.

An item's current state is specified by the state member of the TV_ITEM structure. A tree-view control might change an item's state to reflect a user action, such as selecting the item or setting the focus to the item. In addition, an application might change an item's state to disable or hide the item or to specify an overlay image or state image.

When you specify or change an item's state, the stateMask member of TV_ITEM specifies which state bits to set, and the state member contains the new values for those bits. To set an item's overlay image, stateMask must include the TVIS_OVERLAYMASK value, and state must include the one-based index of the overlay image shifted left eight bits by using the INDEXTOOVERLAYMASK macro. The index can be zero to specify no overlay image. The overlay image must have been added to the tree-view control's list of overlay images by a previous call to the ImageList_SetOverlayImage function. The function specifies the one-based index of the image to add; this is the index used with the INDEXTOOVERLAYMASK macro. A tree-view control can have up to four overlay images.

To set an item's state image, stateMask of TV_ITEM must include the TVIS_STATEIMAGEMASK value, and state must include the one-based index of the state image shifted left twelve bits by using the INDEXTOSTATEIMAGEMASK macro. The index can be zero to specify no state image. For more information about overlay and state images, see Tree-View Image Lists.

+$#A Item Selection

A tree-view control notifies the parent window when the selection changes from one item to another by sending the TVN_SELCHANGING and TVN_SELCHANGED notification messages. Both notifications include a value that specifies whether the change is the result of a mouse click or a keystroke. The notifications also include information about the item that is gaining the selection and the item that is losing the selection. You can use this information to set item attributes that depend on the selection state of the item. Returning TRUE in response to TVN_SELCHANGING prevents the selection from changing; returning FALSE allows the change.

An application can change the selection by sending the TVM_SELECTITEM message.

+$#A Item Information

Tree-view controls are used with a number of messages that retrieve information about items in the control. TVM_GETITEM retrieves the attributes of an item, including its current state, its selected and nonselected bitmapped images, a count of its child items, a pointer to its label string, and its application-defined 32-bit value.

The TVM_GETNEXTITEM message retrieves the tree-view item that bears the specified relationship to the current item. The message can retrieve an item's parent, the next or previous visible item, the first child item, and so on.

The TVM_GETITEMRECT message retrieves the bounding rectangle for a tree-view item. The TVM_GETCOUNT and TVM_GETVISIBLECOUNT messages retrieve a count of the items in a tree-view control and a count of the items that are currently visible in the tree-view control's window, respectively. You can ensure that a particular item is visible by using the TVM_ENSUREVISIBLE message.

+$#A Tree-View Image Lists

Each item in a tree-view control can have a pair of bitmapped images associated with it. The images appear on the left side of an item's label. One image is displayed when the item is selected, and the other is displayed when the item is not selected. For example, an item might display an open folder when it is selected and a closed folder when it is not selected.

To use item images, you must create an image list by using the ImageList_Create function, add the desired bitmaps to the list, and associate the list with the tree-view control by using the TVM_SETIMAGELIST message. By default, all items display the first image in the image list for both the selected and nonselected states. You can change the default behavior for a particular item by specifying the indexes of the selected and nonselected images when adding the item to the tree view using the TVM_INSERTITEM message. You can change the indexes after adding an item by using the TVM_SETITEM message.

A tree-view control's image lists can also contain overlay images, which are designed to be superimposed on item images. A nonzero value in bits 8 through 11 of a tree-view item's state specifies the one-based index of an overlay image (zero indicates no overlay image). Because a 4-bit, one-based index is used, overlay images must be among the first 15 images in the image lists. For more information about tree-view item states, see Tree-View Item States Overview.

If a state image list is specified, a tree-view control reserves space to the left of each item's icon for a state image. An application can use state images, such as checked and cleared check boxes, to indicate application-defined item states. A nonzero value in bits 12 through 15 specifies the one-based index of a state image (zero indicates no state image).

By specifying the I_IMAGECALLBACK value instead of the index of an image in the TV_ITEM structure, you can put off specifying the selected or nonselected image until the item is about to be redrawn. I_IMAGECALLBACK directs the tree view to query the parent window for the index by sending the TVN_GETDISPINFO notification message.

The TVM_GETIMAGELIST message retrieves the handle of a tree-view control's image list. This message is useful if you need to add more images to the list. For more information about image lists, see Image Lists.

+$#A Drag and Drop Operations

A tree-view control notifies the parent window when the user starts to drag an item. The parent window receives a TVN_BEGINDRAG notification message when the user begins dragging an item with the left mouse button and a TVN_BEGINRDRAG notification message when the user begins dragging with the right button. You can prevent a tree-view control from sending these notifications by giving the tree-view control the TVS_DISABLEDRAGDROP style.

You obtain an image to display during a drag operation by using the TVM_CREATEDRAGIMAGE message. The tree-view control creates a dragging bitmap based on the label of the item being dragged. Then the tree-view control creates an image list, adds the bitmap to it, and returns the handle of the image list.

You must provide the code that actually drags the item. This typically involves using the dragging capabilities of the image list functions and processing the WM_MOUSEMOVE and WM_LBUTTONUP (or WM_RBUTTONUP) messages sent to the parent window after the drag operation has begun. For more information about the image list functions, see Image Lists. For more information about dragging a tree-view item, see Dragging a Tree-View Item.

If items in a tree-view control are to be the targets of a drag and drop operation, you need to know when the mouse cursor is on a target item. You can find out by using the TVM_HITTEST message. You specify the address of a TV_HITTESTINFO structure that contains the current coordinates of the mouse cursor. When the SendMessage function returns, the structure contains a flag indicating the location of the mouse cursor relative to the tree-view control. If the cursor is over an item in the tree-view control, the structure contains the handle of the item as well.

You can indicate that an item is the target of a drag and drop operation by using the TVM_SETITEM message to set the state to the TVIS_DROPHILITED value. An item that has this state is drawn in the style used to indicate a drag and drop target.

+$#A About Tree-View Control Notification Messages

A tree-view control sends the following notification messages to its parent window in the form of WM_NOTIFY messages.

Notification | Description
TVN_BEGINDRAG | Signals the start of a drag and drop operation.
TVN_BEGINLABELEDIT | Signals the start of in-place label editing.
TVN_BEGINRDRAG | Signals the start of a drag and drop operation, using the right mouse button.
TVN_DELETEITEM | Signals the deletion of a specific item.
TVN_ENDLABELEDIT | Signals the end of label editing.
TVN_GETDISPINFO | Requests information that the tree-view control requires to display an item.
TVN_ITEMEXPANDED | Signals that a parent item's list of child items was expanded or collapsed.
TVN_ITEMEXPANDING | Signals that a parent item's list of child items is about to be expanded or collapsed.
TVN_KEYDOWN | Signals a keyboard event.
TVN_SELCHANGED | Signals that the selection has changed from one item to another.
TVN_SELCHANGING | Signals that the selection is about to be changed from one item to another.
TVN_SETDISPINFO | Notifies a parent window that it must update the information it maintains for an item.

+$#A Default Tree-View Controls Message Processing

This section describes the window message processing performed by a tree-view control. Because messages specific to tree-view controls are discussed elsewhere, they are not included here.

Message | Processing performed
WM_COMMAND | Processes the EN_UPDATE and EN_KILLFOCUS edit control notification messages and forwards all other edit control notifications to the parent window. There is no return value.
WM_CREATE | Allocates memory and initializes internal data structures. It returns zero if successful or − 1 otherwise.
WM_DESTROY | Frees all system resources associated with the control. It returns zero.
WM_ENABLE | Enables or disables the control.
WM_ERASEBKGND | Erases the window background using the current background color for the tree-view control. It returns TRUE.
WM_GETDLGCODE | Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values.
WM_GETFONT | Returns the handle of the current label font.
WM_HSCROLL | Scrolls the tree-view control. It returns TRUE if scrolling occurs or FALSE otherwise.
WM_KEYDOWN | Sends the NM_RETURN notification message when the user presses the ENTER key. It moves the caret when the user presses the direction keys or the PAGE UP, PAGE DOWN, HOME, END, or BACKSPACE key. It scrolls the tree-view control when the user presses the CTRL key in combination with those keys. It sends the TVN_KEYDOWN notification message to the parent window. It returns TRUE if a key is processed or FALSE otherwise.
WM_KILLFOCUS | Repaints the focused item, if any, and sends an NM_KILLFOCUS notification message to the parent window.
WM_LBUTTONDBLCLK | Cancels label editing and, if an item was double-clicked, sends the NM_DBLCLK or NM_RDBLCLK notification message to the parent window. If the parent window returns TRUE, the tree-view control toggles the expanded state of the item, sending the parent window the TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification messages. There is no return value.
WM_LBUTTONDOWN | Toggles the expanded state if the user clicked the button associated with a parent item. If the user clicked an item label, the tree-view control selects and sets the focus to the item. If the user moves the mouse before releasing the mouse button, the tree-view control begins a drag and drop operation. There is no return value.
WM_PAINT | Paints the invalid region of the tree-view control. It returns zero.
WM_RBUTTONDOWN | Checks to see if an item was clicked and a drag operation was begun. If the operation has begun, it sends a TVN_BEGINRDRAG notification message to the parent window and highlights the drop target. Otherwise, it sends an NM_RCLICK notification message to the parent window. There is no return value.
WM_SETFOCUS | Repaints the focused item, if any, and sends an NM_SETFOCUS notification message to the parent window.
WM_SETFONT | Saves the specified font handle and repaints the tree-view control using the new font.
WM_SETREDRAW | Sets or clears the redraw flag. The tree-view control is redrawn after the redraw flag is set. It returns zero.
WM_SIZE | Recomputes internal variables that depend on the size of the tree-view control's client area. It returns TRUE.
WM_STYLECHANGED | Cancels label editing and redraws the tree-view control using the new styles. It returns zero.
WM_SYSCOLORCHANGE | Redraws the tree-view control using the new color if the redraw flag is set. There is no return value.
WM_TIMER | Begins editing an item label. If the user clicks the label of the focused item, the tree-view control sets a timer instead of entering edit mode immediately. The timer makes it possible for the tree view to avoid entering edit mode if the user double-clicks the label. It returns zero.

WM_VSCROLL

Scrolls the tree-view control. It returns TRUE if scrolling occurs or FALSE otherwise.

+$#A Using Tree-View Controls

• Creating a tree-view control

• Initializing the image list

• Adding tree-view items

• Dragging a tree-view item

+$#A Creating a Tree-View Control

To create a tree-view control, use the CreateWindowEx function, specifying the WC_TREEVIEW value for the window class. The tree-view window class is registered in the application's address space when the common control dynamic-link library (DLL) is loaded. To ensure that the DLL is loaded, use the InitCommonControls function.

The following example creates a tree-view control that is sized to fit the client area of the parent window. It also uses application-defined functions to associate an image list with the control and add items to the control.

// CreateATreeView - creates a tree-view control.

// Returns the handle of the new control if successful or NULL
// otherwise.

// hwndParent - handle of the control's parent window

// lpszFileName - name of the file to parse for tree-view items

HWND CreateATreeView(HWND hwndParent, LPSTR lpszFileName)

{

RECT rcClient; // dimensions of client area

HWND hwndTV; // handle of tree-view control

// Ensure that the common control DLL is loaded.

InitCommonControls();

// Get the dimensions of the parent window's client area, and create

// the tree-view control.

GetClientRect(hwndParent, &rcClient);

hwndTV = CreateWindowEx(0, WC_TREEVIEW, "Tree View",

WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,

0, 0, rcClient.right, rcClient.bottom,

hwndParent, (HMENU) ID_TREEVIEW, g_hinst, NULL);

// Initialize the image list, and add items to the control.

// InitTreeViewImageLists and InitTreeViewItems are application-

// defined functions.

if (!InitTreeViewImageLists(hwndTV) ||

!InitTreeViewItems(hwndTV, lpszFileName)) {

DestroyWindow(hwndTV);

return FALSE;

}

return hwndTV;

}

+$#A Initializing the Image List

Every item in a tree-view control can have two images associated with it. An item displays one image when it is selected and the other when it is not. To include images with tree-view items, you must use the image list functions to create an image list and add images to it. Then you must associate the image list with the tree-view control by using the TVM_SETIMAGELIST message.

The following example creates an image list, adds three bitmaps to the list, and associates the image list with a tree-view control.

// InitTreeViewImageLists - creates an image list, adds three bitmaps to // it, and associates the image list with a tree-view control.

// Returns TRUE if successful or FALSE otherwise.

// hwndTV - handle of the tree-view control

//
// Global variables and constants

// g_nOpen, g_nClosed, and g_nDocument - integer variables for

// indexes of the images

// CX_BITMAP and CY_BITMAP - width and height of an icon

// NUM_BITMAPS - number of bitmaps to add to the image list

BOOL InitTreeViewImageLists(HWND hwndTV)

{

HIMAGELIST himl; // handle of image list

HBITMAP hbmp; // handle of bitmap

// Create the image list.

if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP,

FALSE, NUM_BITMAPS, 0)) == NULL)

return FALSE;

// Add the open file, closed file, and document bitmaps.

hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_OPEN_FILE));

g_nOpen = ImageList_Add(himl, hbmp, (HBITMAP) NULL);

DeleteObject(hbmp);

hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_CLOSED_FILE));

g_nClosed = ImageList_Add(himl, hbmp, (HBITMAP) NULL);

DeleteObject(hbmp);

hbmp = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_DOCUMENT));

g_nDocument = ImageList_Add(himl, hbmp, (HBITMAP) NULL);

DeleteObject(hbmp);

// Fail if not all of the images were added.

if (ImageList_GetImageCount(himl) < 3)

return FALSE;

// Associate the image list with the tree-view control.

TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);

return TRUE;

}

+$#A Adding Tree-View Items

You add an item to a tree-view control by sending the TVM_INSERTITEM message to the control. The message includes a pointer to a TV_INSERTSTRUCT structure, specifying the parent item, the item after which the new item is inserted, and a TV_ITEM structure that defines the attributes of the item. The attributes include the item's label, its selected and nonselected images, and a 32-bit application-defined value.

The example in this section creates a table of contents based on the information in a text file. The example includes two functions. The first function searches a file for headings. When it finds one, it extracts the text of the heading and the value that indicates the level of the heading and then passes them to the second function. Headings are assumed to be in the following form, .[heading].n, where heading is the text of the heading and n indicates the heading level. The example ignores heading levels greater than level four.

The second function adds an item to a tree-view control, using the heading text as the item's label and the heading level to determine the parent item for the new item. A level one heading is added to the root of the tree-view control, a level two heading is added as a child item of the previous level one item, and so on. The function assigns an image to an item based on whether it has any child items. If an item has child items, it gets an image representing a closed folder. Otherwise, it gets an image representing a document. An item uses the same image for both the selected and nonselected states.

// InitTreeViewItems - extracts headings from the specified file and

// passes them to a function that adds them to a tree-view control.

// Returns TRUE if successful or FALSE otherwise.

// hwndTV - handle of the tree-view control

// lpszFileName - name of file with headings

BOOL InitTreeViewItems(HWND hwndTV, LPSTR lpszFileName)

{

HANDLE hf; // handle of file

DWORD cbRead; // number of bytes read

char szItemText[128]; // label text of tree-view item

int nLevel; // heading level

LPCH pch; // pointer to data read from file

LPCH pchTmp; // temporary pointer

DWORD i, j; // counters

// Open the file to parse.

if ((hf = CreateFile(lpszFileName, GENERIC_READ,

FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL)) == (HANDLE) INVALID_HANDLE_VALUE)

return FALSE;

// Parse the file looking for headings.

pch = (LPCH) LocalAlloc(LPTR, sizeof(char) * 2048);

pchTmp = pch;

do {

// Read a chunk of the file.

ReadFile(hf, pchTmp, sizeof(char) * 2048, &cbRead,

(LPOVERLAPPED) NULL);

// Parse the chunk looking for ".[".

for (i = 0, j = 0; i < cbRead; i++) {

if ((i + 2) > cbRead) // break if end of chunk

break;

// Extract the heading text from between the brackets.

if ((*pchTmp == '.') && (*(pchTmp+1) == '[')) {

pchTmp = pchTmp + 2;

i += 2;

while (*pchTmp != ']' && ((i++) <= cbRead) )

szItemText[j++] = *pchTmp++;

szItemText[j] = '\0';

j = 0;

nLevel = atoi(pchTmp + 2);

// Add the item to the tree-view control.

AddItemToTree(hwndTV, (LPSTR) &szItemText, nLevel);

} else

++pchTmp;

}

pchTmp = pch;

} while (cbRead != 0);

CloseHandle((HANDLE) hf);

return TRUE;

}

// AddItemToTree - adds items to a tree-view control.

// Returns the handle of the newly added item.

// hwndTV - handle of the tree-view control

// lpszItem - text of the item to add

// nLevel - level at which to add the item

HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel)

{

TV_ITEM tvi;

TV_INSERTSTRUCT tvins;

static HTREEITEM hPrev = (HTREEITEM) TVI_FIRST;

static HTREEITEM hPrevRootItem = NULL;

static HTREEITEM hPrevLev2Item = NULL;

HTREEITEM hti;

tvi.mask = TVIF_TEXT | TVIF_IMAGE

| TVIF_SELECTEDIMAGE | TVIF_PARAM;

// Set the text of the item.

tvi.pszText = lpszItem;

tvi.cchTextMax = lstrlen(lpszItem);

// Assume the item is not a parent item, so give it a

// document image.

tvi.iImage = g_nDocument;

tvi.iSelectedImage = g_nDocument;

// Save the heading level in the item's application-defined

// data area.

tvi.lParam = (LPARAM) nLevel;

tvins.item = tvi;

tvins.hInsertAfter = hPrev;

// Set the parent item based on the specified level.

if (nLevel == 1)

tvins.hParent = TVI_ROOT;

else if (nLevel == 2)

tvins.hParent = hPrevRootItem;

else

tvins.hParent = hPrevLev2Item;

// Add the item to the tree-view control.

hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0,

(LPARAM) (LPTV_INSERTSTRUCT) &tvins);

// Save the handle of the item.

if (nLevel == 1)

hPrevRootItem = hPrev;

else if (nLevel == 2)

hPrevLev2Item = hPrev;

// The new item is a child item. Give the parent item a

// closed folder bitmap to indicate it now has child items.

if (nLevel > 1) {

hti = TreeView_GetParent(hwndTV, hPrev);

tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;

tvi.hItem = hti;

tvi.iImage = g_nClosed;

tvi.iSelectedImage = g_nClosed;

TreeView_SetItem(hwndTV, &tvi);

}

return hPrev;

}

+$#A Dragging a Tree-View Item

Dragging a tree-view item typically involves processing the TVN_BEGINDRAG (or TVN_BEGINRDRAG) notification message, the WM_MOUSEMOVE message, and the WM_LBUTTONUP (or WM_RBUTTONUP) message. It also involves using the image list functions to draw the item as it is being dragged. For more information about image lists, see Image Lists.

The remainder of this section provides an example that demonstrates how to drag a tree-view item. The example consists of three functions. The first function begins the drag operation, the second drags the image, and the third ends the drag operation.

+$#A Beginning the Tree-View Drag Operation

A tree-view control sends the parent window a TVN_BEGINDRAG (or TVN_BEGINRDRAG) notification message whenever the user starts to drag an item. The parent window receives the notification in the form of a WM_NOTIFY message whose lParam parameter is the address of an NM_TREEVIEW structure. The members of this structure include the screen coordinates of the mouse cursor and a TV_ITEM structure that contains information about the item to be dragged.

The following example shows how to process the WM_NOTIFY message to obtain TVN_BEGINDRAG.

case WM_NOTIFY:

switch (((LPNMHDR) lParam)->code) {

case TVN_BEGINDRAG:

Main_OnBeginDrag(hwndTV, // application-defined function

(NM_TREEVIEW *) lParam);

break;

.

. // Handle other notifications here.

.

}

break;

Beginning the drag operation involves using the ImageList_BeginDrag function. The function's parameters include the handle of the image list containing the image to use during the drag operation and the index of the image. You can either provide your own image list and image, or you can have the tree-view control create them for you by using the TVM_CREATEDRAGIMAGE message.

Because the drag image replaces the mouse cursor for the duration of the drag operation, ImageList_BeginDrag requires you to specify a hot spot within the image. The coordinates of the hot spot are relative to the upper left corner of the image. ImageList_BeginDrag also requires you to specify the initial location of the drag image. An application typically sets the initial location so that the hot spot of the drag image corresponds to that of the mouse cursor at the time the user began the drag operation.

The following function demonstrates how to begin dragging a tree-view item. It uses the drag image provided by the tree-view control and obtains the bounding rectangle of the item to determine the appropriate point for the hot spot. (The dimensions of the bounding rectangle are the same as those of the image.) Note that the bounding rectangle does not account for the indentation of child items. For this reason, the function adds the amount of indentation to the x-coordinate of the hot spot.

The function captures mouse input, causing mouse messages to be sent to the parent window. The parent window needs the subsequent WM_MOUSEMOVE messages to determine where to drag the image and the WM_LBUTTONUP message to determine when to end the drag operation.

// Main_OnBeginDrag - begins dragging an item in a tree-view control.

// hwndTV - handle of the image list

// lpnmtv - address of information about the item being dragged

void Main_OnBeginDrag(HWND hwndTV, NM_TREEVIEW *lpnmtv)

{

HIMAGELIST himl; // handle of image list

RECT rcItem; // bounding rectangle of item

DWORD dwLevel; // heading level of item

DWORD dwIndent; // amount that child items are indented

// Tell the tree-view control to create an image to use

// for dragging.

himl = TreeView_CreateDragImage(hwndTV, lpnmtv->itemNew.hItem);

// Get the bounding rectangle of the item being dragged.

TreeView_GetItemRect(hwndTV, lpnmtv->itemNew.hItem, &rcItem, TRUE);

// Get the heading level and the amount that the child items are

// indented.

dwLevel = lpnmtv->itemNew.lParam;

dwIndent = (DWORD) SendMessage(hwndTV, TVM_GETINDENT, 0, 0);

// Start the drag operation.

ImageList_BeginDrag(himl, hwndTV, 0,

lpnmtv->ptDrag.x,

lpnmtv->ptDrag.y,

lpnmtv->ptDrag.x - ((dwLevel - 1) * dwIndent),

lpnmtv->ptDrag.y - rcItem.top);

// Hide the mouse cursor, and direct mouse input to the

// parent window.

ShowCursor(FALSE);

SetCapture(GetParent(hwndTV));

g_fDragging = TRUE;

return;

}

+$#A Dragging the Tree-View Item

You drag a tree-view item by calling the ImageList_DragMove function when the parent window receives a WM_MOUSEMOVE message, as the following example shows. The example also performs hit testing during the drag operation to determine whether to highlight other items in the tree view as targets of a drag and drop operation.

// Main_OnMouseMove - drags an item in a tree-view control,

// highlighting the item that is the target.

// hwndParent - handle of the parent window

// hwndTV - handle of the tree-view control

// xCur and yCur - x- and y-coordinates of the mouse cursor

void Main_OnMouseMove(HWND hwndParent, HWND hwndTV, LONG xCur, LONG yCur)

{

HTREEITEM htiTarget; // handle of target item

TV_HITTESTINFO tvht; // hit test information

if (g_fDragging) {

// Drag the item to the current position of the mouse cursor.

ImageList_DragMove(xCur, yCur);

// Find out if the cursor is on the item. If it is, highlight

// the item as a drop target.

tvht.pt.x = xCur;

tvht.pt.y = yCur;

if ((htiTarget = TreeView_HitTest(hwndTV, &tvht)) != NULL) {

TreeView_SelectDropTarget(hwndTV, htiTarget);

}

}

return;

}

+$#A Ending the Tree-View Drag Operation

The follow example ends a drag operation. The ImageList_EndDrag function is called when the parent window receives a WM_LBUTTONUP message.

// Main_OnLButtonUp - stops dragging a tree-view item, releases the

// mouse capture, and shows the mouse cursor.

//
// Global variable

// g_fDragging - indicates whether a drag operation is underway.

void Main_OnLButtonUp(void)

{

if (g_fDragging) {

ImageList_EndDrag();

ReleaseCapture();

ShowCursor(TRUE);

g_fDragging = FALSE;

}

return;

}

+$#A Tree-View Control Reference

The following messages, macros, notification messages, and structures are associated with tree-view controls. These elements can be grouped as follows.

Item Labels

TV_ITEM
TVN_GETDISPINFO

Label Editing

TVM_EDITLABEL
TVM_ENDEDITLABELNOW
TVM_GETEDITCONTROL
TVN_BEGINLABELEDIT
TVN_ENDLABELEDIT

Item States

TVM_GETITEM
TVM_SELECTITEM
TVM_SETITEM
TVN_SELCHANGED
TVN_SELCHANGING

Tree-View Image Lists

TVM_GETIMAGELIST
TVM_SETIMAGELIST

Item Position

TV_INSERTSTRUCT
TVM_INSERTITEM
TVM_SORTCHILDREN
TVM_SORTCHILDRENCB

Parent and Child Items

TVM_EXPAND
TVM_GETINDENT
TVM_SETINDENT
TVN_ITEMEXPANDED
TVN_ITEMEXPANDING

Item Information

TV_DISPINFO
TVM_ENSUREVISIBLE
TVM_GETCOUNT
TVM_GETNEXTITEM
TVM_GETVISIBLECOUNT

Drag and Drop Operations

TV_HITTESTINFO
TVM_CREATEDRAGIMAGE
TVM_HITTEST
TVN_BEGINDRAG
TVN_BEGINRDRAG

Miscellaneous

NM_TREEVIEW
TV_KEYDOWN
TV_SORTCB
TVM_DELETEITEM
TVM_GETISEARCHSTRING
TVM_GETITEMRECT
TVN_DELETEITEM
TVN_KEYDOWN
TVN_SETDISPINFO

+$#A Tree-View Control Messages

An application sends messages to add items and to alter the appearance and behavior of a tree-view control. Each message has a corresponding macro, which you can use instead of sending the message explicitly. Some messages may have related macros, which are a more specialized form of a corresponding macro.

TVM_CREATEDRAGIMAGE
TVM_DELETEITEM
TVM_EDITLABEL
TVM_ENDEDITLABELNOW
TVM_ENSUREVISIBLE
TVM_EXPAND
TVM_GETCOUNT
TVM_GETEDITCONTROL
TVM_GETIMAGELIST
TVM_GETINDENT
TVM_GETISEARCHSTRING
TVM_GETITEM
TVM_GETITEMRECT
TVM_GETNEXTITEM
TVM_GETVISIBLECOUNT
TVM_HITTEST
TVM_INSERTITEM
TVM_SELECTITEM
TVM_SETIMAGELIST
TVM_SETINDENT
TVM_SETITEM
TVM_SORTCHILDREN
TVM_SORTCHILDRENCB

+$#A Tree-View Control Notification Messages

A tree-view control sends notifications messages to its parent window to notify it about events.

TVN_BEGINDRAG
TVN_BEGINLABELEDIT
TVN_BEGINRDRAG
TVN_DELETEITEM
TVN_ENDLABELEDIT
TVN_GETDISPINFO
TVN_ITEMEXPANDED
TVN_ITEMEXPANDING
TVN_KEYDOWN
TVN_SELCHANGED
TVN_SELCHANGING
TVN_SETDISPINFO

+$#A Tree-View Control Structures

The following structures are used with tree-view control messages.

NM_TREEVIEW
TV_DISPINFO
TV_HITTESTINFO
TV_INSERTSTRUCT
TV_ITEM
TV_KEYDOWN
TV_SORTCB

+$#A Tree-View Control Constants

The following window style flags and item state flags are used with tree-view controls.

+$#A Tree-View Window Styles

A tree view's window style can include one or more of the following values.

TVS_DISABLEDRAGDROP | Prevents the tree-view control from sending TVN_BEGINDRAG notification messages.
TVS_EDITLABELS | Allows the user to edit the labels of tree-view items.
TVS_HASBUTTONS | Displays plus (+) and minus (-) buttons next to parent items. The user clicks the buttons to expand or collapse a parent item's list of child items. To include buttons with items at the root of the tree view, TVS_LINESATROOT must also be specified.
TVS_HASLINES | Uses lines to show the hierarchy of items.
TVS_LINESATROOT | Uses lines to link items at the root of the tree-view control. This value is ignored if TVS_HASLINES is not also specified.
TVS_SHOWSELALWAYS | Causes a selected item to remain selected when the tree-view control loses focus.

+$#A Tree-View Item States

An item's state determines its appearance and functionality. It can be zero, or one or more of the following values.

TVIS_BOLD | The item is bold.
TVIS_CUT | The item is selected as part of a cut and paste operation.
TVIS_DROPHILITED | The item is selected as a drag and drop target.
TVIS_EXPANDED | The item's list of child items is currently expanded; that is, the child items are visible. This value applies only to parent items.
TVIS_EXPANDEDONCE | The item's list of child items has been expanded at least once. The TVN_ITEMEXPANDING and TVN_ITEMEXPANDED notification messages are not sent for parent items that have specified this value. This value applies only to parent items.
TVIS_FOCUSED | The item has the focus, so it is surrounded by a standard focus rectangle. Only one item can have the focus.
TVIS_OVERLAYMASK | The item's overlay image is included when the item is drawn. The index of the overlay image must be specified in the state member of the TV_ITEM structure by using the INDEXTOOVERLAYMASK macro. The overlay image must be added to the tree view's image list by using the ImageList_SetOverlayImage function. This value should not be combined with any other value.
TVIS_SELECTED | The item is selected. The appearance of a selected item depends on whether it has the focus and on whether the system colors are used for selection.
TVIS_STATEIMAGEMASK | The item's state image is included when the item is drawn. The index of the state image must be specified in the state member of the TV_ITEM structure by using theINDEXTOSTATEIMAGEMASK macro. This value should not be combined with any other value.
TVIS_USERMASK | Same as TVIS_STATEIMAGEMASK.

Créé avec HelpNDoc Personal Edition: Écrire des livres électronique Kindle


Unicode and Character Sets.rtf

+$#AK Unicode and Character Sets

Unicode and traditional character sets give the Microsoft Windows operating system the means to support the many different written languages of the international marketplace. Unicode is a worldwide character-encoding standard that uses 16-bit character values to represent all the characters used in modern computing, including technical symbols and special characters used in publishing. Traditional character sets are previous character-encoding standards, such as the Windows ANSI character set, that use 8-bit character values or combinations of 8-bit values to represent the characters used in a specific language or geographical region.

This overview describes the character set functions and explains how to use them in your Win32-based applications.

+$#A About Unicode and Character Sets

Because the world's character-based data has been developed using both Unicode and traditional characters sets, the Win32 application programming interface (API) provides character set functions that help Win32-based applications convert the character-based data from its original character set to Unicode or another traditional character set. Because not all operating systems support Unicode, these character set functions also help Win32-based applications create character-based data that may be transferred to and used on any operating system.

For details about Unicode beyond the scope of this overview, see The Unicode™ Standard: Worldwide Character Encoding, Version 1.0, Volumes 1 and 2, Addison-Wesley Publishing Company: 1991, 1992, ISBN 0201567881.

+$#A Unicode and Character Set Reference

This section provides detailed reference information for the character set functions, structures, and messages. The information is separated into the following functional groups.

+$#A Character Sets

A character set is a mapping of characters to their identifying numeric values. Most of the character sets commonly used in computers are single-byte character sets in which each character is identified by a value one byte wide. The large number of characters in Asian languages led to the development of multibyte character sets, in particular the double-byte character set (DBCS). Microsoft® Windows NT™ incorporates a new global standard for character encoding: Unicode.

+$#A Single-byte Character Sets

A single-byte character set is a mapping of 256 individual characters to their identifying numeric values. The character codes 0x20 through 0x7E represent standardized displayable characters, but the characters represented by the remaining codes vary among character sets. The ASCII character set covers the range 0x00 through 0x7F.

In Windows, the ANSI character set is used in window manager and graphics device interface (GDI), but the MS-DOS file allocation table (FAT) file system uses a character set called the original equipment manufacturer (OEM) character set. Variations on the character sets, called code pages, include different special characters, typically customized for a language or group of languages. The OEM code page generally used in the United States is code page 437.

Applications that use the Microsoft® Win32® application programming interface (API) can use Unicode to avoid the inconsistencies of varied code pages and as an aid in developing easily localized applications.

An application can use the GetACP function to retrieve the ANSI code-page identifier for the system or use the GetOEMCP function to retrieve the OEM code-page identifier.

The OemToChar and OemToCharBuff functions allow an application to convert a character or string from the OEM code page to either the ANSI code page or Unicode. To convert in the other direction, you can use either the CharToOem or CharToOemBuff function. In addition, an application can use the MultiByteToWideChar and WideCharToMultiByte functions to map single-byte character set (SBCS) strings to Unicode and Unicode strings to SBCS strings.

The GetCPInfo function fills a CPINFO structure with information that includes the size, in bytes, of the largest character in the code page and the default character used when a character code is entered that has no corresponding entry in the code page.

+$#A Double-byte Character Sets

The double-byte character set (DBCS) is called an expanded 8-bit character set because its smallest unit is a byte. It can be thought of as the ANSI character set for some Asian versions of Windows (particularly the Japanese version). Win32 functions for the Japanese version of Windows accept DBCS strings for the ANSI versions of the functions. However, unlike the handling of Unicode, DBCS character handling requires detailed changes in the character-processing algorithms throughout an application's source code.

An application can use the IsDBCSLeadByte function to determine whether a given character is the first byte in a 2-byte character; this helps identify double-byte character sets. In addition, an application can use the MultiByteToWideChar and WideCharToMultiByte functions to map DBCS strings to Unicode and Unicode strings to DBCS strings.

+$#A Unicode

Unicode is a worldwide character-encoding standard. Windows uses it exclusively at the system level for character and string manipulation. Unicode simplifies localization of software and improves multilingual text processing. By implementing it in an application, a developer can enable the application with universal data exchange capabilities for global marketing, using a single binary file for every possible character code.

Unicode defines semantics for each character, standardizes script behavior, provides a standard algorithm for bidirectional text, and defines cross-mappings to other standards. Among the scripts supported by Unicode are Latin, Greek, Han, Hiragana, and Katakana. Supported languages include, but are not limited to, German, French, English, Greek, Chinese, and Japanese.

Unicode can represent all the world's characters in modern computer use, including technical symbols and special characters used in publishing. Because each Unicode character is 16 bits wide, it is possible to have separate values for up to 65,536 characters. Unicode-enabled functions are often referred to as "wide-character" functions.

Win32 functions support applications that use either Unicode or the regular ANSI character set. Mixed use in the same application is also possible. Adding Unicode support to an application is easy, and a developer can even maintain a single set of sources from which to compile an application that supports either Unicode or the Windows ANSI character set.

Win32 functions support Unicode by assigning its strings a specific data type and providing a separate set of entry points and messages to support this new data type. A series of macros and naming conventions make transparent migration to Unicode, or even compiling both non-Unicode and Unicode versions of an application from the same set of sources, a straightforward matter.

Implementing Unicode as a separate data type also enables the compiler's type checking to ensure that only Unicode parameters are used with functions expecting Unicode strings.

+$#A Unicode in Windows

Win32 API elements that use characters are generally implemented in one of three formats:

• A generic version that can be compiled for either ANSI or Unicode

• An ANSI version

• A Unicode version

This guide dicsusses Unicode data types and how they are used in functions and messages; the use of resources, filenames, and command-line arguments; and methods of translating between different types of strings.

+$#A Data Types

Most string operations for Unicode can be written by using the same logic used for handling the Windows ANSI character set, except that the basic unit of operation is a 16-bit character instead of an 8-bit byte. The header files provide a number of type definitions that make it easy to create sources that can be compiled for Unicode or for the ANSI character set.

The following example shows the method used in the Win32 header files to define three sets of data types: a set of generic type definitions that can compile for either ANSI or Unicode, and two sets of specific type definitions. The first set of specific type definitions is for use with the existing Windows (ANSI) character set, and the other is for use with Unicode (or wide) characters.

/* Generic types */

#ifdef UNICODE

typedef wchar_t TCHAR;

#else

typedef unsigned char TCHAR;

#endif

typedef TCHAR * LPTSTR, *LPTCH;

/* 8-bit character specific */

typedef unsigned char CHAR;

typedef CHAR *LPSTR, *LPCH;

/* Unicode specific (wide characters) */

typedef unsigned wchar_t WCHAR;

typedef WCHAR *LPWSTR, *LPWCH;

The letter T preceding a type definition designates a generic type that can be compiled for either ANSI or Unicode. The letter W preceding a type definition designates a wide-character (Unicode) type. For the actual implementation of this method, see the WINNT.H header file.

An application using generic data types can be compiled for Unicode simply by defining UNICODE before the include statements for the header files. To compile the code for ANSI, omit the UNICODE definition.

Developers are encouraged to use the generic data types, but the specific types exist for applications that require mixed-type control.

+$#A Function Prototypes

Function prototypes are also provided in generic, ANSI, and Unicode varieties, as shown below. The generic function prototype consists of the standard function name implemented as a macro. The preprocessor expands each macro into one of the specific function prototypes, depending on whether UNICODE is defined. The letters A (ANSI) and W (wide) are added at the end of the function names in the specific function prototypes. In this example, the generic prototype uses the generic type LPTSTR for the text parameter, but the A and W prototypes use the LPCSTR and LPCWSTR character types, respectively.

SetWindowText(HWND hwnd, LPTSTR lpText);

SetWindowTextA(HWND hwnd, LPCSTR lpText);

SetWindowTextW(HWND hwnd, LPCWSTR lpText);

An application can use the generic function and make Unicode dependent on whether the UNICODE option is used to compile the code, or it can make mixed calls by using the explicit function names ending with A and W.

This three-prototype approach applies to all functions with text arguments. Always use a generic function prototype with the generic string and character types. All function names that end with an uppercase W take wide-character arguments.

Some functions exist only in wide-character versions and can be used only with the appropriate data type.

The reference material for each Unicode-enabled function is labeled with a Unicode icon. If the function exists as a macro that the preprocessor must expand into an ANSI or Unicode form, only the macro form is documented.

Note Whenever a function has a length parameter for a character string, the length should be given as a count of characters (TCHAR units) in the string. However, functions that require or return pointers to untyped memory blocks, such as the GlobalAlloc function, are exceptions.

+$#A Message Translation

Although applications generally use the same window class, messages between windows of different classes are transparently translated by the system.

Even though a window function is implemented to receive messages in Unicode or ANSI format, the window procedure can still send messages or call functions of either type.

The following messages have text arguments and are subject to automatic text translation. (For information about automatic translation, see Subclassing and Automatic Message Translation.)

CB_ADDSTRING | WM_ASKCBFORMATNAME
CB_DIR | WM_CHAR
CB_FINDSTRING | WM_CHARTOITEM
CB_GETLBTEXT | WM_CREATE
CB_INSERTSTRING | WM_DEADCHAR
CB_SELECTSTRING | WM_DEVMODECHANGE
 | WM_GETTEXT
EM_GETLINE | WM_MDICREATE
EM_REPLACESEL | WM_MENUCHAR
EM_SETPASSWORDCHAR | WM_NCCREATE
 | WM_SETTEXT
LB_ADDFILE | WM_SYSCHAR
LB_ADDSTRING | WM_SYSDEADCHAR
LB_DIR | WM_WININICHANGE
LB_FINDSTRING |
LB_GETTEXT |
LB_INSERTSTRING |
LB_SELECTSTRING |

+$#A String Functions

All of the string functions listed in this section exist in ANSI and Unicode implementations to support ANSI and Unicode arguments. There are, however, subtle differences among some of them.

The following string functions do not require special comment; their ANSI and Unicode implementations work identically.

CharNext
CharPrev
lstrcat
lstrcpy
lstrlen

The value returned by the lstrlen function is always the number of characters, regardless of whether the ANSI or Unicode form is used.

The following string functions are sensitive to the locale of the current thread (as derived from the locale the user selects in Control Panel). The lstrcmp and lstrcmpi functions do not perform byte comparisons like their ANSI C namesakes; they compare strings according to the rules of the selected locale.

CharLower
CharLowerBuff
CharUpper
CharUpperBuff
lstrcmp
lstrcmpi

The following functions convert between the OEM character set and either ANSI or Unicode, depending on which version is used.

CharToOem
CharToOemBuff
OemToChar
OemToCharBuff

The print function wsprintf supports Unicode by providing the following new and changed data types in its format specifications. These format specifications affect the way the wsprintf function interprets the corresponding passed-in parameter.

Format specification | ANSI version | Unicode version
c | CHAR | WCHAR
C | WCHAR | CHAR
hc, hC | CHAR | CHAR
hs, hS | LPSTR | LPSTR
lc, lC | WCHAR | WCHAR
ls, lS | LPWSTR | LPWSTR
s | LPSTR | LPWSTR
S | LPWSTR | LPSTR

The data type for the output text always depends on the version of the function. Where the data type of the passed-in parameter and of the output text do not agree, wsprintf will perform a conversion from Unicode to ANSI, or vice versa, as required.

For the Unicode version of wsprintf, the format string is Unicode, as is the output text.

+$#A Standard C Functions

The standard C libraries contain wide-character versions of the ANSI string functions that begin with the letters str. The wide-character versions of the functions start with the letters wcs (or sometimes _wcs). The Unicode data type is compatible with the wide-character data type wchar_t in ANSI C; this allows access to the wide-character string functions.

Generic functions exist for all standard C string functions. They start with the letters _tcs and are listed in the TCHAR.H header file. These functions use the generic data types TCHAR and TCHAR*

An application must add the following lines to its program in order to use the generic functions and compile for Unicode:

#define _UNICODE

#include <tchar.h>

#include <wchar.h>

Note that both the TCHAR.H and WCHAR.H are required, and that the leading underscore on the _UNICODE variable is also required.

The wcstombs and mbstowcs functions can convert from the character set supported by the standard C library to Unicode and back, with some limitations. For more information about translating strings to and from Unicode, see Translation Between String Types.

The printf function defined in TCHAR.H supports the same format specifications as wsprintf; for details, see String Functions. Similarly, TCHAR.H contains a wprintf function, in which the format string itself is a Unicode string.

+$#A Filenames

In Windows, the ANSI character set is used in window manager and GDI; the MS-DOS FAT file system uses the OEM character set. Windows applications that create MS-DOS files have sometimes had to use the CharToOem and OemToChar functions to translate between these character sets. However, the New Technology file system (NTFS) file system is capable of storing filenames in Unicode; no translation is necessary with NTFS.

With Unicode implementations of the file-system functions, it is not necessary to perform translations to and from ANSI and OEM character sets. Instead, a developer can use a single source file to compile non-Unicode versions of an application by providing macros for functions that are not invoked when compiling for Unicode, such as CharToOem and OemToChar.

The special filename characters in MS-DOS are unchanged in Unicode filenames:

"\," "/," ".," "?," "*."

These special characters are in the ASCII range of characters (0x00 through 0x7F) and their Unicode equivalents are simply the same values in a 2-byte form: 0x0000 through 0x007F.

+$#A Translation Between String Types

The following Win32 functions translate character strings from one string type to another.

Function | Description
FoldString | Translates one character string to another.
LCMapString | Maps a character string by locale.
ToUnicode | Translates a virtual-key code into a Unicode character.
MultiByteToWideChar | Maps a multibyte string into a wide-character string.
WideCharToMultiByte | Maps a wide-character string into a multibyte string.

The WideCharToMultiByte and MultiByteToWideChar functions are particularly useful for applications that support several string types. ANSI C also defines the conversion functions wcstombs and mbstowcs, but they can only convert to and from the character set supported by the standard C library.

+$#A Command-line Arguments

An application can use the GetCommandLine function to retrieve Unicode command-line arguments by calling it as a Unicode function.

+$#A Using Strings and Unicode

• Using generic data types

• Registering window classes

• Subclassing and automatic message translation

• Using special characters in Unicode

• Using functions that have no Unicode equivalents

+$#A Using Generic Data Types

If you use generic data types in your code, it can be compiled for Unicode simply by defining UNICODE before the include statements for the header files. To compile the code for ANSI, omit the UNICODE definition.

To convert code that processes strings so it can be compiled for either ANSI or Unicode, follow these steps.

1. Change all character and string types used for text to TCHAR, LPTSTR, or LPTCH.

2. Be sure that pointers to nontext data buffers or binary byte arrays are coded with the LPBYTE type and not mistakenly with the LPTSTR or LPTCH type. Declare pointers of indeterminate type explicitly as void pointers by using LPVOID, as appropriate.

3. Make pointer arithmetic type-independent. Using units of TCHAR size yields variables that are two bytes if UNICODE is defined, and one byte if UNICODE is not defined. Using pointer arithmetic always returns the number of elements pointed to by the pointer, whether the elements are one or two bytes in size. The following expression always returns the number of elements, regardless of whether UNICODE is defined:

cCount = lpEnd - lpStart;

The following expression determines the number of bytes used:

cByteCount = (lpEnd - lpStart) * sizeof(TCHAR);

There is no need to change a statement like the following one, because the pointer increment points to the next character element.

chNext = *++lpText;

4. Replace literal strings and manifest character constants with macros. Change expressions like the following one.

while(*lpFileName++ != '\\') {

/* .

/* .

/* .

}

Use the TEXT macro as follows in this expression.

while(*lpFileName++ != TEXT('\\')) {

./*

./*

./*

}

Precede a literal string in the Unicode form of a function by the letter L. The TEXT macro causes strings to be evaluated as L"string" when UNICODE is defined, and to "string" otherwise. The following call to the Unicode version of lstrlen demonstrates the use of the L identifier.

cch == lstrlenW(L"hello world");

For easier management, move literal strings into resources, especially if they contain characters outside the ASCII range (that is, 0x00 through 0x7F).

5. Call only the Unicode versions of the standard C library string functions, as listed in Standard C Functions.

6. Change any code that relies on 255 as the largest value for a character.

When you compile code that you have changed as outlined above, the compiler creates the same binary file as it did before you made the changes. When you compile the code using the UNICODE option, however, it is compiled as a Unicode application.

+$#A Registering Window Classes

A window class is supported by a window procedure. You can register a window class by using either the RegisterClassA or RegisterClassW function. If you register the window class by using RegisterClassA, the application tells the system that the windows of the created class expect messages with text or character parameters to use the ANSI character set; if you register it by using RegisterClassW, the application requests that the system pass text parameters of messages as Unicode. The IsWindowUnicode function enables applications to query the nature of each window.

The following example shows how to register an ANSI window class and a Unicode window class and how to write the window procedures for both cases. For the purposes of this example, all functions and structures are shown with the specific A or W data types. Using the techniques explained in Using Generic Data Types, you could alternatively write this example by using generic data types, so that it could be compiled as either ANSI or Unicode, depending on whether UNICODE is defined.

/* Register an ANSI window class. */

WNDCLASSA AnsiWndCls;

AnsiWndCls.style = CS_DBLCLKS | CS_PARENTDC;

AnsiWndCls.lpfnWndProc = (WNDPROC)AnsiWndProc;

AnsiWndCls.cbClsExtra = 0;

AnsiWndCls.cbWndExtra = 0;

AnsiWndCls.hInstance = hmodUser;

AnsiWndCls.hIcon = NULL;

AnsiWndCls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_IBEAM);

AnsiWndCls.hbrBackground = NULL;

AnsiWndCls.lpszMenuName = NULL;

AnsiWndCls.lpszClassName = "TestAnsi";

RegisterClassA(&AnsiWndCls);

/* Register a Unicode window class. */

WNDCLASSW UnicodeWndCls;

UnicodeWndCls.style = CS_DBLCLKS | CS_PARENTDC;

UnicodeWndCls.lpfnWndProc = (WNDPROC)UniWndProc;

UnicodeWndCls.cbClsExtra = 0;

UnicodeWndCls.cbWndExtra = 0;

UnicodeWndCls.hInstance = hmodUser;

UnicodeWndCls.hIcon = NULL;

UnicodeWndCls.hCursor = LoadCursor(NULL,(LPTSTR)IDC_IBEAM);

UnicodeWndCls.hbrBackground = NULL;

UnicodeWndCls.lpszMenuName = NULL;

UnicodeWndCls.lpszClassName = L"TestUnicode";

RegisterClassW(&UnicodeWndCls);

The following example shows the difference between handling the WM_CHAR message in an ANSI window procedure and a Unicode window procedure.

/* ANSI Window Procedure */

LONG AnsiWndProc(HWND hwnd, UINT message,

WPARAM wParam, LPARAM lParam)

{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*

* wParam - the value of the key

* lParam - (not used in this example)

*/

if (lstrcmpA("Q", (LPCSTR) wParam)) {

/*.

/*.

/*.

}

else {

./*

/*.

/*.

}

break;

./*

./* process other messages */

./*

}

}

/* Unicode Window Procedure */

LONG UniWndProc(HWND hwnd, UINT message,

WPARAM wParam, LPARAM lParam)

{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*

* wParam - the value of the key

* lParam - (not used in this example)

*/

if (lstrcmpW(L"Q", (LPCWSTR) wParam)) {

/*.

/*.

/*.

}

else {

/*.

/* .

/* .

}

break;

/* .

/* process other messages */

/* .

}

}

All text in messages received by AnsiWndProc will be composed of ANSI characters, and all text in messages received by UniWndProc will be composed of Unicode characters.

+$#A Subclassing and Automatic Message Translation

Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. The system automatically translates messages into ANSI or Unicode form, depending on the form of the function that subclassed the window procedure.

The following call to the SetWindowLongA function subclasses the current window procedure associated with the window identified by the hwnd parameter. The new window procedure, NewWndProc, will receive messages with text in ANSI format.

OldWndProc = (WNDPROC) SetWindowLongA(hwnd,

GWL_WNDPROC, (LONG)NewWndProc);

When NewWndProc has finished processing a message, it uses the CallWindowProc function as follows to pass the message to OldWndProc.

CallWindowProc(OldWndProc, hwnd, uMessage, wParam, lParam);

If OldWndProc was created with a class style of UNICODE, messages will be translated from the ANSI form received by NewWndProc into Unicode.

Similarly, a call to the SetWindowLongW function would subclass the current window procedure with a window procedure that expects Unicode text messages. Message translation, if necessary, is performed during the processing of the CallWindowProc function.

For more information about subclassing, see Window Procedures.

+$#A Using Special Characters in Unicode

There are a few special characters and characters with unusual meanings in Unicode text strings. To avoid unexpected problems with these characters, use the rules provided in the following topics.

• Null-terminated strings

• Byte-order mark

• Escape sequences and control characters

• Line and paragraph separators

• ASCII control codes 0x000D and 0x000A

• Nonspacing characters and diacritics

+$#A Null-terminated Strings

Always cast zero to TCHAR when using null-terminated strings. The code 0x0000 is the Unicode string terminator for null-terminated strings. A single null byte is not sufficient for this code, because many Unicode characters contain null bytes as either the high or the low byte. An example is the letter A, for which the character code is 0x0041.

+$#A Byte-order Mark

Always prefix a Unicode plain text file with a byte-order mark. Because Unicode plain text is a sequence of 16-bit codes, it is sensitive to the byte ordering used when the text was written.

A byte-order mark is not a control character that selects the byte order of the text; it simply informs an application receiving the file that the file is byte ordered.

Ideally, all Unicode text would follow only one set of byte-ordering rules. This is not possible, however, because microprocessors differ in the position of the least significant byte: Intel® and MIPS® processors have the least significant byte first; Motorola processors (and byte-reversed Unicode files) have it last. With only a single set of byte-ordering rules, users of one type of microprocessor would be forced to swap the byte order every time a plain text file is read from or written to, even if the file is never transferred to another system based on a different microprocessor.

The preferred place to specify byte order is in a file header, but text files do not have headers. Therefore, Unicode has defined a character (0xFEFF) and a noncharacter (0xFFFE) as byte-order marks. They are mirror byte-images of each other.

Since the sequence 0xFEFF is exceedingly rare at the outset of regular non-Unicode text files, it can serve as an implicit marker or signature to identify the file as a Unicode file. Applications written to read both Unicode and non-Unicode text files should use the presence of this sequence as a near-certain indicator that the file is a Unicode file. (Compare this technique to using the MS-DOS EOF marker to terminate text files.)

When an application finds 0xFEFF at the beginning of a text file, it typically processes the file as though it were a Unicode file, although it may also perform further heuristic checks to verify that this is true. Such a check could be as simple as a test of whether the variation in the low-order bytes is much higher than the variation in the high-order bytes. For example, if ASCII text is converted to Unicode text, every second byte is zero. Also, checking both for the linefeed and carriage-return characters (0x000A and 0x000D) and for even or odd file size can provide a strong indicator of the nature of the file.

When an application finds 0xFFFE at the beginning of a text file, it interprets it to mean the file is a byte-reversed Unicode file. The application can either swap the order of the bytes or alert the user that an error has occurred.

The Unicode byte-order mark character is not found in any code page, so it disappears if data is converted to ANSI. Unlike other Unicode characters, it is not replaced by a default character when it is converted. If a byte-order mark is found in the middle of a file, it is not interpreted as a Unicode character and has no effect on text output.

The Unicode value 0xFFFF is illegal in plain text files and cannot be passed between Windows functions. The value 0xFFFF is reserved for an application's private use.

+$#A Escape Sequences and Control Characters

Translate escape sequences character by character into Unicode. When an ASCII plain text file is converted to Unicode, there is a chance that it will subsequently be converted back to ASCII. Converting escape sequences into Unicode on a character-by-character basis, rather than as a single 2-byte characters makes it possible to perform the reverse conversion without recognizing and parsing the escape sequences as such. For example, ESC+A should become 0x001B (ESC), 0x0041 (A), rather than 0x411B.

The first 32 sixteen-bit characters in Unicode are intended for the 32 control characters. This approach supports the existing use of control characters for formatting purposes ⎯ that is, Unicode applications can treat these control characters in exactly the same way as they treat their ASCII equivalents.

+$#A Line and Paragraph Separators

Use the line-separator character (0x2028) and the paragraph-separator character (0x2029) to divide plain text. A new line is begun after each line separator. A new paragraph is begun after each paragraph separator.

Because these are separator codes, it is not necessary to start the first line or paragraph in the file with them, or to end the last line or paragraph with them. (Doing so would indicate that there is an empty line or paragraph in that location.)

The line separator can be used to indicate an unconditional end of line. However, line separators do not correspond to the carriage-return and linefeed characters, or to the carriage-return−linefeed character combination; they must be processed separately from carriage return and linefeed characters.

The paragraph separator can be inserted between paragraphs of text. This allows plain text files to be created that can be formatted with different line widths on different systems. The receiving system can ignore any line breaks and break paragraphs only at the paragraph separators.

+$#A ASCII Control Codes 0x000D and 0x000A

The Unicode standard does not prescribe a specific meaning for the control codes 0x000D (carriage return) and 0x000A (linefeed). There is no requirement that these codes be used in combination, and if used individually, either code may represent either itself only or both codes together. It is entirely up to the application to determine what these codes represent. (Applications have always had to interpret these control codes in ASCII; the Unicode policy is not a change in their use.)

+$#A Nonspacing Characters and Diacritics

When breaking lines or otherwise separating text, be sure to keep nonspacing characters with their base characters. Many scripts contain characters that visually combine with other characters on output ⎯ for example, the diacritical marks (or "floating accents"). In Unicode, nonspacing characters follow their base character.

+$#A Using Functions That Have No Unicode Equivalents

Windows functions that have not been exported in a Unicode version have typically been replaced by more powerful or extended functions that do support Unicode. If you are porting code that calls the OpenFile function, for example, you can support Unicode by using the CreateFile or CreateFileEx function instead.

If a function has no Unicode equivalent, you can map characters to and from 8-bit character sets before and after the function call. For example, the number-formatting functions atoi and itoa use only the digits 0 through 9. Normally, mapping Unicode to 8-bit characters could cause loss of data, but you can avoid this by making your code type independent and conditionalizing the expressions. For example, the following statements are type dependent and should be changed to support Unicode.

char str[4];

num = atoi(str);

These statements could be rewritten as follows to make them type independent.

TCHAR tstr[4];

CHAR strTmp[SIZE];

#ifdef UNICODE

wcstombs(strTmp, (const wchar_t *) tstr, sizeof(strTmp));

num = atoi(strTmp);

#else

num = atoi(tstr);

#endif

In this example, the wcstombs function is the standard C function that translates Unicode to ASCII. The example relies on the fact that the digits 0 through 9 can always be translated from Unicode to ASCII, even if some of the surrounding text cannot. The atoi function stops at any character that is not a digit. You can use the LCMapString function if you need to process text that includes the native digits provided for some of the scripts in Unicode.

+$#A Unicode and Character Set Functions

This section describes the character set functions. On errors, some of these functions set an extended error value. Use the GetLastError function to retrieve this value.

IsDBCSLeadByteEx
TranslateCharsetInfo
WideCharToMultiByte

+$#A Obsolete Unicode Functions

The AnsiToOem function is obsolete. Use the CharToOem function.

The AnsiToOemBuff function is obsolete. Use the CharToOemBuff function.

The OemToAnsi function is obsolete. Use the OemToChar function.

The OemToAnsiBuff function is obsolete. Use the OemToCharBuff function.

+$#A Unicode and Character Set Structures

This section describes the structures used by the character set functions.

CHARSETINFO
FONTSIGNATURE
LOCALESIGNATURE

+$#A Unicode and Character Set Constants

This section describes the constants used with the character set functions.

+$#A ANSI Code-Page Identifiers

Identifier | Meaning
874 | Thai
932 | Japan
936 | Chinese (PRC, Singapore)
949 | Korean
950 | Chinese (Taiwan, Hong Kong)
1200 | Unicode (BMP of ISO 10646)
1250 | Windows 3.1 Eastern European
1251 | Windows 3.1 Cyrillic
1252 | Windows 3.1 Latin 1 (US, Western Europe)
1253 | Windows 3.1 Greek
1254 | Windows 3.1 Turkish
1255 | Hebrew
1256 | Arabic
1257 | Baltic

+$#A OEM Code-Page Identifiers

Identifier | Meaning
437 | MS-DOS United States
708 | Arabic (ASMO 708)
709 | Arabic (ASMO 449+, BCON V4)
710 | Arabic (Transparent Arabic)
720 | Arabic (Transparent ASMO)
737 | Greek (formerly 437G)
775 | Baltic
850 | MS-DOS Multilingual (Latin I)
852 | MS-DOS Slavic (Latin II)
855 | IBM Cyrillic (primarily Russian)
857 | IBM Turkish
860 | MS-DOS Portuguese
861 | MS-DOS Icelandic
862 | Hebrew
863 | MS-DOS Canadian-French
864 | Arabic
865 | MS-DOS Nordic
866 | MS-DOS Russian (former USSR)
869 | IBM Modern Greek
874 | Thai
932 | Japan
936 | Chinese (PRC, Singapore)
949 | Korean
950 | Chinese (Taiwan, Hong Kong)

1361

Korean (Johab)

+$#A Code-Page Identifiers

Identifier | Meaning
037 | EBCDIC
437 | MS-DOS United States
500 | EBCDIC "500V1"
708 | Arabic (ASMO 708)
709 | Arabic (ASMO 449+, BCON V4)
710 | Arabic (Transparent Arabic)
720 | Arabic (Transparent ASMO)
737 | Greek (formerly 437G)
775 | Baltic
850 | MS-DOS Multilingual (Latin I)
852 | MS-DOS Slavic (Latin II)
855 | IBM Cyrillic (primarily Russian)
857 | IBM Turkish
860 | MS-DOS Portuguese
861 | MS-DOS Icelandic
862 | Hebrew
863 | MS-DOS Canadian-French
864 | Arabic
865 | MS-DOS Nordic
866 | MS-DOS Russian
869 | IBM Modern Greek
874 | Thai
875 | EBCDIC
932 | Japan
936 | Chinese (PRC, Singapore)
949 | Korean
950 | Chinese (Taiwan, Hong Kong)
1026 | EBCDIC
1200 | Unicode (BMP of ISO 10646)
1250 | Windows 3.1 Eastern European
1251 | Windows 3.1 Cyrillic
1252 | Windows 3.1 US (ANSI)
1253 | Windows 3.1 Greek
1254 | Windows 3.1 Turkish
1255 | Hebrew
1256 | Arabic
1257 | Baltic
1361 | Korean (Johab)
10000 | Macintosh Roman
10001 | Macintosh Japanese
10006 | Macintosh Greek I
10007 | Macintosh Cyrillic
10029 | Macintosh Latin 2
10079 | Macintosh Icelandic
10081 | Macintosh Turkish

+$#A Code-Page Bitfields

Bit | Code page | Description
ANSI |  |
0 | 1252 | Latin 1
1 | 1250 | Latin 2: Eastern Europe
2 | 1251 | Cyrillic
3 | 1253 | Greek
4 | 1254 | Turkish
5 | 1255 | Hebrew
6 | 1256 | Arabic
7 | 1257 | Baltic
8 - 16 |  | Reserved for ANSI
ANSI and OEM
17 | 874 | Thai
18 | 932 | JIS/Japan
19 | 936 | Chinese: Simplified chars--PRC and Singapore
20 | 949 | Korean Unified Hangeul Code (Hangeul TongHabHyung Code)
21 | 950 | Chinese: Traditional chars--Taiwan and Hong Kong
22 - 29 |  | Reserved for alternate ANSI and OEM
30 - 31 |  | Reserved by system.
OEM |  |
32 - 47 |  | Reserved for OEM
48 | 869 | IBM Greek
49 | 866 | MS-DOS Russian
50 | 865 | MS-DOS Nordic
51 | 864 | Arabic
52 | 863 | MS-DOS Canadian French
53 | 862 | Hebrew
54 | 861 | MS-DOS Icelandic
55 | 860 | MS-DOS Portuguese
56 | 857 | IBM Turkish
57 | 855 | IBM Cyrillic; primarily Russian
58 | 852 | Latin 2
59 | 775 | Baltic
60 | 737 | Greek; former 437 G
61 | 708 | Arabic; ASMO 708
62 | 850 | WE/Latin 1
63 | 437 | US

Créé avec HelpNDoc Personal Edition: Créer des sites web d'aide facilement


Up-Down Controls.rtf

+$#AK Up-Down Controls

An up-down control is a pair of arrow buttons that the user can click to increment or decrement a value, such as a scroll position or a number displayed in a companion control. The value associated with an up-down control is called its current position. An up-down control is most often used with a companion control, which is called a buddy window.

+$#A About Up-Down Controls

To the user, an up-down control and its buddy window often look like a single control. You can specify that an up-down control automatically position itself next to its buddy window and that it automatically set the caption of the buddy window to its current position. For example, you can use an up-down control with an edit control to prompt the user for numeric input. For more information about edit controls, see Edit Controls The following illustration shows an up-down control with an edit control as its buddy window, a combination that is sometimes referred to as a spinner control.

{ewc msdncd, EWGraphic, group10610 0 /a "SDK_01.bmp"}

An up-down control without a buddy window functions as a sort of simplified scroll bar. For example, a tab control sometimes displays an up-down control to enable the user to scroll additional tabs into view. The following illustration shows an up-down control in the upper right corner of a tab control.

{ewc msdncd, EWGraphic, group10610 1 /a "SDK_02.bmp"}

You can create an up-down control and specify its buddy window in several ways. The UPDOWN_CLASS value specifies an up-down control's window class. You can specify this window class in a dialog box template or in a call to the CreateWindowEx function. For more information about dialog box templates, see Dialog Boxes. Another way is to use the CreateUpDownControl function to create an up-down control and, at the same time, specify its buddy window, current position, and minimum and maximum positions.

The UPDOWN_CLASS window class is registered when the common controls dynamic-link library (DLL) is loaded. If you create an up-down control without using the CreateUpDownControl function, you must ensure that the DLL is loaded. You can do so by using the InitCommonControls function. For more information about common controls, see Common Controls.

CreateUpDownControl enables you to specify a buddy window. If you create an up-down control without using this function, you can assign a buddy window by specifying the UDS_AUTOBUDDY window style or by using the UDM_SETBUDDY message. If UDS_AUTOBUDDY is specified, the up-down control automatically selects the previous window in the Z order as its buddy window. This window might be the previous control in a dialog box template. You can use UDM_SETBUDDY to assign a specific buddy window to an up-down control. To determine an up-down control's current buddy window, use the UDM_GETBUDDY message. An up-down control and its buddy window must have the same parent window.

An up-down control notifies its parent window when its current position changes by sending it a UDN_DELTAPOS notification message and a WM_VSCROLL message.

+$#A About Up-Down Control Styles

Using window styles, you can control characteristics of an up-down control, such as how it positions itself relative to its buddy window, whether it sets the text of its buddy window, and whether it processes the UP ARROW and DOWN ARROW keys.

An up-down control with the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style aligns with the left or right edge of its buddy window. The width of the buddy window is decreased to accommodate the width of the up-down control.

An up-down control with the UDS_SETBUDDYINT style sets the caption of its buddy window (using the WM_SETTEXT message) whenever the current position changes. The control inserts a thousands separator between every three digits of a decimal string unless the UDS_NOTHOUSANDS style is specified. If the buddy window is a list box, an up-down control sets its current selection instead of its caption.

You can specify the UDS_ARROWKEYS style to provide a keyboard interface for an up-down control. If this style is specified, the control processes the UP ARROW and DOWN ARROW keys. The control also subclasses the buddy window so that it can process these keys when the buddy window has the focus.

If you use an up-down control for horizontal scrolling, you can specify the UDS_HORZ style. This style causes the up-down control's arrows to point left and right instead of up and down.

By default, the current position does not change if the user attempts to increment it or decrement it beyond the maximum or minimum value. You can change this behavior by using the UDS_WRAP style, so the position "wraps" to the opposite extreme. For example, incrementing past the upper limit wraps the position back to the lower limit.

+$#A Position and Acceleration

After an up-down control is created, you can change the control's current position, minimum position, and maximum position by sending messages. You can also change the radix base used to display the current position in the buddy window and the rate at which the current position changes when the up or down arrow is clicked.

To retrieve the current position of an up-down control, use the UDM_GETPOS message. For an up-down control with a buddy window, the current position is the number in the buddy window's caption. Because the caption may have changed (for example, the user may have edited the text of an edit control), the up-down control retrieves the current caption and updates its current position accordingly.

The buddy window's caption may be either a decimal or hexadecimal string, depending on the radix base (that is, either base 10 or 16) of the up-down control. You can set the radix base by using the UDM_SETBASE message and retrieve the radix base by using the UDM_GETBASE message.

The UDM_SETPOS message sets the current position of a buddy window. Note that, unlike a scroll bar, an up-down control automatically changes its current position when the up and down arrows are clicked. An application, therefore, does not need to set the current position when processing the WM_VSCROLL message.

You can change the minimum and maximum positions of an up-down control by using the UDM_SETRANGE message. The maximum position may be less than the minimum, and in that case clicking the up arrow decreases the current position. To put it another way, up means moving towards the maximum position. To retrieve the minimum and maximum positions for an up-down control, use the UDM_GETRANGE message.

You can control the rate at which the position changes when the user holds down an arrow button by setting the up-down control's acceleration. The acceleration is defined by an array of UDACCEL structures. Each structure specifies a time interval and the number of units by which to increment or decrement at the end of that interval. To set the acceleration, use the UDM_SETACCEL message. To retrieve acceleration information, use the UDM_GETACCEL message.

+$#A Default Up-Down Controls Message Processing

This section describes the window message processing performed by an up-down control.

Message | Processing performed
WM_CREATE | Allocates and initializes a private data structure and saves its address as window data.
WM_DESTROY | Frees data allocated during WM_CREATE processing.
WM_ENABLE | Invalidates the window.
WM_KEYDOWN | Changes the current position in the case of an UP ARROW or DOWN ARROW key.
WM_KEYUP | Ends changing the position.
WM_LBUTTONDOWN | Captures the mouse. If the buddy window is an edit control or list box, it sets the focus to the buddy window. If the mouse is over the up or down button, it begins changing the position and sets a timer.
WM_LBUTTONUP | Ends changing the position and releases the mouse capture if the up-down control has captured the mouse. If the buddy window is an edit control, it selects all the text in the edit control.
WM_PAINT | Paints the up-down control.
WM_TIMER | Changes the current position if the mouse is being held down over a button and a sufficient interval has elapsed.

+$#A Up-Down Control Reference

The following functions, messages, notification messages, and structures are associated with up-down controls. These elements can be grouped as follows.

Up-Down Control Creation

CreateUpDownControl
UDM_GETBUDDY
UDM_SETBUDDY

Current Position and Acceleration

UDACCEL
UDM_GETACCEL
UDM_GETBASE
UDM_GETPOS
UDM_GETRANGE
UDM_SETACCEL
UDM_SETBASE
UDM_SETPOS
UDM_SETRANGE

Notification Messages

UDN_DELTAPOS
WM_VSCROLL

+$#A Up-Down Control Functions

The following function is used with up-down controls.

CreateUpDownControl

+$#A Up-Down Control Messages

The following messages are used with up-down controls.

UDM_GETACCEL
UDM_GETBASE
UDM_GETBUDDY
UDM_GETPOS
UDM_GETRANGE
UDM_SETACCEL
UDM_SETBASE
UDM_SETBUDDY
UDM_SETPOS
UDM_SETRANGE

+$#A Up-Down Control Notification Messages

The following notification message is used with up-down controls.

UDN_DELTAPOS

+$#A Up-Down Control Structures

The following structure is used with up-down controls.

UDACCEL

+$#A Up-Down Control Styles

The following window styles are used with up-down controls.

UDS_ALIGNLEFT | Positions the up-down control next to the left edge of the buddy window. The buddy window is moved to the right and its width decreased to accommodate the width of the up-down control.
UDS_ALIGNRIGHT | Positions the up-down control next to the right edge of the buddy window. The width of the buddy window is decreased to accommodate the width of the up-down control.
UDS_ARROWKEYS | Causes the up-down control to increment and decrement the position when the UP ARROW and DOWN ARROW keys are pressed.
UDS_AUTOBUDDY | Automatically selects the previous window in the Z order as the up-down control's buddy window.
UDS_HORZ | Causes the up-down control's arrows to point left and right instead of up and down.
UDS_NOTHOUSANDS | Does not insert a thousands separator between every three decimal digits.
UDS_SETBUDDYINT | Causes the up-down control to set the text of the buddy window (using the WM_SETTEXT message) when the position changes. The text consists of the position formatted as a decimal or hexadecimal string.
UDS_WRAP | Causes the position to "wrap" if it is incremented or decremented beyond the ending or beginning of the range.

Créé avec HelpNDoc Personal Edition: Générateur de documentations PDF gratuit


Introduction

Créé avec HelpNDoc Personal Edition: Environnement de création d'aide complet